Opérations PostgreSQL que vous ne pouvez pas EXPLIQUER · Felix Geisendörfer

Opérations PostgreSQL que vous ne pouvez pas EXPLIQUER · Felix Geisendörfer


Opérations PostgreSQL que vous ne pouvez pas EXPLIQUER · Felix Geisendörfer

Publié :

Je m’intéresse actuellement aux composants internes de PostgreSQL pendant mon temps libre, notamment en essayant de comprendre le planificateur et l’exécuteur de requêtes. C’est une expérience profondément humiliante, mais je suis parfois ravi de pouvoir répondre à des questions simples sur lesquelles je fais des recherches.

L’une de ces questions est de savoir comment PostgreSQL s’exécute ORDER BY clauses à l’intérieur des expressions agrégées.

# CREATE TABLE foo AS
SELECT * FROM generate_series(1, 3) i;
SELECT 3
# SELECT * FROM foo;
 i
---
 1
 2
 3
(3 rows)

Considérons un tableau simple contenant 3 lignes d’une colonne int allant de 1 à 3 :

Supposons maintenant que nous souhaitions convertir ces lignes en un tableau json. Cela peut être accompli en utilisant la fonction d’agrégation json_agg :

# SELECT json_agg(i) FROM foo;
 json_agg
-----------
 (1, 2, 3)
(1 row)

Mais que se passe-t-il si nous voulons classer les éléments du tableau par ordre décroissant ? Pas de problème, nous pouvons simplement utiliser le support order-by-clause disponible pour toutes les expressions agrégées :

# SELECT json_agg(i ORDER BY i DESC) FROM foo;
 json_agg
-----------
 (3, 2, 1)
(1 row)

Si vous êtes comme moi, vous imaginerez probablement le EXPLAIN sortie pour cette requête devant contenir trois nœuds : un Seq Scanun Sortet un Aggregate. Cependant, lorsque nous courons EXPLAINil s’avère qu’il n’y a pas Sort nœud.

# EXPLAIN SELECT json_agg(i ORDER BY i DESC) FROM foo;
                         QUERY PLAN
-------------------------------------------------------------
 Aggregate  (cost=41.88..41.89 rows=1 width=32)
   ->  Seq Scan on foo  (cost=0.00..35.50 rows=2550 width=4)
(2 rows)

En fait, le plan reste même inchangé même si nous modifions l’ordre de tri ou supprimons entièrement la clause :

# EXPLAIN SELECT json_agg(i ORDER BY i ASC) FROM foo;
                         QUERY PLAN
-------------------------------------------------------------
 Aggregate  (cost=41.88..41.89 rows=1 width=32)
   ->  Seq Scan on foo  (cost=0.00..35.50 rows=2550 width=4)
(2 rows)

# EXPLAIN SELECT json_agg(i) FROM foo;
                         QUERY PLAN
-------------------------------------------------------------
 Aggregate  (cost=41.88..41.89 rows=1 width=32)
   ->  Seq Scan on foo  (cost=0.00..35.50 rows=2550 width=4)
(2 rows)

Mais comment est-ce possible ? S’il n’y a pas Sort nœud, comment l’agrégat effectue-t-il son tri ?

Eh bien, il s’avère que les nœuds du plan agrégé peuvent effectuer leur propre tri. Ces types n’apparaissent pas explicitement dans EXPLAINmais nous pouvons les tracer en utilisant l’option développeur trace_sort :

# SET trace_sort=true;
SET
# SET client_min_messages='log';
SET
# SELECT json_agg(i ORDER BY i DESC) FROM foo;
LOG:  begin datum sort: workMem = 4096, randomAccess = f
LOG:  performsort starting: CPU 0.00s/0.00u sec elapsed 0.00 sec
LOG:  performsort done: CPU 0.00s/0.00u sec elapsed 0.00 sec
LOG:  internal sort ended, 25 KB used: CPU 0.00s/0.00u sec elapsed 0.00 sec
 json_agg
-----------
 (3, 2, 1)
(1 row)

Une autre façon de voir que le tri fait partie du plan est d’utiliser l’option debug_print_plan, mais le résultat n’est pas pour les âmes sensibles. Plus précisément, vous devez rechercher le :aggorder champ ci-dessous :

