165 votes

Comment utiliser plusieurs instructions WITH dans une requête PostgreSQL ?

Je voudrais "déclarer" ce qui est en fait plusieurs tables TEMP à l'aide de l'instruction WITH. La requête que j'essaie d'exécuter est la suivante :

WITH table_1 AS (
SELECT GENERATE_SERIES('2012-06-29', '2012-07-03', '1 day'::INTERVAL) AS date
)

WITH table_2 AS (
SELECT GENERATE_SERIES('2012-06-30', '2012-07-13', '1 day'::INTERVAL) AS date
)

SELECT * FROM table_1
WHERE date IN table_2

J'ai lu Documentation PostgreSQL et des recherches sur l'utilisation de plusieurs WITH et n'a pas trouvé de réponse.

1 votes

Essayez de mettre une virgule avant la deuxième with déclaration toute autre après. Je ne suis pas sûr pour postgres mais c'est la syntaxe normale avec Oracle et sql server.

2 votes

J'ai essayé d'utiliser une virgule, puis un point-virgule, mais il y avait toujours des erreurs de syntaxe : ERROR: syntax error at or near "WITH" pour la virgule et ERROR: syntax error at or near ";" pour le point-virgule.

270voto

Matt Points 10091

Conformément aux autres commentaires, la deuxième expression de tableau commune [CTE] est précédée d'une virgule et non d'une déclaration AVEC.

WITH cte1 AS (SELECT...)
, cte2 AS (SELECT...)
SELECT *
FROM
    cte1 c1
    INNER JOIN cte2 c2
    ON ........

En ce qui concerne votre requête, cette syntaxe devrait fonctionner avec PostgreSql, Oracle et sql-server. WITH avec un point-virgule ( ;WTIH ), mais c'est parce que les utilisateurs de serveurs SQL (moi y compris) ne terminent pas les instructions précédentes qui doivent être terminées avant qu'un CTE ne soit défini...

Notez cependant que vous aviez un deuxième problème de syntaxe en ce qui concerne votre WHERE déclaration. WHERE date IN table_2 n'est pas valide car vous ne faites jamais référence à une valeur/colonne de la table_2. Je préfère INNER JOIN plus IN ou Exists Voici donc une syntaxe qui devrait fonctionner avec un JOIN :

WITH table_1 AS (
SELECT GENERATE_SERIES('2012-06-29', '2012-07-03', '1 day'::INTERVAL) AS date
)

, table_2 AS (
SELECT GENERATE_SERIES('2012-06-30', '2012-07-13', '1 day'::INTERVAL) AS date
)

SELECT * 
FROM
     table_1 t1
     INNER JOIN 
     table_2 t2
     ON t1.date = t2.date
;

Si vous voulez garder la même façon de faire, c'est-à-dire EXISTS serait mieux que IN, mais pour utiliser IN, vous avez besoin d'une véritable instruction SELECT dans votre where.

SELECT * 
FROM
     table_1 t1
WHERE t1.date IN (SELECT date FROM table_2);

L'IN est très problématique lorsque date pourrait potentiellement être NULL donc si vous ne voulez pas utiliser un JOIN Dans ce cas, je suggérerais EXISTS . Comme suit :

SELECT * 
FROM
     table_1 t1
WHERE EXISTS (SELECT * FROM table_2 t2 WHERE t2.date = t1.date);

0 votes

Heureux d'apporter son aide. Je n'ai pas trouvé l'article sur la non-utilisation de IN, mais je suggère fortement d'utiliser un JOIN ou EXISTS plutôt que IN. Si une valeur nulle existe dans votre jeu de résultats, vous obtiendrez tous les enregistrements et pas seulement ceux que vous voulez. C'est bizarre mais c'est ainsi que fonctionnent la plupart des RDBM. Essayez de faire une recherche sur le sujet, je sais que la bonne réponse que j'ai vue à ce sujet se trouvait sur ce site également... Quoi qu'il en soit, passez une bonne nuit.

23voto

Suz'l Shrestha Points 297

Vous pouvez également enchaîner vos résultats à l'aide de l'instruction WITH. Par exemple :

WITH tab1 as (Your SQL statement),
tab2 as ( SELECT ... FROM tab1 WHERE your filter),
tab3 as ( SELECT ... FROM tab2 WHERE your filter)
SELECT * FROM tab3;

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X