Dans le cadre de leur travail, les dĂ©veloppeurs sont souvent confrontĂ©s Ă des situations oĂč ils Ă©crivent une requĂȘte en pensant «la base de donnĂ©es est intelligente, elle s'en sortira seule! »«
Dans certains cas (partiellement à cause d'une méconnaissance des capacités de la base de données, partiellement à cause d'optimisations prématurées), cette approche mÚne à la création de « monstres ».
Tout d'abord, je vais donner un exemple de cette requĂȘte :
-- pour chaque paire clé, nous trouvons les valeurs associées des champs
WITH RECURSIVE cte_bind AS (
SELECT DISTINCT ON (key_a, key_b)
key_a a
, key_b b
, fld1 bind_fld1
, fld2 bind_fld2
FROM
tbl
)
-- nous trouvons les valeurs min/max pour chaque premiÚre clé
, cte_max AS (
SELECT
a
, max(bind_fld1) bind_fld1
, min(bind_fld2) bind_fld2
FROM
cte_bind
GROUP BY
a
)
-- nous relions par la premiÚre clé les paires clés et les valeurs min/max
, cte_a_bind AS (
SELECT
cte_bind.a
, cte_bind.b
, cte_max.bind_fld1
, cte_max.bind_fld2
FROM
cte_bind
INNER JOIN
cte_max
ON cte_max.a = cte_bind.a
)
SELECT * FROM cte_a_bind;Pour Ă©valuer le quality de la requĂȘte, crĂ©ons un ensemble de donnĂ©es arbitraire :
CREATE TABLE tbl AS
SELECT
(random() * 1000)::integer key_a
, (random() * 1000)::integer key_b
, (random() * 10000)::integer fld1
, (random() * 10000)::integer fld2
FROM
generate_series(1, 10000);
CREATE INDEX ON tbl(key_a, key_b);
Il s'avĂšre que la lecture des donnĂ©es a pris moins d'un quart de tout le temps d'exĂ©cution de la requĂȘte : DĂ©mĂȘlons les dĂ©tails

Examinons de prĂšs la requĂȘte, et posons-nous la question :
Pourquoi utiliser WITH RECURSIVE, s'il n'y a pas de CTE récursifs ?
- Pourquoi regrouper les valeurs min/max dans un CTE séparé, alors qu'elles se lient ensuite à l'échantillon d'origine ?
- +25% de temps
Pourquoi utiliser Ă nouveau la lecture de la CTE prĂ©cĂ©dente via un âSELECT * FROMâ inconditionnel Ă la fin ? - +14% de temps
Dans ce cas, nous avons eu de la chance que l'algorithme de hachage ait été choisi pour la jointure et non la boucle imbriquée, sinon nous aurions eu non pas un seul passage de CTE Scan, mais 10K !
un peu sur CTE Scan
Il faut se rappeler quele CTE Scan est l'Ă©quivalent du Seq Scan â donc aucune indexation, seulement un balayage complet qui nĂ©cessiterait 10K x 0.3ms = 3000ms pour les itĂ©rations sur cte_max 1K x 1.5ms = ou 1500ms pour les itĂ©rations sur cte_bind En fait, que voulions-nous obtenir en rĂ©sultat ?!
Ah, gĂ©nĂ©ralement, une telle question surgit vers la cinquiĂšme minute d'analyse des requĂȘtes « Ă trois niveaux ». Nous voulions afficher pour chaque paire de clĂ©s unique
les valeurs min/max du groupe par key_a Alors, utilisons des.
fonctions de fenĂȘtre :
SĂLECTIONNER DISTINCT SUR(key_a, key_b)
key_a a
, key_b b
, max(fld1) OVER(w) bind_fld1
, min(fld2) OVER(w) bind_fld2
DE
tbl
FENĂTRE
w COMME (PARTITIONNER PAR key_a); 
Comme la lecture des donnĂ©es dans les deux cas prend environ 4 Ă 5 ms, notre gain de temps total -32% â est pur et simple la charge retirĂ©e du CPU de la base, si une telle requĂȘte est exĂ©cutĂ©e suffisamment souvent.
En général, il ne faut pas forcer la base à « porter du rond, rouler du carré ».
Source : habr.com
