Как использовать несколько операторов WITH в одном запросе PostgreSQL?

96

Я хотел бы «объявить», что фактически является несколькими таблицами TEMP, используя оператор WITH. Запрос, который я пытаюсь выполнить, выглядит следующим образом:

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

Я прочитал документацию PostgreSQL и изучил использование нескольких WITHоператоров, но не смог найти ответа.

Грег
источник
Попробуйте поставить запятую перед вторым withутверждением, после любого другого. Не уверен насчет postgres, но это нормальный синтаксис с Oracle и sql server
msheikh25 01
Я пробовал использовать запятую, а затем точку с запятой, но все еще были синтаксические ошибки: ERROR: syntax error at or near "WITH"для запятой и ERROR: syntax error at or near ";"для точки с запятой.
Грег

Ответы:

155

В соответствии с другими комментариями второму выражению общей таблицы [CTE] предшествует запятая, а не оператор WITH, поэтому

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

Что касается вашего фактического запроса, этот синтаксис должен работать в PostgreSql, Oracle и sql-server, ну, позже, как правило, вы продолжаете WITHиспользовать точку с запятой ( ;WTIH), но это потому, что обычно люди sql-server (включая меня) не заканчиваются предыдущие операторы, которые необходимо завершить до определения CTE ...

Однако обратите внимание, что у вас возникла вторая проблема с синтаксисом в отношении вашего WHEREутверждения. WHERE date IN table_2недействителен, потому что вы никогда не ссылаетесь на значение / столбец из таблицы_2. Я предпочитаю INNER JOINболее INили Existsменее вот синтаксис, который должен работать с 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
;

Если вы хотите сохранить то, что у вас было, что обычно EXISTS было бы лучше, чем IN, но для использования IN вам понадобится фактический оператор SELECT в вашем where.

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

IN очень проблематичен, хотя dateпотенциально может быть NULLтак, если вы не хотите использовать a, JOINя бы предложил EXISTS. Следующим образом:

SELECT * 
FROM
     table_1 t1
WHERE EXISTS (SELECT * FROM table_2 t2 WHERE t2.date = t1.date);
Мэтт
источник
2
Спасибо за подробное объяснение, синтаксис сработал :)
Грег
рад помочь. Я не могу найти статью о неиспользовании IN, но я настоятельно рекомендую использовать JOIN или EXISTS поверх IN. Если в вашем наборе результатов существует значение NULL, происходит следующее: вы получите каждую запись, а не только те, которые вам нужны. Это странно, но именно так работает большинство RDBM. попробуйте поискать по нему, я знаю, что хороший ответ, который я видел об этом, тоже был на этом сайте ... в любом случае, спокойной ночи
Мэтт
5

Вы также можете связать свои результаты с помощью оператора WITH. Например:

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;
Суз'л Шреста
источник