Подзапросы ms sql: MS SQL Server и T-SQL
Содержание
MS SQL Server и T-SQL
Последнее обновление: 20.07.2017
Подзапросы в SELECT
В выражении SELECT мы можем вводить подзапросы четырьмя способами:
Использовать в условии в выражении WHERE
Использовать в условии в выражении HAVING
Использовать в качестве таблицы для выборки в выражении FROM
Использовать в качестве спецификации столбца в выражении SELECT
Рассмотрим некоторые из этих случаев. Например, получим все товары, у которых цена выше средней:
SELECT * FROM Products WHERE Price > (SELECT AVG(Price) FROM Products)
Чтобы получить нужные товары, нам вначале надо выполнить подзапрос на получение средней цены товара: SELECT AVG(Price) FROM Products
.
Или выберем всех покупателей из таблицы Customers, у которых нет заказов в таблице Orders:
SELECT * FROM CUSTOMERS WHERE Id NOT IN (SELECT CustomerId FROM Orders)
Хотя в данном случае подзапросы прекрасно справляются со своей задачей, стоит отметить, что это не самый эффективный способ для извлечения данных из
других таблиц, так как в рамках T-SQL для сведения данных из разных таблиц можно использовать оператор JOIN, который рассматривается в следующей теме.
Получение набора значений
При использовании в операторах сравнения подзапросы должны возвращать одно скалярное значение. Но иногда возникает необходимость получить
набор значений. Чтобы при использовании в операторах сравнения подзапрос мог возвращать набор значений, перед ним необходимо использовать один из операторов: ALL, SOME или
ANY.
При использовании ключевого слова ALL условие в операции сравнения должно быть верно для всех значений,
которые возвращаются подзапросом. Например, найдем все товары, цена которых меньше чем у любого товара фирмы Apple:
SELECT * FROM Products WHERE Price < ALL(SELECT Price FROM Products WHERE Manufacturer='Apple')
Если бы мы в данном случае опустили бы ключевое слово ALL, то мы бы столкнулись с ошибкой.
Допустим, если подзапрос возвращает значения vl1, val2 и val3, то условие фильтрации фактически было бы аналогично
объединению этих значений через оператор AND:
WHERE Price < val1 AND Price < val2 AND Price < val3
В тоже время подобный запрос гораздо проще переписать другим образом:
SELECT * FROM Products WHERE Price < (SELECT MIN(Price) FROM Products WHERE Manufacturer='Apple')
При применении ключевых слов ANY и SOME условие в операции сравнения должно быть истинным для хотя бы одного из значений, возвращаемых
подзапросом. По действию оба этих оператора аналогичны, поэтому можно применять любое из них.
Например, в следующем случае получим товары, которые стоят меньше самого дорого товара компании Apple:
SELECT * FROM Products WHERE Price < ANY(SELECT Price FROM Products WHERE Manufacturer='Apple')
И также стоит отметить, что данный запрос можно сделать проще, переписав следующим образом:
SELECT * FROM Products WHERE Price < (SELECT MAX(Price) FROM Products WHERE Manufacturer='Apple')
Подзапрос как спецификация столбца
Результат подзапроса может представлять отдельный столбец в выборке. Например, выберем все заказы и добавим к ним информацию о названии товара:
SELECT *, (SELECT ProductName FROM Products WHERE Id=Orders.ProductId) AS Product FROM Orders
Подзапросы в команде INSERT
В команде INSERT подзапросы могут применяться для определения значения, которое вставляется в один из столбцов:
INSERT INTO Orders (ProductId, CustomerId, CreatedAt, ProductCount, Price) VALUES ( (SELECT Id FROM Products WHERE ProductName='Galaxy S8'), (SELECT Id FROM Customers WHERE FirstName='Tom'), '2017-07-11', 2, (SELECT Price FROM Products WHERE ProductName='Galaxy S8') )
Подзапросы в команде UPDATE
В команде UPDATE подзапросы могут применяться:
В качестве устанавливаемого значения после оператора SET
Как часть условия в выражении WHERE
Так, увеличим количество купленных товаров на 2 в тех заказах, где покупатель Тоm:
UPDATE Orders SET ProductCount = ProductCount + 2 WHERE CustomerId=(SELECT Id FROM Customers WHERE FirstName='Tom')
Или установим для заказа цену товара, полученную в результате подзапроса:
UPDATE Orders SET Price = (SELECT Price FROM Products WHERE Id=Orders. ProductId) + 2000 WHERE Id=1
Подзапросы в команде DELETE
В команде DELETE подзапросы также применяются как часть условия. Так, удалим все заказы на Galaxy S8, которые сделал Bob:
DELETE FROM Orders WHERE ProductId=(SELECT Id FROM Products WHERE ProductName='Galaxy S8') AND CustomerId=(SELECT Id FROM Customers WHERE FirstName='Bob')
НазадСодержаниеВперед
T-SQL — подзапросы — CoderLessons.com
Подзапрос или Внутренний запрос или Вложенный запрос – это запрос в другом запросе SQL Server, встроенный в предложение WHERE. Подзапрос используется для возврата данных, которые будут использоваться в основном запросе в качестве условия для дальнейшего ограничения данных, подлежащих извлечению.
Подзапросы могут использоваться с операторами SELECT, INSERT, UPDATE и DELETE вместе с такими операторами, как =, <,>,> =, <=, IN, BETWEEN и т. Д.
Есть несколько правил, которым должны следовать подзапросы:
Вы должны заключить подзапрос в скобки.
Подзапрос должен включать в себя предложение SELECT и предложение FROM.
Подзапрос может включать необязательные предложения WHERE, GROUP BY и HAVING.
Подзапрос не может включать предложения COMPUTE или FOR BROWSE.
Вы можете включить предложение ORDER BY, только если включено предложение TOP.
Вы можете вкладывать подзапросы до 32 уровней.
Вы должны заключить подзапрос в скобки.
Подзапрос должен включать в себя предложение SELECT и предложение FROM.
Подзапрос может включать необязательные предложения WHERE, GROUP BY и HAVING.
Подзапрос не может включать предложения COMPUTE или FOR BROWSE.
Вы можете включить предложение ORDER BY, только если включено предложение TOP.
Вы можете вкладывать подзапросы до 32 уровней.
Подзапросы с оператором SELECT
Синтаксис
Подзапросы чаще всего используются с оператором SELECT. Ниже приведен основной синтаксис.
SELECT column_name [, column_name ] FROM table1 [, table2 ] WHERE column_name OPERATOR (SELECT column_name [, column_name ] FROM table1 [, table2 ] [WHERE])
пример
Рассмотрим таблицу CUSTOMERS, имеющую следующие записи.
ID NAME AGE ADDRESS SALARY 1 Ramesh 32 Ahmedabad 2000.00 2 Khilan 25 Delhi 1500.00 3 kaushik 23 Kota 2000.00 4 Chaitali 25 Mumbai 6500.00 5 Hardik 27 Bhopal 8500.00 6 Komal 22 MP 4500.00 7 Muffy 24 Indore 10000.00
Давайте применим следующий подзапрос с оператором SELECT.
SELECT * FROM CUSTOMERS WHERE ID IN (SELECT ID FROM CUSTOMERS WHERE SALARY > 4500)
Приведенная выше команда выдаст следующий вывод.
ID NAME AGE ADDRESS SALARY 4 Chaitali 25 Mumbai 6500. 00 5 Hardik 27 Bhopal 8500.00 7 Muffy 24 Indore 10000.00
Подзапросы с оператором INSERT
Подзапросы также могут использоваться с операторами INSERT. Оператор INSERT использует данные, возвращенные из подзапроса, для вставки в другую таблицу. Выбранные данные в подзапросе могут быть изменены с помощью любой символьной, даты или числовой функции.
Синтаксис
Ниже приведен основной синтаксис.
INSERT INTO table_name [ (column1 [, column2 ]) ] SELECT [ *|column1 [, column2 ] FROM table1 [, table2 ] [ WHERE VALUE OPERATOR ]
пример
Рассмотрим таблицу CUSTOMERS_BKP с такой же структурой, что и таблица CUSTOMERS. Ниже приведен синтаксис для копирования полной таблицы CUSTOMERS в CUSTOMERS_BKP.
INSERT INTO CUSTOMERS_BKP SELECT * FROM CUSTOMERS WHERE ID IN (SELECT ID FROM CUSTOMERS)
Подзапросы с оператором UPDATE
Подзапрос может использоваться вместе с оператором UPDATE. Можно использовать один или несколько столбцов в таблице при использовании подзапроса с оператором UPDATE.
Синтаксис
Ниже приведен основной синтаксис.
UPDATE table SET column_name = new_value [ WHERE OPERATOR [ VALUE ] (SELECT COLUMN_NAME FROM TABLE_NAME) [ WHERE) ]
пример
Предположим, у нас есть таблица CUSTOMERS_BKP, которая является резервной копией таблицы CUSTOMERS.
Следующий пример команды обновляет SALARY в таблице CUSTOMERS в 0,25 раза для всех клиентов, чей возраст больше или равен 27.
UPDATE CUSTOMERS SET SALARY = SALARY * 0.25 WHERE AGE IN (SELECT AGE FROM CUSTOMERS_BKP WHERE AGE >= 27 )
Это повлияет на две строки, и, наконец, таблица CUSTOMERS будет иметь следующие записи.
ID NAME AGE ADDRESS SALARY 1 Ramesh 32 Ahmedabad 500.00 2 Khilan 25 Delhi 1500.00 3 kaushik 23 Kota 2000. 00 4 Chaitali 25 Mumbai 6500.00 5 Hardik 27 Bhopal 2125.00 6 Komal 22 MP 4500.00 7 Muffy 24 Indore 10000.00
Подзапросы с оператором DELETE
Подзапрос может использоваться вместе с оператором DELETE, как и любые другие операторы, упомянутые выше.
Синтаксис
Ниже приведен основной синтаксис.
DELETE FROM TABLE_NAME [ WHERE OPERATOR [ VALUE ] (SELECT COLUMN_NAME FROM TABLE_NAME) [ WHERE) ]
пример
Предположим, у нас есть таблица CUSTOMERS_BKP, которая является резервной копией таблицы CUSTOMERS.
В следующем примере команды удаляются записи из таблицы CUSTOMERS для всех клиентов, чей возраст больше или равен 27.
DELETE FROM CUSTOMERS WHERE AGE IN (SELECT AGE FROM CUSTOMERS_BKP WHERE AGE >=27 )
Это повлияет на две строки, и, наконец, таблица CUSTOMERS будет иметь следующие записи.
Как писать подзапросы в SQL
В этой статье кратко объясняется, как написать подзапрос на языке SQL, на примерах.
Введение
SQL-запрос — это команда, используемая для запроса данных из таблиц, хранящихся в реляционных базах данных. Как правило, SQL-запрос содержит как минимум два или более предложений:
- Выберите предложение : Это предложение используется для указания метаданных набора результатов (столбцы, фиксированные значения, выражения).
- Из пункта : Этот пункт используется для указания запрашиваемых источников данных. Источником данных может быть одна таблица или представление, а также более сложные формы.
- Где пункт : Этот пункт используется для указания операций фильтрации данных, необходимых в SQL-запросе.
В следующих разделах объясняется, как написать подзапрос на SQL в предложениях SELECT, FROM и WHERE.
- Примечание: Все примеры в этой статье сделаны с использованием базы данных Stack Overflow 2013 и SQL Server 2019
Запись подзапросов в предложении SELECT
Сначала мы объясним, как написать подзапрос на SQL в предложении SELECT. Даже если написание подзапроса
поддерживается в предложении SELECT, разработчики должны тщательно написать свой запрос, как только они решат его использовать, поскольку он
снижает производительность запроса.
Предположим, что нам нужно написать SQL-запрос для получения первых десяти пользователей в базе данных переполнения стека и
последний значок, полученный каждым пользователем. Рассмотрим следующий запрос:
SELECT TOP (10) [Id] ,[DisplayName] ,(SELECT TOP 1 [Name] FROM [dbo].[Значки] badges WHERE badges.UserId = users.Id Order By [Date] Desc) as Latest_Badge FROM [StackOverflow2013]. [dbo].[Users] пользователи |
Рисунок 1. Написание подзапроса в предложении SELECT
Запись подзапроса в виде столбца не означает, что подзапрос выполняется для каждой строки, полученной из таблицы Users.
стол. Если мы отобразим предполагаемый план выполнения, он покажет, что данные значков извлекаются, а затем остаются присоединенными.
с таблицей пользователей.
Рисунок 2 – План выполнения
Запись подзапросов в предложении FROM
В этом разделе мы проиллюстрируем, как написать подзапрос на SQL в предложении FROM.
Вместо использования имени таблицы или представления в предложении FROM мы можем использовать подзапрос SQL в качестве источника данных, отметив, что
требуется присвоение псевдонима. Попробуем написать предыдущий запрос по-другому:
ВЫБЕРИТЕ [Идентификатор] ,[DisplayName] ,(SELECT TOP 1 [Name] FROM [dbo]. [Значки] badges WHERE badges.UserId = users.Id ORDER BY [Date] DESC) as Latest_Badge FROM (SELECT TOP 10 * ОТ [StackOverflow2013].[dbo].[Пользователи] ) пользователи |
Вместо того, чтобы писать параметр TOP 10 в предложении SELECT, мы решили заставить оптимизатор запросов SQL Server
выполните операцию извлечения данных TOP 10 перед присоединением к таблице Users с данными Badges, как показано
ранее.
Рисунок 3. Запись подзапроса в предложении FROM
На скриншоте ниже вы можете заметить, как выполняется оператор TOP сразу после сканирования кластера Users.
index, тогда как в предыдущем разделе он выполнялся как последний шаг.
Рисунок 4 – План выполнения
- Примечание : Это не означает, что второй подход лучше первого. Пример используется только для иллюстрации влияния перемещения оператора TOP в подзапрос
Написание подзапросов в JOINS
Кроме того, мы можем добавлять соединения в предложении FROM при использовании подзапросов. Давайте используем следующий пример, чтобы проиллюстрировать, как написать подзапрос на SQL в предложении FROM, когда необходимы соединения.
SELECT users.[Id] ,[DisplayName] ,latest_posts.[CreationDate] ИЗ [StackOverflow2013].[dbo].[Пользователи] пользователи INNER JOIN (ВЫБЕРИТЕ ТОП-10 [OwnerUserId],[CreationDate] FROM [dbo].[Posts] ORDER BY [CreationDate] DESC) |
В приведенном выше примере мы использовали подзапрос для получения последних десяти сообщений и даты их создания. Затем мы присоединились
результат с таблицей пользователей, чтобы получить информацию о владельцах сообщений.
Рисунок 5. Использование подзапроса SQL в предложении FROM с соединениями
Написание подзапросов в предложении WHERE
Чтобы проиллюстрировать, как написать подзапрос в SQL в предложении WHERE, мы отредактируем предыдущий запрос, чтобы получить
пользователей, опубликовавших последние десять сообщений в базе данных переполнения стека. Давайте воспользуемся следующим запросом:
SELECT [Id] ,[DisplayName] FROM [StackOverflow2013].[dbo].[Users] users WHERE [Id] IN (SELECT TOP 10 [OwnerUserId] FROM [dbo].[Posts] ORDER ПО [Дата Создания] DESC) |
В этом запросе мы переместили подзапрос из предложения FROM в предложение WHERE и использовали оператор IN для
отфильтровать идентификатор пользователя на основе результата подзапроса.
Рисунок 6. Запись подзапроса SQL в предложении WHERE
Альтернативы
Существует множество альтернатив использования подзапросов в SQL:
- Использование представлений: в некоторых случаях представления могут заменять подзапросы, чтобы запрос выглядел проще. Этот параметр не влияет на производительность запросов и не улучшает ее, за исключением индексированных представлений. Вы можете узнать больше о представлениях в следующей статье: Изучение SQL: Представления SQL
- Использование общих табличных выражений (CTE): Общие табличные выражения являются альтернативой подзапросам. Вы можете узнать больше об этой функции в следующей статье: CTE в SQL Server; Запрос общих табличных выражений
Сводка
В этой статье показано, как написать подзапрос на SQL в предложениях SELECT, FROM и WHERE. Даже если эта возможность интересна для структурированного языка запросов (SQL), разработчикам следует использовать ее с осторожностью, поскольку она может повлиять на производительность запросов. Кроме того, крайне важно создавать некоторые индексы, когда это необходимо для повышения производительности запросов.
- Автор
- Последние сообщения
Хади Фадлаллах
Хади — профессионал SQL Server с более чем 10-летним опытом. Его основная специализация — интеграция данных. Он является одним из ведущих участников ETL и SQL Server Integration Services на Stackoverflow.com. Кроме того, он опубликовал несколько серий статей о Biml, функциях SSIS, поисковых системах, Hadoop и многих других технологиях.
Помимо работы с SQL Server, он работал с различными технологиями обработки данных, такими как базы данных NoSQL, Hadoop, Apache Spark. Он сертифицированный профессионал MongoDB, Neo4j и ArangoDB.
На академическом уровне Хади имеет две степени магистра в области компьютерных наук и бизнес-вычислений. В настоящее время он является доктором философии. кандидат наук о данных, специализирующийся на методах оценки качества больших данных.
Хади действительно любит узнавать что-то новое каждый день и делиться своими знаниями. Вы можете связаться с ним на его личном сайте.
Просмотреть все сообщения от Хади Фадлаллы
Последние сообщения от Хади Фадлаллы (посмотреть все)0003
У меня также сложилось впечатление, что JOIN всегда лучше, чем подзапрос в MySQL, но EXPLAIN — лучший способ вынести суждение. Вот пример, когда подзапросы работают лучше, чем JOIN.
Вот мой запрос с 3 подзапросами:
EXPLAIN SELECT vrl.list_id,vrl.ontology_id,vrl.position,l.name КАК list_name, vrlih.position КАК предыдущая_позиция, vrl.moved_date ОТ `vote-ranked-listory` vrl INNER JOIN списки l ON l.list_id = vrl.list_id ВНУТРЕННЕЕ СОЕДИНЕНИЕ `История элемента списка голосов` ВНУТРЕННЕЕ СОЕДИНЕНИЕ list_burial_state lbs ON lbs.list_id = vrl.list_id AND lbs.burial_score < 0,5 ГДЕ vrl.position <= 15 И l.status='ACTIVE' И l.is_public=1 И vrl.ontology_id <1000000000 И (ВЫБЕРИТЕ list_id ИЗ list_tag, ГДЕ list_id=l.list_id И tag_id=43) IS NULL И (ВЫБЕРИТЕ list_id ИЗ list_tag, ГДЕ list_id=l.list_id И tag_id=55) IS NULL И (ВЫБЕРИТЕ list_id ИЗ list_tag, ГДЕ list_id=l.list_id И tag_id=246403) НЕ НУЛЕВОЕ ЗАКАЗАТЬ ПО vrl.moved_date DESC LIMIT 200;
EXPLAIN показывает:
+-----+------+----------+----- ---+-------------------------------- -------+--------------+---------+--- --------------------------------+------+----------- --+ | идентификатор | тип_выбора | стол | тип | возможные_ключи | ключ | key_len | ссылка | строки | Экстра | +----+------+-----------+--------+--- -------------------------------------------------- +--------------+-----------+---------- -------------------------+--------------------+--- ---------+ | 1 | ОСНОВНОЙ | врл | индекс | ОСНОВНОЙ | дата_перемещения | 8 | НУЛЕВОЙ | 200 | Использование где | | 1 | ОСНОВНОЙ | л | eq_ref | ПЕРВИЧНЫЙ, статус, общедоступный, idx_lookup, is_public_status | ОСНОВНОЙ | 4 | ranker. vrl.list_id | 1 | Использование где | | 1 | ОСНОВНОЙ | врлих | eq_ref | ОСНОВНОЙ | ОСНОВНОЙ | 9| ranker.vrl.list_id,ranker.vrl.ontology_id,const | 1 | Использование где | | 1 | ОСНОВНОЙ | фунты | eq_ref | ОСНОВНОЙ,idx_list_burial_state,burial_score | ОСНОВНОЙ | 4 | ranker.vrl.list_id | 1 | Использование где | | 4 | ЗАВИСИМЫЙ ПОДЗАПРОС | list_tag | ссылка | list_tag_key, list_id, tag_id | list_tag_key | 9 | ranker.l.list_id, константа | 1 | Использование где; Использование индекса | | 3 | ЗАВИСИМЫЙ ПОДЗАПРОС | list_tag | ссылка | list_tag_key, list_id, tag_id | list_tag_key | 9| ranker.l.list_id, константа | 1 | Использование где; Использование индекса | | 2 | ЗАВИСИМЫЙ ПОДЗАПРОС | list_tag | ссылка | list_tag_key, list_id, tag_id | list_tag_key | 9 | ranker.l.list_id, константа | 1 | Использование где; Использование индекса | +----+------+-----------+--------+--- -------------------------------------------------- +--------------+-----------+---------- -------------------------+--------------------+--- ---------+
Тот же запрос с JOIN:
EXPLAIN SELECT vrl. list_id,vrl.ontology_id,vrl.position,l.name КАК list_name, vrlih.position КАК предыдущая_позиция, vrl.moved_date ОТ `vote-ranked-listory` vrl INNER JOIN списки l ON l.list_id = vrl.list_id ВНУТРЕННЕЕ СОЕДИНЕНИЕ `История элемента списка голосов` ВНУТРЕННЕЕ СОЕДИНЕНИЕ list_burial_state lbs ON lbs.list_id = vrl.list_id AND lbs.burial_score < 0,5 LEFT JOIN list_tag lt1 ON lt1.list_id = vrl.list_id AND lt1.tag_id = 43 LEFT JOIN list_tag lt2 ON lt2.list_id = vrl.list_id AND lt2.tag_id = 55 ВНУТРЕННЕЕ СОЕДИНЕНИЕ list_tag lt3 ON lt3.list_id = vrl.list_id AND lt3.tag_id = 246403 ГДЕ vrl.position <= 15 И l.status='ACTIVE' И l.is_public=1 И vrl.ontology_id <1000000000 И lt1.list_id — NULL, И lt2.tag_id — NULL ЗАКАЗАТЬ ПО vrl.moved_date DESC LIMIT 200;
и вывод:
+----+-------------+-------+--------+---- --------------------------------------------------+ --------------+----------+----------- ------+-------+-------- ------------------------+ | идентификатор | тип_выбора | стол | тип | возможные_ключи | ключ | key_len | ссылка | строки | Экстра | +----+-------------+-------+---------+------------- -------------------------------------------------------+--------- -----+---------+---------------------------------- -----------+-------+----------------- ---------------+ | 1 | ПРОСТО | л3 | ссылка | list_tag_key, list_id, tag_id | идентификатор_тега | 5 | константа | 2386 | Использование где; Использование временного; Использование сортировки файлов | | 1 | ПРОСТО | л | eq_ref | ПЕРВИЧНЫЙ, статус, общедоступный, idx_lookup, is_public_status | ОСНОВНОЙ | 4 | ranker. lt3.list_id | 1 | Использование где | | 1 | ПРОСТО | врлих | ссылка | ОСНОВНОЙ | ОСНОВНОЙ | 4 | ranker.lt3.list_id | 103 | Использование где | | 1 | ПРОСТО | врл | ссылка | ОСНОВНОЙ | ОСНОВНОЙ | 8 | ranker.lt3.list_id,ranker.vrlih.ontology_id | 65 | Использование где | | 1 | ПРОСТО | лт1 | ссылка | list_tag_key, list_id, tag_id | list_tag_key | 9| ranker.lt3.list_id,const | 1 | Использование где; Использование индекса; Не существует | | 1 | ПРОСТО | фунты | eq_ref | ОСНОВНОЙ,idx_list_burial_state,burial_score | ОСНОВНОЙ | 4 | ranker.vrl.list_id | 1 | Использование где | | 1 | ПРОСТО | л2 | ссылка | list_tag_key, list_id, tag_id | list_tag_key | 9 | ranker.lt3.list_id,const | 1 | Использование где; Использование индекса | +----+-------------+-------+---------+------------- -------------------------------------------------------+--------- -----+---------+---------------------------------- -----------+-------+----------------- ---------------+
Сравнение столбца строк
указывает на разницу, и запрос с JOIN использует Использование временного; Использование сортировки файлов
.