# SET debug_print_plan=true;
SET
# SELECT json_agg(i ORDER BY i DESC) FROM foo;
LOG:  plan:
DETAIL:     {PLANNEDSTMT
   :commandType 1
   :queryId 0
   :hasReturning false
   :hasModifyingCTE false
   :canSetTag true
   :transientPlan false
   :dependsOnRole false
   :parallelModeNeeded false
   :planTree
      {AGG
      :startup_cost 41.88
      :total_cost 41.89
      :plan_rows 1
      :plan_width 32
      :parallel_aware false
      :plan_node_id 0
      :targetlist (
         {TARGETENTRY
         :expr
            {AGGREF
            :aggfnoid 3175
            :aggtype 114
            :aggcollid 0
            :inputcollid 0
            :aggtranstype 2281
            :aggargtypes (o 23)
            :aggdirectargs <>
            :args (
               {TARGETENTRY
               :expr
                  {VAR
                  :varno 65001
                  :varattno 1
                  :vartype 23
                  :vartypmod -1
                  :varcollid 0
                  :varlevelsup 0
                  :varnoold 1
                  :varoattno 1
                  :location 16
                  }
               :resno 1
               :resname <>
               :ressortgroupref 1
               :resorigtbl 0
               :resorigcol 0
               :resjunk false
               }
            )
            :aggorder (
               {SORTGROUPCLAUSE
               :tleSortGroupRef 1
               :eqop 96
               :sortop 521
               :nulls_first true
               :hashable true
               }
            )
            :aggdistinct <>
            :aggfilter <>
            :aggstar false
            :aggvariadic false
            :aggkind n
            :agglevelsup 0
            :aggsplit 0
            :location 7
            }
         :resno 1
         :resname json_agg
         :ressortgroupref 0
         :resorigtbl 0
         :resorigcol 0
         :resjunk false
         }
      )
      :qual <>
      :lefttree
         {SEQSCAN
         :startup_cost 0.00
         :total_cost 35.50
         :plan_rows 2550
         :plan_width 4
         :parallel_aware false
         :plan_node_id 1
         :targetlist (
            {TARGETENTRY
            :expr
               {VAR
               :varno 1
               :varattno 1
               :vartype 23
               :vartypmod -1
               :varcollid 0
               :varlevelsup 0
               :varnoold 1
               :varoattno 1
               :location -1
               }
            :resno 1
            :resname <>
            :ressortgroupref 0
            :resorigtbl 0
            :resorigcol 0
            :resjunk false
            }
         )
         :qual <>
         :lefttree <>
         :righttree <>
         :initPlan <>
         :extParam (b)
         :allParam (b)
         :scanrelid 1
         }
      :righttree <>
      :initPlan <>
      :extParam (b)
      :allParam (b)
      :aggstrategy 0
      :aggsplit 0
      :numCols 0
      :grpColIdx
      :grpOperators
      :numGroups 1
      :aggParams (b)
      :groupingSets <>
      :chain <>
      }
   :rtable (
      {RTE
      :alias <>
      :eref
         {ALIAS
         :aliasname foo
         :colnames ("i")
         }
      :rtekind 0
      :relid 404407
      :relkind r
      :tablesample <>
      :lateral false
      :inh false
      :inFromCl true
      :requiredPerms 2
      :checkAsUser 0
      :selectedCols (b 9)
      :insertedCols (b)
      :updatedCols (b)
      :securityQuals <>
      }
   )
   :resultRelations <>
   :utilityStmt <>
   :subplans <>
   :rewindPlanIDs (b)
   :rowMarks <>
   :relationOids (o 404407)
   :invalItems <>
   :nParamExec 0
   }

LOG:  begin datum sort: workMem = 4096, randomAccess = f
LOG:  performsort starting: CPU 0.00s/0.00u sec elapsed 0.00 sec
LOG:  performsort done: CPU 0.00s/0.00u sec elapsed 0.00 sec
LOG:  internal sort ended, 25 KB used: CPU 0.00s/0.00u sec elapsed 0.00 sec
 json_agg
-----------
 (3, 2, 1)
(1 row)

En conclusion : Tandis que EXPLAIN est une arme puissante pour ceux qui cherchent à comprendre les performances des requêtes, vous devez être conscient du fait qu’il y a des choses que vous ne pouvez pas EXPLAIN :).

La recherche pour cet article a été effectuée à l’aide de PostgreSQL 9.6.3 et LLDB, mais devrait également s’appliquer aux versions récentes ainsi qu’au prochain PostgreSQL 10.

