Sql вложенные запросы: Вложенные запросы (SQL Server) — SQL Server
Содержание
join — Помогите оптимизировать запрос SQL с вложенным запросом
Вопрос задан
Изменён
1 год 2 месяца назад
Просмотрен
46 раз
Подскажите пожалуйста SQL запрос чтобы получить количество первых сообщений (action = sendMess) в определенный период времени из Таблицы UserAction, желательно не использую вложенный запрос.
Например, количество пользователей отправивших первое сообщение после января 2019 года
userId | dateAction | action |
---|---|---|
111111 | 2018-12-05 15:16:16 | sendMess |
111111 | 2019-01-12 15:16:16 | sendMess |
111111 | 2019-01-11 15:16:16 | sendMess |
111111 | 2019-01-19 15:16:16 | sendMess |
222222 | 2019-01-07 15:16:16 | sendMess |
222222 | 2019-03-07 15:16:16 | sendMess |
222222 | 2020-04-29 15:16:16 | delMess |
333333 | 2022-01-12 15:16:16 | updMess |
333333 | 2022-01-12 15:16:16 | sendMess |
777777 | 2019-01-15 15:16:16 | sendMess |
777777 | 2019-05-15 15:16:16 | sendMess |
Ожидаемый рез: 2
(пользователи 222222 и 777777)
Мой план действий:
к таблице UserAction из временного периода приджоинить по userId таблицу с действиями находящимися раньше начала временного периода и по идее при джоине во второй таблице появится должны null поля — это будет означать что раньше этого времени не было сообщений.
Получилось реализовать через вложенные запросы, можно ли это переделать только через джоины?
SELECT ua.action, ua.userId, ua.dateAction, ua2.action, ua2.userId, ua2.dateAction, (COUNT(ua.userId) OVER () - COUNT(ua2.userId) OVER ()) as count FROM UserAction ua LEFT JOIN (SELECT ua2.action, ua2.userId, ua.dateAction FROM userAction ua2 WHERE 1 AND ua2.action = 'sendMess' AND ua2.dateAction < '2019-01-03 11:49:10' GROUP BY ua2.userId) ua2 USING (userId) WHERE 1 AND ua.action = 'sendMess' AND ua.dateAction > '2019-01-03 11:49:10' GROUP BY ua.userId
- sql
- join
1
Количество пользователей отправивших первое сообщение после указанной даты. Читаем как «Количество пользователей у которых минимальная дата сообщения больше указанной». И тогда получаем:
select count(1) from ( select userId from userAction where action = 'sendMess' group by userId having min(dateAction) > '2019-01-03 11:49:10' ) x
2
Зарегистрируйтесь или войдите
Регистрация через Google
Регистрация через Facebook
Регистрация через почту
Отправить без регистрации
Почта
Необходима, но никому не показывается
Отправить без регистрации
Почта
Необходима, но никому не показывается
Нажимая на кнопку «Отправить ответ», вы соглашаетесь с нашими пользовательским соглашением, политикой конфиденциальности и политикой о куки
Python SQLite: вложенные SQL-запросы
Смотреть материал на видео
На этом занятии
поговорим о возможности создавать вложенные запросы к СУБД. Лучше всего это
понять на конкретном примере. Предположим, что у нас имеются две таблицы:
Первая students содержит
информацию о студентах, а вторая marks – их отметки по
разным дисциплинам. Каждый из студентов (кроме четвертого) проходил язык Си. От
нас требуется выбрать всех студентов, у которых оценка по языку Си выше, чем у
Маши (студент с id = 2). По идее нам тут нужно реализовать два
запроса: первый получает значение оценки для Маши по языку Си:
SELECT mark FROM marks WHERE id = 2 AND subject LIKE 'Си'
А второй
выбирает всех студентов, у которых оценка по этому предмету выше, чем у Маши:
SELECT name, subject, mark FROM marks JOIN students ON students.rowid = marks.id WHERE mark > 3 AND subject LIKE 'Си'
Так вот, в языке
SQL эти два запроса
можно объединить, используя идею вложенных запросов:
SELECT name, subject, mark FROM marks JOIN students ON students.rowid = marks.id WHERE mark > (SELECT mark FROM marks WHERE id = 2 AND subject LIKE 'Си') AND subject LIKE 'Си'
Мы здесь во
второй запрос вложили первый для определения оценки Маши по предмету Си.
Причем, этот вложенный запрос следует записывать в круглых скобках, говоря
СУБД, что это отдельная конструкция, которую следует выполнить независимо. То,
что будет получено на его выходе и будет фигурировать в качестве Машиной
оценки.
Но, что если
вложенный запрос вернет несколько записей (оценок), например, если записать его
вот так:
SELECT name, subject, mark FROM marks JOIN students ON students.rowid = marks.id WHERE mark > (SELECT mark FROM marks WHERE id = 2 ) AND subject LIKE 'Си'
В этом случае
будет использован только первый полученный результат, другие попросту
проигнорируются и результат будет тем же (так как первое значение – это оценка
Маши по предмету Си).
Если же
вложенный SELECT ничего не
находит (возвращает значение NULL), то внешний запрос не будет возвращать
никаких записей.
Также следует
обращать внимание, что подзапросы не могут обрабатывать свои результаты,
поэтому в них нельзя указывать, например, оператор GROUP BY. Но
агрегирующие функции вполне можно использовать, например, так:
SELECT name, subject, mark FROM marks JOIN students ON students.rowid = marks.id WHERE mark > (SELECT avg(mark) FROM marks WHERE id = 2 ) AND subject LIKE 'Си'
Вложения в команде INSERT
Вложенные
запросы можно объявлять и в команде INSERT. Предположим,
что у нас имеется еще одна таблица female вот с такой
структурой:
Она идентична по
структуре таблице students со списком студентов. Наша задача
добавить в female всех студентов
женского пола.
Для начала
запишем запрос выбора девушек из таблицы students:
SELECT * FROM students WHERE sex = 2
А, затем,
укажем, что их нужно поместить в таблицу female:
INSERT INTO female SELECT * FROM students WHERE sex = 2
После выполнения
этого запроса таблица female будет содержать следующие
записи:
Но если
выполнить запрос еще раз, то возникнет ошибка, т. к. мы попытаемся добавить
строки с уже существующими id, что запрещено по структуре этого поля
– оно определено как главный ключ и содержит только уникальные значения.
Чтобы поправить
ситуацию, можно вложенный запрос написать так:
INSERT INTO female SELECT NULL, name, sex, old FROM students WHERE sex = 2
Мы здесь в
качестве значения первого поля указали NULL и,
соответственно, СУБД вместо него сгенерирует уникальный ключ для добавляемых
записей. Теперь таблица female выглядит так:
Вложения в команде UPDATE
Похожим образом
можно создавать вложенные запросы и для команды UPDATE. Допустим, мы
хотим обнулить все оценки в таблице marks, которые меньше
или равны минимальной оценки студента с id = 1. Такой
запрос можно записать в виде:
UPDATE marks SET mark = 0 WHERE mark <= (SELECT min(mark) FROM marks WHERE id = 1)
И на выходе
получим измененную таблицу:
Как видите,
минимальная оценка у первого студента была равна 3 и все тройки обнулились.
Вложения в команде DELETE
Ну, и наконец,
аналогичные действия можно выполнять и в команде DELETE. Допустим,
требуется удалить из таблицы students всех студентов, возраст которых
меньше, чем у Маши (студента с id = 2). Запрос будет выглядеть так:
DELETE FROM students WHERE old < (SELECT old FROM students WHERE id = 2)
В результате,
получим таблицу:
Вот так
создаются вложенные запросы в языке SQL. Однако, прибегать к ним следует
в последнюю очередь, если никакими другими командами не удается решить
поставленную задачу. Так как они создают свое отдельное обращение к БД и на это
тратятся дополнительные вычислительные ресурсы.
Конечно, на этом
занятии мы лишь рассмотрели примеры, принцип создания вложенных запросов. На
практике они могут разрастаться и становиться довольно объемными, включать в
себя различные дополнительные операции для выполнения нетривиальных действий с таблицами
БД.
На этом мы
завершим обзор SQL-языка. Этого материала вам будет вполне достаточно
для начальной работы с БД. По мере развития сможете дальше углубляться в эту
тему и узнавать множество новых подходов к реализации различных задач с помощью
SQL-запросов.
Видео по теме
Python SQLite #1: что такое СУБД и реляционные БД
Python SQLite #2: подключение к БД, создание и удаление таблиц
Python SQLite #3: команды SELECT и INSERT при работе с таблицами БД
Python SQLite #4: команды UPDATE и DELETE при работе с таблицами
Python SQLite #5: агрегирование и группировка GROUP BY
Python SQLite #6: оператор JOIN для формирования сводного отчета
Python SQLite #7: оператор UNION объединения нескольких таблиц
Python SQLite #8: вложенные SQL-запросы
Python SQLite #9: методы execute, executemany, executescript, commit, rollback и свойство lastrowid
Python SQLite #10: методы fetchall, fetchmany, fetchone, Binary, iterdump
Пример вложенного подзапроса SQL Вопрос для собеседования
Обзор вложенных запросов
Вложенный запрос SQL, также известный как подзапрос или подзапрос, представляет собой метод запроса SELECT, встроенный в другой запрос. Это эффективный способ получить желаемый результат, который может потребовать нескольких шагов запроса или для обработки взаимозависимых запросов.
Когда использовать вложенные подзапросы
Вложенные подзапросы можно использовать несколькими способами:
- Вы можете запрашивать подзапросы (например, выбирать из подзапроса)
- Вы можете заменить одномерные массивы (например, типичный список элементов) и соединения отдельных полей одним подзапросом в предложении WHERE или HAVING
Чтобы использовать подзапрос, необходимо соблюдать несколько синтаксических правил:
- Подзапрос должен быть заключен в круглые скобки
- В зависимости от используемого механизма SQL вам может потребоваться псевдоним для данного подзапроса
- При использовании в предложении WHERE или HAVING оператор SELECT подзапроса может возвращать только одно оцениваемое поле
Пример вопроса SQL для собеседования с использованием вложенного подзапроса

