OUTER / CROSS APPLY Подзапрос без предложения FROM - PullRequest
1 голос
/ 20 февраля 2020

Большинство онлайн-документации или учебных пособий, обсуждающих OUTER|CROSS APPLY, описывают что-то вроде:

SELECT columns
FROM table OUTER|CROSS APPLY (SELECT … FROM …);

Подзапрос, как правило, является полным SELECT … FROM … запросом.

Я должен где-то прочитать, что подзапрос FROM не требуется, и в этом случае столбцы появляются из основного запроса:

SELECT columns
FROM table OUTER|CROSS APPLY (SELECT … );

, потому что я обычно использовал его как метод для предварительного вычисления столбцов.

Вопрос в том, что на самом деле происходит, если FROM опущен в подзапросе? Это коротко для чего-то еще? Я обнаружил, что не означает то же самое, что и из основной таблицы.

У меня есть образец здесь: http://sqlfiddle.com/#! 18 / 0188f7 / 4/1

Ответы [ 3 ]

3 голосов
/ 20 февраля 2020

Сначала рассмотрим

SELECT o.name, o.type
FROM sys.objects o

Теперь рассмотрим

SELECT o.name, (SELECT o.type) AS type
FROM sys.objects o

A SELECT без FROM, как будто выбирается из воображаемой таблицы из одной строки. Вышеупомянутое не меняет результаты, скалярный подзапрос просто действует как коррелированный подзапрос и использует значение из внешнего запроса.

APPLY ведет себя таким же образом. Ссылки на столбцы из внешнего запроса просто передаются как коррелированные параметры. Так что это то же самое, что и

SELECT o.name, ca.type
FROM sys.objects o
CROSS APPLY (SELECT o.type) AS ca

Но APPLY в целом более эффективен, чем скалярный подзапрос в SELECT (в том смысле, что он может расширять строку или удалять строки из результата )

1 голос
/ 20 февраля 2020

То, что вы упомянули, не является SUBQUERY. Это отдельное табличное выражение. Используете ли вы предложение FROM в правильном выражении или нет проблемы.

  1. Если вы используете предложение FROM в правильном табличном выражении, то у вас есть источник данных в правильном табличном выражении.
  2. Если вы не используете выражение FROM в правильном выражении, ваш источник данных исходит из выражения левой таблицы.

Сначала мы увидим, что такое оператор APPLY. Ссылка BOL

Использование APPLY

Левый и правый операнды оператора APPLY являются табличными выражениями. Основное различие между этими операндами заключается в том, что right_table_source может использовать табличную функцию, которая берет столбец из left_table_source в качестве одного из аргументов функции. Left_table_source может включать в себя табличные функции, но это не может содержать аргументы, которые являются столбцами из right_table_source.

Оператор APPLY работает следующим образом для создания источника таблицы для предложения FROM:

  1. Оценивает right_table_source для каждого строка left_table_source для создания наборов строк.

    Значения в right_table_source зависят от left_table_source. right_table_source может быть представлен примерно следующим образом: TVF (left_table_source.row), где TVF является табличной функцией.

  2. Объединяет наборы результатов, которые создаются для каждой строки в оценке right_table_source с параметром left_table_source путем выполнения операции UNION ALL.

    Список столбцов, полученных в результате оператора APPLY, представляет собой набор столбцов из left_table_source, который объединяется со списком столбцов из right_table_source.

В зависимости от того, как вы используете оператор APPLY, он будет вести себя как коррелированный подзапрос или CROSS JOIN

  1. Использование значений слева табличное выражение в правом табличном выражении
--  without FROM (similar to Correlated Subquery)
    SELECT id, data, value
    FROM test OUTER APPLY(SELECT data*10 AS value) AS sq;
Не использовать значения выражения левой таблицы в выражении правой таблицы
--  FROM table (Similar to cross join)
    SELECT id, data, value
    FROM test OUTER APPLY(SELECT data*10 AS value FROM test) AS sq;
0 голосов
/ 20 февраля 2020

Пропуск инструкции FROM не указан c для CROSS / OUTER APPLY; любое допустимое SQL предложение select может его опустить. Если вы не используете FROM, у вас нет источника данных, поэтому вы не можете указать столбцы в этом источнике. Скорее вы можете выбрать только те значения, которые уже существуют; будь то константы, определенные в самом операторе или в некоторых случаях (например, в подзапросах) столбцов, на которые ссылаются другие части запроса.

Это проще понять, если вы знакомы с Oracle 's Dual Таблица; стол с 1 строкой. В MS SQL эта таблица будет выглядеть следующим образом:

-- Ref: https://blog.sqlauthority.com/2010/07/20/sql-server-select-from-dual-dual-equivalent/
CREATE TABLE DUAL
(
    DUMMY VARCHAR(1) NOT NULL 
    , CONSTRAINT CHK_ColumnD_DocExc  CHECK (DUMMY = 'X') -- ensure this column can only hold the value X 
    , CONSTRAINT PK_DUAL PRIMARY KEY (DUMMY) -- ensure we can only have unique values... combined with the above means we can only ever have 1 row
)  
GO
INSERT INTO DUAL (DUMMY)
VALUES ('X')
GO

Затем вы можете сделать select 1 one, 'something else' two from dual. Вы на самом деле не используете двойной; просто убедитесь, что у вас есть таблица, которая всегда будет возвращать ровно 1 строку.

Теперь в SQL, где бы вы ни пропускали оператор FROM, считайте этот оператор таким, как если бы он сказал FROM DUAL / он имеет то же значение , только SQL допускает такой более краткий подход.

Обновление

Вы упоминаете в комментариях, что не видите, как вы можете ссылаться на столбцы из исходного оператора в подзапросе (например, того типа, который вы можете увидеть при использовании APPLY). Приведенный ниже код показывает это без сценария APPLY. По общему признанию, демонстрационный код здесь не тот, который вы когда-либо использовали (поскольку вы могли бы просто набрать where Something like '%o%' в исходном операторе, не нуждаясь в выражении подзапроса / in), но в иллюстративных целях он показывает точно такой же сценарий, что и вы. получил с вашим сценарием APPLY; то есть оператор просто возвращает значение SOMETHING для текущей строки.

declare @someTable table (
    Id bigint not null identity(1,1)
    , Something nvarchar(32) not null
)
insert @someTable (Something) values ('one'), ('two'), ('three')

select * 
from @someTable x
where x.Something in 
(
    -- this subquery references the SOMETHING column from above, but doesn't have a FROM statement
    -- note: there is only 1 value at a time for something here; not all 3 values at once; it's the same single value as Something as we have before the in keyword above
    select Something 
    where Something like '%o%'
)
...