# SELECT version();
                                                   version
--------------------------------------------------------------------------------------------------------------
 PostgreSQL 9.6.3 on x86_64-apple-darwin14.5.0, compiled by Apple LLVM version 7.0.0 (clang-700.1.76), 64-bit
(1 row)

— Félix Geisendörfer


Abonnez-vous à ce blog via RSS ou E-Mail ou recevez de petites mises à jour de ma part via Gazouillement.





Source link

Postagens Similares

  • Pikiran tentang Pembunuhan Charlie Kirk

    Pertama beberapa poin utama: Saya sangat terganggu oleh semuanya Saya berbeda dengan kirk tentang banyak politiknya Saya pikir itu sangat buruk bagi negara yang dia terbunuh Saya tidak berpikir dia hampir sama buruknya dengan yang dijelaskan banyak orang Meskipun saya tidak menyukai politiknya, saya kebanyakan menyukai bagaimana dia bertunangan Saya pikir dia berusaha berbuat baik,…

  • Come consentire i popup nelle visualizzazioni Web create dinamicamente in Electron.js

    Il mio progetto della barra dei menu smol utilizza lo speciale tag webview di Electron per generare dinamicamente un elenco di finestre del browser secondario per la chat. Negli ultimi due mesi ho avuto un problema con i popup SSO, ovvero che semplicemente non funzionano affatto, presumibilmente perché Electron blocca i popup per impostazione predefinita….

  • simonw/pedalicano

    simonw/pedalicano. Chiaramente non stavo prestando attenzione quando lo erano annunciato per primo a maggio, ma oggi ho accidentalmente attivato un “animale domestico” in Codex Desktop – un piccolo robot animato, che ricorda Clippy – e poi ho scoperto che puoi crearne uno tuo. Così ho fatto e ora ho un simpatico pellicano su una bicicletta…

  • 📝 26-06-2026 09:57 – ਕੇਵ ਕੁਇਰਕ

    📝 26-06-2026 09:57 – ਕੇਵ ਕੁਇਰਕ 2013 ਤੋਂ ਮਾਣ ਨਾਲ ਵੈੱਬ ਨੂੰ ਬਰਬਾਦ ਕਰ ਰਿਹਾ ਹੈ। 26 ਜੂਨ 2026 @ 09:57 ਕੀ ਤੁਸੀਂ ਜਾਣਦੇ ਹੋ ਕਿ ਕੀ ਮਜ਼ੇਦਾਰ ਨਹੀਂ ਹੈ? ਇੱਕ 200 ਸਾਲ ਪੁਰਾਣੇ ਪੱਥਰ ਦੇ ਘਰ ਵਿੱਚ ਰਹਿਣਾ, ਬਿਨਾਂ ਕਿਸੇ ਇਨਸੂਲੇਸ਼ਨ ਦੇ, ਗਰਮੀ ਦੀ ਲਹਿਰ ਦੌਰਾਨ। ਹਰ ਜਗ੍ਹਾ ਢਹਿ-ਢੇਰੀ ਹੋਏ…

  • REVUE : Un adieu aux armes (Ernest Hemingway)

    Celui d’Ernest Hemingway Un adieu aux armes suit Frederic Henry, ambulancier américain dans l’armée italienne pendant la Première Guerre mondiale, et son histoire d’amour avec l’infirmière anglaise Catherine Barkley. À travers cinq livres, Frédéric est blessé, envoyé à Milan pour se rétablir, approfondit sa relation avec Catherine qui à son tour tombe enceinte, retourne sur…

  • ఇష్టమైన ఐదు: M/M రొమాంటసీ

    వెనెస్సా విడా కెల్లీచే వెనెస్సా విడా కెల్లీ ది ప్రిన్స్ హార్ట్ బై వెనెస్సా విడా కెల్లీ ది ప్రిన్స్ హార్ట్ బై బెన్ ఆల్డర్సన్ రచించిన అవర్ రోగ్ ఫేట్స్ అవర్ రోగ్ ఫేట్స్ బై సారా గ్లెన్ మార్ష్ ఎ స్పెల్ ఫర్ హార్ట్‌సిక్‌నెస్ రచించిన వెనెస్సా విడా కెల్లీ వెన్ ద టైడ్స్ హోల్డ్ ది మూన్ Source link

Deixe um comentário

O seu endereço de email não será publicado. Campos obrigatórios marcados com *