Рассчитать совокупный процент от общего объема продаж в данный день. Вывод таблицы должен выглядеть примерно так, как показано ниже.
дата | pct_total_sales |
---|---|
05.01.2020 | Х% |
07.01.2020 | Д% |
Вы можете работать с этим примером, используя интерактивную скрипку SQL здесь.
Прежде чем мы начнем писать SQL, мы разобьем вопрос на этапы:
- Подсчитаем общий объем продаж за день
- Рассчитать кумулятивную сумму общего объема продаж за день и общего объема продаж за все дни
- Разделить общий объем продаж за день на общую сумму
1. Подсчитайте сумму продаж за день
Сначала мы напишем базовый запрос, который на следующем шаге станет подзапросом. Приведенный ниже запрос вычисляет общий объем продаж за день, и вы можете взаимодействовать с запросом с помощью этой скрипки SQL.
ВЫБОР #нам нужно просуммировать sale_usd по дате дата, сумма (sale_usd) как total_usd ОТ sales_info #поскольку мы агрегируем sale_usd по дате, нам нужно #нужно группировать по дате ГРУППИРОВАТЬ ПО дате
2. Рассчитать кумулятивную сумму общего объема продаж за день и общего объема продаж за все дни
Приведенный ниже запрос вычисляет кумулятивную сумму общего объема продаж за день и общего объема продаж за все дни. Вы можете взаимодействовать с приведенным ниже запросом с помощью этой скрипты SQL.
На этом шаге вы заметите, что мы строим запрос на основе предыдущего запроса из предыдущего шага. Чтобы успешно построить подзапрос, нам нужно было заключить подзапрос в круглые скобки и убедиться, что мы используем псевдоним для таблицы (поскольку мы используем MySQL, обратите внимание, что это не является требованием для некоторых вариантов SQL). Нам также необходимо убедиться, что мы включили все поля, необходимые для внешнего запроса (например, дату и общий объем продаж).
ВЫБОР #в этом запросе мы не группируем по, так как используем оконную функцию дата, СУММА(total_usd) ПРЕВЫШЕ ( ORDER BY даты ASC строки МЕЖДУ неограниченной предыдущей и текущей строкой) as cum_total, # это оконная функция для # вычисляем общую сумму SUM(total_usd) OVER () как итог # это оконная функция для # подсчитать итог ОТ( ВЫБИРАТЬ #нам нужно просуммировать sale_usd по дате дата, сумма (sale_usd) как total_usd ОТ sales_info #поскольку мы агрегируем sale_usd по дате, нам нужно #нужно группировать по дате СГРУППИРОВАТЬ ПО дате ) as q1 #создаем псевдоним для этой таблицы, как того требует MySQL
3. Разделите совокупный общий объем продаж на совокупную сумму
Последний шаг — разделить cum_total
на общую сумму. Мы можем сделать это на том же шаге, что и выше (просто разделив две оконные функции), или мы можем построить подзапрос поверх предыдущего шага. В приведенном ниже запросе используется другой подзапрос, в результате чего конечный запрос имеет два вложенных подзапроса. Вы можете взаимодействовать с приведенным ниже запросом с помощью этой скрипты SQL.
ВЫБОР дата, 100 * cum_total / всего как ОТ( #в этом запросе мы не группируем по #поскольку мы используем оконную функцию ВЫБИРАТЬ дата, СУММ(total_usd) ПРЕВЫШЕНО (ПО ДАТЕ ASC строки МЕЖДУ неограниченной предыдущей и текущей строкой) as cum_total, # это оконная функция для # вычисляем общую сумму SUM(total_usd) OVER () как итог # это оконная функция # для расчета суммы ОТ( ВЫБИРАТЬ #нам нужно просуммировать sale_usd по дате дата, сумма (sale_usd) как total_usd ОТ sales_info #поскольку мы агрегируем sale_usd по дате, нам нужно #нужно группировать по дате СГРУППИРОВАТЬ ПО дате ) as q1 #мы создаем псевдоним для этой таблицы, как того требует MySQL ) как q2
Видео о вложенных запросах в Inductive University
[00:00]
В этом уроке я покажу вам, как настроить в отчете вложенный запрос, чтобы использовать результаты одного запроса для управления другим. Давайте сначала взглянем на два примера таблиц, которые есть в моей базе данных. Сначала у меня есть таблица с записями оборудования. Он имеет столбцы имени идентификатора и описания. Кроме того, есть таблица, содержащая информацию о событиях простоя. В нем есть столбец с идентификатором события простоя, столбец с идентификатором оборудования, которое вышло из строя, а затем столбцы с указанием причины простоя и минут, в течение которых оно было недоступно. Я хочу включить данные из обеих этих таблиц в свой отчет. Мы можем видеть, что они связаны между собой через столбец ID в таблице Equipment_table и ID оборудования в таблице downtime_table. Что я хотел бы сделать, так это запустить запрос, который возвращает данные о каждой единице оборудования, а затем я могу использовать его, чтобы сообщить мне о любом времени простоя для каждой единицы оборудования. К счастью, вложенный запрос поможет мне сделать это. Я начну с создания источника данных SQL-запроса и изменю имя ключа данных на «оборудование».
У меня уже есть скопированный запрос, который я вставлю сюда, и мы можем быстро перейти к нему.
[01:07]
По сути, этот запрос возвращает каждый из столбцов из таблицы Equipment_table и назначает им псевдонимы. Идентификатор становится идентификационным номером оборудования, имя становится именем оборудования, а описание становится описанием оборудования. Это будет мой родительский запрос. Чтобы добавить дочерний запрос. Я перейду к разделу вложенных запросов и щелкну значок плюса. Мне нужен только один ребенок для этого примера. Но если вам нужно больше в вашей вложенной системе запросов, вы можете продолжать щелкать значок плюса, чтобы добавить дочерние элементы к своему дочернему, или вы также можете добавить одноранговые узлы. Я изменю ключ данных своего дочернего запроса, чтобы оборудовать время простоя, и возьму другой запрос. Этот запрос возвращает столбцы причины и минут простоя из таблицы времени простоя и назначает им псевдонимы. Он также использует предложение «где» для фильтрации результатов в столбце Equipment_ID. Я хочу отфильтровать результаты этого запроса на основе результатов родительского запроса, чтобы сделать это. Мне понадобится параметр, использующий синтаксис фигурных скобок. И тогда моим именем параметра будет имя столбца из первой таблицы, которая относится к этой таблице. Если вы помните, этот столбец называется «id», но я указал его в псевдониме «идентификационный номер оборудования» в своем запросе.
[02:17]
Таким образом, я могу использовать идентификационный номер оборудования в качестве своего параметра, если бы я не использовал псевдоним, я бы просто использовал здесь идентификатор. Это хорошая идея использовать здесь псевдонимы, потому что если бы я этого не сделал и у меня была опечатка в поле параметра, дочерний элемент будет искать соответствие в каждом столбце каждого родительского запроса, а если он не найдет его там, он будет искать в параметры для матча, а также. Если в конечном итоге он найдет что-то с похожим названием, вы можете оказаться в ситуации, когда вы просматриваете неправильные данные, и может быть трудно определить, почему. Так что псевдонимы могут быть здесь полезны. Итак, у меня настроен вложенный запрос, и я хочу проверить это. Я перейду на вкладку предварительного просмотра, и мы сможем посмотреть на результаты. Я не буду рассказывать, как я устанавливал этот стол. При желании вы можете сослаться на страницу групп таблиц в руководстве пользователя. Все, что вам нужно знать, это то, что результаты родительского запроса отображаются серым цветом, а результаты дочернего запроса — красным.
[03:04]
Это работает так, что родительские запросы, выполняемые в, возвращают определенное количество строк. Затем дочерние запросы выполняются для каждой строки, возвращаемой родителем. Важно отметить, что для каждой строки, возвращаемой родительским запросом, запускается новый запрос. Если у вас есть дополнительные вложенные дочерние запросы, которые выполняются для каждой строки, возвращаемой первым набором дочерних элементов. Количество запросов может увеличиваться в геометрической прогрессии. Важно знать о последствиях во время выполнения. Производительность системы может снизиться из-за достаточно сложной системы запросов, и вам, возможно, придется некоторое время ждать результатов. Теперь вы можете подумать, что этот пример также можно реализовать с помощью соединения SQL, и вы будете правы. Однако вложенные запросы выгодны, поскольку их гораздо проще писать и поддерживать по сравнению со сложными операторами соединения, и они обеспечивают больший контроль. Самым большим преимуществом также является то, что вложенные запросы имеют гораздо меньше ограничений, чем соединение SQL, если бы вложенные запросы можно было настроить для связывания данных между базами данных различных схем и даже другими источниками данных, такими как архиватор тегов, что делает их чрезвычайно мощным инструментом.
В этом уроке я покажу вам, как настроить в отчете вложенный запрос, чтобы использовать результаты одного запроса для управления другим. Давайте сначала взглянем на два примера таблиц, которые есть в моей базе данных. Сначала у меня есть таблица с записями оборудования. Он имеет столбцы имени идентификатора и описания. Кроме того, есть таблица, содержащая информацию о событиях простоя. В нем есть столбец с идентификатором события простоя, столбец с идентификатором оборудования, которое вышло из строя, а затем столбцы с указанием причины простоя и минут, в течение которых оно было недоступно. Я хочу включить данные из обеих этих таблиц в свой отчет. Мы можем видеть, что они связаны между собой через столбец ID в таблице Equipment_table и ID оборудования в таблице downtime_table. Что я хотел бы сделать, так это запустить запрос, который возвращает данные о каждой единице оборудования, а затем я могу использовать его, чтобы сообщить мне о любом времени простоя для каждой единицы оборудования. К счастью, вложенный запрос поможет мне сделать это. Я начну с создания источника данных SQL-запроса и изменю имя ключа данных на «оборудование». У меня уже есть скопированный запрос, который я вставлю сюда, и мы можем быстро перейти к нему.
[01:07] По сути, этот запрос возвращает каждый из столбцов из таблицы Equipment_table и назначает им псевдонимы. Идентификатор становится идентификационным номером оборудования, имя становится именем оборудования, а описание становится описанием оборудования. Это будет мой родительский запрос. Чтобы добавить дочерний запрос. Я перейду к разделу вложенных запросов и щелкну значок плюса. Мне нужен только один ребенок для этого примера. Но если вам нужно больше в вашей вложенной системе запросов, вы можете продолжать щелкать значок плюса, чтобы добавить дочерние элементы к своему дочернему, или вы также можете добавить одноранговые узлы. Я изменю ключ данных своего дочернего запроса, чтобы оборудовать время простоя, и возьму другой запрос. Этот запрос возвращает столбцы причины и минут простоя из таблицы времени простоя и назначает им псевдонимы. Он также использует предложение «где» для фильтрации результатов в столбце Equipment_ID. Я хочу отфильтровать результаты этого запроса на основе результатов родительского запроса, чтобы сделать это. Мне понадобится параметр, использующий синтаксис фигурных скобок. И тогда моим именем параметра будет имя столбца из первой таблицы, которая относится к этой таблице. Если вы помните, этот столбец называется «id», но я указал его в псевдониме «идентификационный номер оборудования» в своем запросе.
[02:17] Таким образом, я могу использовать идентификационный номер оборудования в качестве своего параметра, если бы я не использовал псевдоним, я бы просто использовал идентификатор здесь. Это хорошая идея использовать здесь псевдонимы, потому что если бы я этого не сделал и у меня была опечатка в поле параметра, дочерний элемент будет искать соответствие в каждом столбце каждого родительского запроса, а если он не найдет его там, он будет искать в параметры для матча, а также. Если в конечном итоге он найдет что-то с похожим названием, вы можете оказаться в ситуации, когда вы просматриваете неправильные данные, и может быть трудно определить, почему. Так что псевдонимы могут быть здесь полезны. Итак, у меня настроен вложенный запрос, и я хочу проверить это. Я перейду на вкладку предварительного просмотра, и мы сможем посмотреть на результаты. Я не буду рассказывать, как я устанавливал этот стол. При желании вы можете сослаться на страницу групп таблиц в руководстве пользователя. Все, что вам нужно знать, это то, что результаты родительского запроса отображаются серым цветом, а результаты дочернего запроса — красным.
[03:04] Это работает так, что родительские запросы, выполняемые в, возвращают определенное количество строк. Затем дочерние запросы выполняются для каждой строки, возвращаемой родителем. Важно отметить, что для каждой строки, возвращаемой родительским запросом, запускается новый запрос. Если у вас есть дополнительные вложенные дочерние запросы, которые выполняются для каждой строки, возвращаемой первым набором дочерних элементов. Количество запросов может увеличиваться в геометрической прогрессии. Важно знать о последствиях во время выполнения.