web-dev-qa-db-fra.com

Obtenez un vrai plan de requête pour expliquer Supprimer

J'ai une requête de suppression qui court une longue période. J'essaie de l'expliquer:

EXPLAIN DELETE FROM matches
WHERE id = 1

Et je reçois:

QUERY PLAN
Delete on matches  (cost=0.43..2.65 rows=1 width=6)
  ->  Index Scan using matches_pkey on matches  (cost=0.43..2.65 rows=1 width=6)
        Index Cond: (id = 1)

Mais cela ne pourrait pas être le coût complet, car la présente table est référencée dans de nombreuses contraintes clés étrangères qui doivent être vérifiées, et je sais pour un fait que cette requête prend 40 secondes à compléter.

Comment puis-je voir le coût total d'une telle requête de suppression et de tous les contrôles de clé étrangère qui doivent se produire avant de compléter?


J'ai essayé EXPLAIN (ANALYZE, BUFFERS) mais cela ne me montre pas ces informations lorsque la requête est abandonnée en raison de la contrainte de clé étrangère dans une autre table. Il montre simplement le message d'erreur ("violent la contrainte de clé étrangère"), mais n'explique pas combien de temps il a dépensé pour atteindre ce message d'erreur.

Il est correct car il échoue (et, pour ma logique, rétablir toute la transaction), mais je ne veux pas que ce manque de prendre 2 ms au lieu de 40 secondes. J'ai déjà résolu mon problème, mais je suis toujours curieux s'il est possible d'obtenir des coûts d'une requête échouée.

1
Max Yankov

Vous devez exécuter EXPLAIN (ANALYZE, BUFFERS).

Méfiez-vous que cela exécute réellement le DELETE, alors exécutez-le dans une transaction et ROLLBACK ensuite.

Cela vous montrera des informations sur les contraintes de clé étrangère à moins qu'elles ne soient différées, auquel cas elles sont exécutées au moment de la sellette.

Je suis toujours curieux s'il est possible d'obtenir des coûts d'une requête échouée.

Dans ce cas, les coûts se situeront quelque part entre les coûts de l'analyse de l'indice (après quoi la gâchette pour la clé étrangère commence) et les coûts totaux. Cela dépend si la ligne conflictuelle se retrouve tôt ou tard.

2
Laurenz Albe

Il est correct car il échoue (et, pour ma logique, rétablir toute la transaction), mais je ne veux pas que ce manque de prendre 2 ms au lieu de 40 secondes.

Exécuter la requête DELETE (avec EXPLAIN ANALYZE Wrapper ou non) est considérablement plus coûteux que de vérifier avec un SELECT si une référence FK empêchera l'opération.

Identifier les contraintes FK pointant vers votre table:

SELECT c.conrelid::regclass::text AS referencing_table
     , pg_get_constraintdef(c.oid) AS fk
FROM   pg_constraint c
WHERE  c.confrelid = 'public.bigtype'::regclass
AND    c.contype  = 'f'
ORDER  BY 1, 2;

Voir:

Vous obtenez 0-N lignes de la forme:

referencing_tbl | FOREIGN KEY (referencing_col, ...) REFERENCES matches(referenced_col, ...) ...

Basé sur cela, la requête la plus rapide possible serait:

SELECT EXISTS (
   SELECT FROM matches m
   WHERE  WHERE id = 1
   AND   (EXISTS (SELECT FROM referencing_tbl t WHERE (t.referencing_col, ...) = (m.referenced_col, ...))
    -- OR EXISTS ... -- one predicate per FK
         )
   );

true ... au moins une ligne est référencée. DELETE va échouer. false ... pas de références. DELETE va réussir.

Bien sûr, s'il y a un accès en écriture simultanée, il y a une condition de course possible. Probablement sans importance pour votre cas.

Si votre conception relationnelle n'est pas stable, vous pouvez générer cette requête de sélection complètement dynamique ...

1