Méfiez-vous des opérations, des buffers apportant...
Prenons un exemple d'une petite requĂȘte pour examiner certaines approches universelles pour optimiser les requĂȘtes sur PostgreSQL. Les utiliser ou non - c'est Ă vous de dĂ©cider, mais il est bon d'en avoir connaissance.
Dans certaines versions ultérieures de PG, la situation pourrait évoluer avec l'intelligence croissante du planificateur, mais pour 9.4/9.6, cela semble à peu prÚs identique, comme le montrent les exemples ici.
Prenons une requĂȘte tout Ă fait rĂ©elle :
SELECT
TRUE
FROM
"Document" d
INNER JOIN
"DocumentExtension" doc_ex
USING("@Document")
INNER JOIN
"DocumentType" t_doc ON
t_doc."@DocumentType" = d."DocumentType"
WHERE
(d."Person3" = 19091 or d."Employee" = 19091) AND
d."$Draft" IS NULL AND
d."Deleted" IS NOT TRUE AND
doc_ex."State"[1] IS TRUE AND
t_doc."DocumentType" = 'WorkPlan'
LIMIT 1; sur les noms de tables et de champsOn peut avoir des opinions diffĂ©rentes sur les noms de champs et de tables « russes », mais c'est une question de goĂ»t. Ătant donnĂ© que n'avons pas de dĂ©veloppeurs Ă©trangers, et PostgreSQL nous permet de donner des noms mĂȘme en idĂ©ogrammes, Ă condition qu'ils soient entourĂ©s de guillemets, nous prĂ©fĂ©rons nommer les objets de maniĂšre claire et explicite, pour Ă©viter les ambiguĂŻtĂ©s.
Jetons un Ćil au plan obtenu :

