#sql #sql-server
Вопрос:
Этот запрос фильтрует некоторые идентификаторы из основной таблицы и возвращает все данные об этих идентификаторах из двух других таблиц в виде двух наборов записей:
WITH filteredIds (fid) AS
(
SELECT id
FROM MasterTable
WHERE <somecondition>
)
SELECT *
FROM Table1
RIGHT JOIN filteredIds ON (Table1.id = filteredIds.fid);
WITH filteredIds (fid) AS
(
SELECT id
FROM MasterTable
WHERE <somecondition>
)
SELECT *
FROM Table2
RIGHT JOIN filteredIds ON (Table2.id = filteredIds.fid);
Это работает до сих пор, но было бы здорово иметь только одно предложение WITH, так как условие всегда одно и то же. Более того, условие часто записывается вручную, так как оно используется для сбора некоторых диагностических данных.
Но это
WITH filteredIds (fid) AS
(
SELECT id
FROM MasterTable
WHERE <somecondition>
)
SELECT *
FROM Table1
RIGHT JOIN filteredIds ON (Table1.id = filteredIds.fid);
SELECT *
FROM Table2
RIGHT JOIN filteredIds ON (Table2.id = filteredIds.fid);
не работает, SQL Server утверждает, что он не знает объект filteredIds
в последнем запросе.
Я что-то наблюдал, или это так не работает? Я предполагаю, что альтернативой будет временная таблица.
Комментарии:
1. CTE-это всего лишь часть другого отдельного оператора sql. Его определение не существует вне оператора, в котором оно определено и заполнено. В этом отношении это точно то же самое, что и производная таблица.
2. Если столбцы достаточно похожи, возможно, а
union all
? Или как насчет встроенной функции с табличным значением, в основном параметризованного представления? Кстати,right join
странно, нет ничего, с чем вы могли бы сделать то, сright
чем вы не могли бы сделатьleft
Ответ №1:
Как только CTE был использован в запросе, он не может быть повторно использован в другом запросе. Насколько я знаю, прямого обходного пути нет. Возможно, самое близкое, чего вы хотите, — это создать настоящую временную таблицу:
CREATE TABLE #temp (fid int);
INSERT INTO #temp (fid)
SELECT id FROM MasterTable WHERE <somecondition>;
Затем вы можете повторно использовать временную таблицу напрямую:
SELECT * FROM Table1 a RIGHT JOIN #temp b ON a.id = b.fid;
SELECT * FROM Table2 a RIGHT JOIN #temp b ON a.id = b.fid;
Комментарии:
1. Вы немного преувеличиваете — у вас может быть несколько определений запросов CTE, но только одно предложение WITH.
2. @SMor Как только CTE был использован в запросе, он не может быть повторно использован в другом запросе. Позвольте мне сменить язык.
3. @TimBiegeleisen, мне
VIEW
кажется, что a ближе к CTE, чем временная таблица. По крайней мере, в SQL Server, потому что и представления, и CTE не материализуются и не встроены в основной запрос и оптимизированы как один большой запрос.4. @Влад, я согласен… Я не думал о представлениях, когда писал свой ответ (который, кстати, все еще действителен).
Ответ №2:
Вы не можете делать с CTE то, что хотите. CTE не существует вне запроса, в котором он был определен.
Я думаю, что самое близкое к тому, что вы хотите, называется «вид».
CREATE VIEW filteredIds
AS
SELECT id AS fid
FROM MasterTable
WHERE <somecondition>
;
И теперь вы можете использовать это представление во многих других запросах.
SELECT *
FROM Table1
RIGHT JOIN filteredIds ON (Table1.id = filteredIds.fid);
SELECT *
FROM Table2
RIGHT JOIN filteredIds ON (Table2.id = filteredIds.fid);
Кроме того, в SQL Server он будет работать точно так же, как CTE, в том смысле, что список отфильтрованных идентификаторов не материализуется, и механизм оптимизирует весь запрос, ссылающийся на представление. Он встроит определение представления во внешний / основной запрос, как если бы вы написали все в одном большом запросе. Точно так же, как это было бы с CTE.
С помощью временной таблицы вы явно записываете этот временный результат на диск и считываете его обратно.
В некоторых случаях это может быть лучше, в некоторых-нет.