144ms et presque 53K buffers â c'est-Ă -dire plus de 400 Mo de donnĂ©es ! Et nous aurons de la chance si tout cela se trouve dans le cache au moment de notre requĂȘte, sinon elle prendra des fois plus de temps Ă ĂȘtre lue depuis le disque.
L'algorithme est le plus important !
Pour optimiser une requĂȘte, il faut d'abord comprendre ce qu'elle est censĂ©e faire.
Laissons pour l'instant de cĂŽtĂ© le dĂ©veloppement de la structure mĂȘme de la base de donnĂ©es, et convenons que nous pouvons relativement « facilement » réécrire la requĂȘte et/ou ajouter Ă la base certains indices dont nous avons besoin Donc, la requĂȘte :.
â vĂ©rifie l'existence d'un document
â dans l'Ă©tat souhaitĂ© et de type spĂ©cifique
â oĂč l'auteur ou l'exĂ©cutant est l'employĂ© dont nous avons besoin
JOIN + LIMIT 1
Il est assez frĂ©quent qu'un dĂ©veloppeur prĂ©fĂšre Ă©crire une requĂȘte dans laquelle il fait d'abord des jointures entre de nombreuses tables, puis il ne reste qu'un seul enregistrement de ce grand ensemble. Mais ce qui est plus facile pour le dĂ©veloppeur n'est pas toujours plus efficace pour la base de donnĂ©es.
Dans notre cas, il n'y avait que 3 tables - et quel effetâŠ
Commençons par éliminer la jointure avec la table « DocumentType », et au passage indiquons à la base de données que
l'enregistrement de type est unique (nous le savons, mais le planificateur ne s'en doute pas encore) : (nous le savons, mais le planificateur ne le soupçonne pas encore) :
AVEC T COMME (
SĂLECTIONNER
"@TypeDeDocument"
DE
"TypeDeDocument"
OĂ
"TypeDeDocument" = 'PlanDeTravail'
LIMIT 1
)
...
OĂ
d."TypeDeDocument" = (TABLE T)
...Oui, si la table/CTE consiste en un seul champ d'un seul enregistrement, alors dans PG, on peut mĂȘme Ă©crire ainsi, au lieu de
d."TypeDeDocument" = (SĂLECTIONNER "@TypeDeDocument" DE T LIMIT 1)Calculs « paresseux » dans les requĂȘtes PostgreSQL
BitmapOr vs UNION
Dans certains cas, le Bitmap Heap Scan peut nous coĂ»ter trĂšs cher â par exemple ici, oĂč beaucoup d'enregistrements rĂ©pondent Ă la condition requise. Nous avons obtenu cela Ă cause de la condition OR, devenue BitmapOr- opĂ©ration dans le plan.
Revenons Ă la tĂąche initiale â il faut trouver un enregistrement correspondant Ă n'importe lequel des conditions â c'est-Ă -dire qu'il n'est pas nĂ©cessaire de chercher tous les 59K enregistrements selon les deux conditions. Il y a un moyen de traiter une condition, puis de passer Ă la seconde seulement si rien n'a Ă©tĂ© trouvĂ© dans la premiĂšre.Nous aidera une telle construction :
(
SĂLECTIONNER
...
LIMIT 1
)
UNION ALL
(
SĂLECTIONNER
...
LIMIT 1
)
LIMIT 1« LIMIT 1 extĂ©rieur » garantit que la recherche s'arrĂȘtera dĂšs que le premier enregistrement sera trouvĂ©. Et s'il est trouvĂ© dans le premier bloc, le second ne sera pas exĂ©cutĂ© (never executed dans le plan).
« Cacher sous CASE » des conditions complexes
Dans la requĂȘte initiale, il y a un point trĂšs gĂȘnant â la vĂ©rification de l'Ă©tat dans la table liĂ©e « DocumentExtension ». IndĂ©pendamment de la vĂ©racitĂ© des autres conditions dans l'expression (par exemple, d.«Supprimé» IS NOT TRUE), cette jointure est toujours effectuĂ©e et « consomme des ressources ». Plus ou moins, cela dĂ©pend du volume de cette table.
Mais on peut modifier la requĂȘte de sorte que la recherche de l'enregistrement liĂ© se fasse uniquement lorsque c'est rĂ©ellement nĂ©cessaire :
SĂLECTIONNER
...
DE
"Document" d
OĂ
...
/*index cond*/ ET
CASE
QUAND "$Brouillon" EST NULL ET "Supprimé" IS NOT TRUE ALORS (
SĂLECTIONNER
"Ătat"[1] IS TRUE
DE
"DocumentExtension"
OĂ
"@Document" = d."@Document"
)
FIN Puisque nous n'avons besoin d'aucun des champs de la table liĂ©e pour le rĂ©sultat, nous avons la possibilitĂ© de transformer la JOIN en condition par sous-requĂȘte.
Nous laisserons les champs indexables « en dehors » du CASE, nous mettons les conditions simples dans le bloc WHEN â et maintenant, la requĂȘte « lourde » n'est exĂ©cutĂ©e que lorsqu'on entre dans le THEN.
Mon nom de famille est « Total »
Nous rassemblons la requĂȘte rĂ©sultante avec tous les mĂ©canismes dĂ©crits ci-dessus :
AVEC T COMME (
SĂLECTIONNEZ
"@TypeDocument"
DE
"TypeDocument"
OĂ
"TypeDocument" = 'PlanTravail'
)
(
SĂLECTIONNEZ
VRAI
Ă PARTIR DE
"Document" d
OĂ
("Personne3", "TypeDocument") = (19091, (TABLE T)) ET
CAS
QUAND "$Brouillon" EST NULL ET "Supprimé" N'EST PAS VRAI ALORS (
SĂLECTIONNEZ
"Ătat"[1] EST VRAI
DE
"DocumentExtension"
OĂ
"@Document" = d."@Document"
)
FIN
LIMIT 1
)
UNION ALL
(
SĂLECTIONNEZ
VRAI
Ă PARTIR DE
"Document" d
OĂ
("TypeDocument", "Employé") = ((TABLE T), 19091) ET
CAS
QUAND "$Brouillon" EST NULL ET "Supprimé" N'EST PAS VRAI ALORS (
SĂLECTIONNEZ
"Ătat"[1] EST VRAI
DE
"DocumentExtension"
OĂ
"@Document" = d."@Document"
)
FIN
LIMIT 1
)
LIMIT 1;Adapté [à ] des index
Un Ćil averti a remarquĂ© que les conditions indexĂ©es dans les sous-blocs UNION varient lĂ©gĂšrement â c'est parce que nous avons dĂ©jĂ des index appropriĂ©s sur la table. Et s'il n'y en avait pas â il vaudrait mieux les crĂ©er : Document(Personne3, TypeDocument) et Document(TypeDocument, EmployĂ©).
sur l'ordre des champs dans les conditions ROWDu point de vue du planificateur, bien sĂ»r, il est Ă©galement possible d'Ă©crire (A, B) = (constA, constB)et (B, A) = (constB, constA). Mais lors de l'Ă©criture dans l'ordre des champs dans l'index, cette requĂȘte est tout simplement plus facile Ă dĂ©boguer.
Qu'est-ce au programme ?

Malheureusement, nous n'avons pas eu de chance, et rien n'a Ă©tĂ© trouvĂ© dans le premier bloc UNION, donc le second a finalement Ă©tĂ© exĂ©cutĂ©. Mais mĂȘme dans ce cas â seulement 0,037 ms et 11 buffers!
Nous avons accĂ©lĂ©rĂ© la requĂȘte et rĂ©duit le "traitement" des donnĂ©es en mĂ©moire de plusieurs milliers de fois, en utilisant des mĂ©thodes assez simples â un bon rĂ©sultat avec peu de copier-coller. đ
Source : habr.com
