Вложенный select. Вложенные запросы. Примеры

В прошлом уроке мы столкнулись с одним неудобством. Когда мы хотели узнать, кто создал тему "велосипеды", и делали соответствующий запрос:

Вместо имени автора, мы получали его идентификатор. Это и понятно, ведь мы делали запрос к одной таблице - Темы, а имена авторов тем хранятся в другой таблице - Пользователи. Поэтому, узнав идентификатор автора темы, нам надо сделать еще один запрос - к таблице Пользователи, чтобы узнать его имя:

В SQL предусмотрена возможность объединять такие запросы в один путем превращения одного из них в подзапрос (вложенный запрос). Итак, чтобы узнать, кто создал тему "велосипеды", мы сделаем следующий запрос:

То есть, после ключевого слова WHERE , в условие мы записываем еще один запрос. MySQL сначала обрабатывает подзапрос, возвращает id_author=2, и это значение передается в предложение WHERE внешнего запроса.

В одном запросе может быть несколько подзапросов, синтаксис у такого запроса следующий: Обратите внимание, что подзапросы могут выбирать только один столбец, значения которого они будут возвращать внешнему запросу. Попытка выбрать несколько столбцов приведет к ошибке.

Давайте для закрепления составим еще один запрос, узнаем, какие сообщения на форуме оставлял автор темы "велосипеды":

Теперь усложним задачу, узнаем, в каких темах оставлял сообщения автор темы "велосипеды":

Давайте разберемся, как это работает.

  • Сначала MySQL выполнит самый глубокий запрос:

  • Полученный результат (id_author=2) передаст во внешний запрос, который примет вид:

  • Полученный результат (id_topic:4,1) передаст во внешний запрос, который примет вид:

  • И выдаст окончательный результат (topic_name: о рыбалке, о рыбалке). Т.е. автор темы "велосипеды" оставлял сообщения в теме "О рыбалке", созданной Сергеем (id=1) и в теме "О рыбалке", созданной Светой (id=4).
Вот собственно и все, что хотелось сказать о вложенных запросах. Хотя, есть два момента, на которые стоит обратить внимание:
  • Не рекомендуется создавать запросы со степенью вложения больше трех. Это приводит к увеличению времени выполнения и к сложности восприятия кода.
  • Приведенный синтаксис вложенных запросов, скорее наиболее употребительный, но вовсе не единственный. Например, мы могли бы вместо запроса

    написать

    Т.е. мы можем использовать любые операторы, используемые с ключевым словом WHERE (их мы изучали в прошлом уроке).

Объединение таблиц.

В запросе можно объединять данные из одной или нескольких таблиц. Такое объединение таблиц называется соединением (связыванием) таблиц. Различают внутреннее и внешнее соединения.

Внутреннее соединение – это соединение, при котором результирующий набор получается путем перечисления нужных полей из разных таблиц после слова SELECT. При этом таблицы должны находиться в отношении «один-к-одному».

Например,

SELECT R.fam, R.birthday,A.Foto

FROM Rabotniki R, Advanced A

где таблицы Rabotniki и Advanced содержат основные и дополнительные сведения о работниках предприятия. Связь «один-к-одному». Таблице Rabotniki дан псевдоним R, а таблице Advanced дан псевдоним A.

В предыдущем примере перед именем поля записано имя таблицы. Имена поля и таблицы отделяются точкой. Имя таблицы указывать необходимо, если имена полей повторяются для различных таблиц.

Если применить внутреннее соединение к таблицам, связанным по принципу «один-ко-многим», то результирующий набор может содержать избыточную информацию. Для устранения избыточности используют критерии отбора.

Пример 1 . Дана БД Sotrudniki, состоящая из двух таблиц:

Запрос внутреннего соединения таблиц, связанных отношением «один-ко-многим» имеет вид

Число записей в результирующем наборе данных равно произведению числа записей в таблице Sotrudniki на число записей в таблице Doljn. Результирующий набор данных имеет вид и содержит избыточную информацию:

Для ограничения числа записей в результирующем наборе данных применяют критерии отбора. Запрос в этом случае будет иметь вид

SELECT S_Fio,S_Birthday,D_Nazv FROM Sotrudniki, Doljn

WHERE S_Doljn=D_Code

Число записей в результирующем наборе данных будет равно числу записей в таблице Sotrudniki. p

Пример 2 . Требуется для БД Sotrudniki сформировать запрос, который позволит получить список сотрудников, имеющих должность «программист». Получим

SELECT S_fio, S_birthday FROM sotrudniki, doljn

WHERE S_doljn=D_code and D_nazv="программист"

В SQL-запросах допускается самообъединение таблицы. В этом случае одной таблице даются два псевдонима.

Например, для нахождения всех ровесников в таблице Sotrudniki можно написать запрос:

SELECT s1.s_fio, s2.s_fio, s1.s_birthday

FROM Sotrudniki s1, Sotrudniki s2

WHERE (EXTRACT(YEAR

FROM s1.s_birthday)=EXTRACT(YEAR

FROM s2.s_birthday))

AND (s1.s_fio!=s2.s_fio) AND (s1.s_fio

Последнее условие упорядочивает фамилии и исключает дублирование результатов.

При внутреннем соединении все таблицы, поля которых указаны в SQL-запросе, являются равноправными. То есть каждой записи в первой таблице находилась соответствующая ей запись во второй таблице.

При внешнем объединении (outer join ) в результирующий набор включаются записи независимо от того, есть ли соответствующее поле во второй таблице. Существует три типа внешнего объединения.

1) LEFT OUTER JOIN … ON – левое, включает в результат все записи первой таблицы, даже те, для которых не имеется соответствия во второй.

2) RIGHT OUTER JOIN … ON – правое, включает в результат все записи второй таблицы, даже те, для которых не имеется соответствия в первой.

3) FULL OUTER JOIN … ON – полное, включает в результат объединение записей обеих таблиц, независимо от их соответствия.

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

Например. Пусть в таблице Sotrudniki БД Sotrudniki есть фамилии, имеющие должность, не указанную в таблице Doljn, и есть должности в таблице Doljn, для которых нет фамилии в таблице Sotrudniki. Тогда

1) SELECT * FROM Sotrudniki LEFT OUTER JOIN Doljn

ON S_doljn=D_code

Результат включат все поля и таблицы Sotrudniki и таблицы Doljn. Число строк соответствует числу записей таблицы Sotrudniki. В строках, относящихся к записям, для которых в Doljn не нашлось соответствие, поля таблицы Doljn остаются пустыми.

2) SELECT * FROM Sotrudniki RIGHT OUTER JOIN Doljn

ON S_doljn=D_code

Число строк соответствует числу записей таблицы Doljn. В строках, относящихся к записям, для которых в Sotrudniki не нашлось соответствие, поля таблицы Sotrudniki остаются пустыми.

3) SELECT * FROM Sotrudniki FULL OUTER JOIN Doljn ON

К строкам, относящимся к таблице Sotrudniki добавлены строки, относящиеся к таблице Doljn, для которых нет соответствия в таблице Sotrudniki.


В SQL-запросе можно использовать запросы, вложенные в первый. Это можно применить и к операторам, возвращающих совокупные характеристики, и к операторам, возвращающим множество значений.

Например,

1) Определить всех однофамильцев в таблицах Sotrudniki и Sotrudniki1, имеющих одинаковую структуру:

SELECT * FROM Sotrudniki

WHERE S_fio IN (SELECT S_fio FROM Sotrudniki1)

Вложенный оператор SELECT возвращает множество фамилий из таблицы Sotrudniki1, а конструкция WHERE основного оператора SELECT отбирает в таблице Sotrudniki те записи, которые имеются во множестве фамилий из таблицы Sotrudniki1.

2) Вывести из БД Sotrudniki фамилию (фамилии) самого молодого сотрудника:

SELECT S_Fio, EXTRACT(YEAR FROM S_Birthday)

WHERE EXTRACT(YEAR FROM S_Birthday)=

(SELECT max(EXTRACT(YEAR FROM S_Birthday))

FROM Sotrudniki)

Вложенный оператор SELECT возвращает максимальный год рождения, который используется в условии WHERE основного оператора SELECT.

3) Вывести из БД Students все оценки конкретного студента, например, Петрова:

SELECT S_fam, P_nazv, E_mark FROM

Examination,Predm, Students

WHERE E_student=(SELECT S_code FROM Students

WHERE S_fam="Петров")

AND E_Predm=P_code AND E_Student=S_code

Во вложенной конструкции SELECT определяется код студента по фамилии "Петров", а последние условия обеспечивают исключение избыточности при внутреннем объединении таблиц.

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

Например, получить сведения о предметах, по которым проводился экзамен конкретного числа, например, ‘14.01.2006’:

SELECT * FROM Predm PR

WHERE "14.01.2006" IN (SELECT E_date

FROM Examination

WHERE PR.P_code=E_predm)

Эту же задачу можно решить с помощью операции соединения таблиц:

SELECT DISTINCT P_nazv FROM Predm, Examination

WHERE P_code=E_predm AND E_date= "14.01.2006"

Пример. Вывести фамилию (фамилии) студента, получившего на экзамене оценку выше среднего балла

SELECT DISTINCT S_fam FROM Students, Examination

E_mark>(SELECT AVG(E_mark) FROM Examination)

AND S_code=E_student

В условии WHERE при работе с множествами записей можно использовать ключевые слова ALL и ANY. ALL - условие выполняется для всех записей, ANY - условие выполняется хотя бы для одной записи.

Например ,

1) вывести фамилии сотрудников из таблицы Sotrudniki, которые не старше любого сотрудника в таблице Sotrudniki1:

WHERE S_birthday>= ALL (SELECT S_birthday

FROM Sotrudniki1)

2) вывести фамилии сотрудников из таблицы Sotrudniki, которые моложе хотя бы одного сотрудника в таблице Sotrudniki1:

SELECT S_fio,S_birthday FROM Sotrudniki

WHERE S_birthday> ANY (SELECT S_birthday FROM Sotrudniki1)

Во вложенных конструкциях SELECT можно использовать ключевое слово EXISTS, которое означает отбор только тех записей, для которых вложенный запрос возвращает одно или более значений.

Например,

SELECT S_fio,S_birthday FROM Sotrudniki S1

WHERE EXISTS (SELECT S_fio,S_birthday

FROM Sotrudniki S2

WHERE (S1.S_birthday=S2.S_birthday)

AND (S1.S_code!=S2.S_code))

Получение списка сотрудников, которые имеют хотя бы одного сверстника.

Вложенный запрос относится к специальным объектам в программе 1С. К его функционалу прибегают, если требуется сформировать и выполнить специальные виды запросов к таблицам базы данных (БД) в программе. Для получения результата по запросу сначала необходимо корректное составление текста запроса, который будет аккумулировать актуальную информацию об источниках для получения данных. Это могут быть таблицы, специальные поля, уникальные группировки.

Современный язык запросов для программы 1С

Вложенные запросы пишутся с применением языка, который имеет свои особенности и в тоже время похож на другие специальные языки SQL, к примеру, по своему синтаксису. Главные отличительные черты языка запросов в 1С такие:

  • Разыменование ссылочных полей;
  • Возможности нетипичных запросов;
  • Упорядочивание по примитивным типам;
  • Использование конструкции «Представление»;
  • Выборка данных по шаблону;
  • Совместное использование группировок и итогов;
  • Конструкция с условием «Где».

Есть и другие отличительные особенности языка запросов в 1С, которые позволяют ему быть эффективным.

Что нужно знать о вложенных запросах и языке запросов?

Профессиональное изучение конструкции языка запроса позволяет не только понять, для чего предназначен язык запросов , но и научится представлять совокупность объектов базы в формате 2-х мерных таблиц, обрабатывать консоль запросов, создавать файлы для хранения списков запросов, познакомиться со специфическими полями таблиц, имеющих ссылочный тип. И всё это будут только начальные знания о системе вложенных запросов в 1С. Важно освоить и групповые операции, относящиеся к языку запросов, изучить, как делать корректную выборку по нескольким источникам данных.

Пример вложенного запроса

Рассмотрим один из вариантов вложенного запроса на основе объединения запросов. Предположим, есть документы по приходу и расходу, при этом одно и то же юридическое лицо выступает и как продавец, и как покупатель. Нам нужно узнать, общий долг по контрагенту. Для этого используем эффективную конструкцию «ОБЪЕДИНИТЬ ВСЕ».

Таблица «ПРИХОД»

Таблица «РАСХОД»

Сначала определяем все расходы, затем приход по юрлицам. Второй запрос ставим со знаком «-», это позволит корректно свернуть данные.

Результат действия:

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

Тогда результатом отчёта будет:

Когда речь идёт о таблицах Расхода и Прихода, к примеру, по справочнику Номенклатура, то необходимо исключить дублирование. Но сливать каждый из запросов отдельно не получится. Поэтому нужно сделать так:

В этом случае запрос помещён между скобками, его называют вложенным запросом. Это позволяет не только провести группировку актуальных для нас записей, но и исключить аналогичные элементы из двух используемых подзапросов.

Существуют определённые требования, которых необходимо придерживаться, выполняя объединение двух запросов. Прежде всего, это касается количества полей. Необходимо, чтобы их число было идентичным. К примеру, если бы в таблице данных «Расход» была указана Скидка, а в данных по Приходу такого элемента не было бы, то следует прибегнуть к следующей конструкции:

В дальнейшем нужно будет скорректировать величину суммы на скидку и выполнить необходимую группировку.

Вторым непременным условием является порядок. Дело в том, что процедура объединения по полям происходит в чётком соответствии с их порядком, то есть тем, как они обозначены в каждой последовательной секции. Для изменения порядка применим такую конструкцию:

Кстати, при применении конструкции Объединения следует отличать её от понятия соединения нескольких запросов. Объединение даёт возможность вертикального соединения результатов выборки, поочерёдно берутся данные из первого, потом уже переходят ко второму. Именно так получаются данные в результате конструкции объединения информации из двух таблиц.

Подзапрос - это запрос, содержащийся в выражении ключевого слова WHERE другого запроса с целью дополнительных ограничений на выводимые данные. Подзапросы называют также вложенными запросами. Их используют для наложения условий на выводимые данные. Подзапросы могут использоваться с операторами SELECT, INSERT, UPDATE или DELETE.

В некоторых случаях подзапрос можно использовать вместо связывания таблиц, тем самым связывая данные таблиц неявно. При использовании в запросе подзапроса сначала выполняется подзапрос, а только потом - содержащий его запрос, причем с учетом условий выполнения подзапроса. Подзапрос можно использовать либо в выражении ключевого слова WHERE, либо в выражении ключевого слова HAVING главного запроса. Логические операции и операции сравнения типа =, >, <, о, IN, NOT IN, AND, OR и т п. можно использовать в подзапросе. Все, что применимо к обычному запросу, применимо и к подзапросу.

При составлении подзапросов необходимо придерживаться следующих правил.

Подзапрос необходимо заключить в круглые скобки.

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

Ключевое слово ORDER BY использовать в подзапросе нельзя, хотя в главном запросе ORDER BY использоваться может. Вместо ORDER BY в подзапросе можно использовать GROUP BY.

Подзапрос, возвращающий несколько строк данных, можно использовать только в операторах, допускающих множество значений, например в IN.

Операцию BETWEEN по отношению к подзапросу использовать нельзя, но ее можно использовать в самом подзапросе. Базовый синтаксис оператора с подзапросом выглядит следующим образом.

SELECT имя_столбиа FROM таблица WHERE имя_столбца = (SELECT имя__столбца FROM таблица WHERE условия);

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

Пример. Пусть требуется определить количество предметов обучения с оценкой, превышающей среднее значение оценки студента с идентификатором 301:

SELECT COUNT (DISTINCT subj_id), mark FROM exam_marks GROUP BY mark HAVING mark > (SELECT AVG(mark) FROM exam_marks WHERE stud_id=301);

При использовании в операторе нескольких подзапросов увеличивается время, необходимое для обработки запроса, и повышается вероятность ошибок из-за усложнения оператора.

Связанные подзапросы допускаются во многих реализациях SQL. При использовании подзапросов во внутреннем запросе можно ссылаться на таблицу, имя которой указано в предложении FROM внешнего запроса. Такие подзапросы называются связанными. Связанный подзапрос выполняется по одному разу для каждой строки таблицы основного запроса, а именно:



Выбирается строка из таблицы, имя которой указано во внешнем запросе;

Выполняется подзапрос, и полученное в результате его выполнения значение применяется для анализа этой строки в условии WHERE внешнего запроса;

По результату оценки этого условия принимается решение о включении / невключении строки в состав выходных данных;

Процедура повторяется для следующей строки таблицы внешнего запроса.

Предложение GROUP BY позволяет группировать выводимые SELECT–запросом записи по значению некоторого поля. Использование предложения HAVING позволяет при выводе осуществлять фильтрацию таких групп. Предикат предложения HAVING оценивается не для каждой строки результата, а для каждой группы выходных записей, сформированной предложением GROUP BY внешнего запроса.

SELECT exam_date, SUM(mark) FROM exam_marks a GROUP BY exam_date

HAVING 10< (SELECT COUNT(mark) FROM exam_marks b

WHERE a.exam_date=b.exam_date);

Необходимо по данным таблицы exam_marks определить сумму полученных студентами оценок (значений поля mark), сгруппировав значения оценок по датам экзаменов и исключив те дни, когда число студентов, сдававших в течении дня экзамены, было меньше десяти.

Резюме: подзапрос представляет собой запрос, выполняемый в рамках другого запроса для задания дополнительных условий на выводимые данные. Подзапрос можно использовать в выражениях ключевых слов WHERE и HAVING. Синтаксис подзапросов практически не отличается от синтаксиса обычного запроса, имеются лишь небольшие ограничения. Одним из таких ограничений является запрет на использование в подзапросах ключевого слова ORDER BY, однако, вместо него можно использовать GROUP BY, чем достигается практически тот же эффект. Подзапросы используются для размещения в запросах условий, точные данные для которых не известны, тем самым расширяя возможности и гибкость SQL.

  • Перевод
  • Tutorial
Надо “ SELECT * WHERE a=b FROM c ” или “ SELECT WHERE a=b FROM c ON * ” ?

Если вы похожи на меня, то согласитесь: SQL - это одна из тех штук, которые на первый взгляд кажутся легкими (читается как будто по-английски!), но почему-то приходится гуглить каждый простой запрос, чтобы найти правильный синтаксис.


А потом начинаются джойны, агрегирование, подзапросы, и получается совсем белиберда. Вроде такой:


SELECT members.firstname || " " || members.lastname AS "Full Name" FROM borrowings INNER JOIN members ON members.memberid=borrowings.memberid INNER JOIN books ON books.bookid=borrowings.bookid WHERE borrowings.bookid IN (SELECT bookid FROM books WHERE stock>(SELECT avg(stock) FROM books)) GROUP BY members.firstname, members.lastname;

Буэ! Такое спугнет любого новичка, или даже разработчика среднего уровня, если он видит SQL впервые. Но не все так плохо.


Легко запомнить то, что интуитивно понятно, и с помощью этого руководства я надеюсь снизить порог входа в SQL для новичков, а уже опытным предложить по-новому взглянуть на SQL.


Не смотря на то, что синтаксис SQL почти не отличается в разных базах данных, в этой статье для запросов используется PostgreSQL. Некоторые примеры будут работать в MySQL и других базах.

1. Три волшебных слова

В SQL много ключевых слов, но SELECT , FROM и WHERE присутствуют практически в каждом запросе. Чуть позже вы поймете, что эти три слова представляют собой самые фундаментальные аспекты построения запросов к базе, а другие, более сложные запросы, являются всего лишь надстройками над ними.

2. Наша база

Давайте взглянем на базу данных, которую мы будем использовать в качестве примера в этой статье:







У нас есть книжная библиотека и люди. Также есть специальная таблица для учета выданных книг.

  • В таблице "books" хранится информация о заголовке, авторе, дате публикации и наличии книги. Все просто.
  • В таблице “members” - имена и фамилии всех записавшихся в библиотеку людей.
  • В таблице “borrowings” хранится информация о взятых из библиотеки книгах. Колонка bookid относится к идентификатору взятой книги в таблице “books”, а колонка memberid относится к соответствующему человеку из таблицы “members”. У нас также есть дата выдачи и дата, когда книгу нужно вернуть.

3. Простой запрос

Давайте начнем с простого запроса: нам нужны имена и идентификаторы (id) всех книг, написанных автором “Dan Brown”


Запрос будет таким:


SELECT bookid AS "id", title FROM books WHERE author="Dan Brown";

А результат таким:


id title
2 The Lost Symbol
4 Inferno

Довольно просто. Давайте разберем запрос чтобы понять, что происходит.

3.1 FROM - откуда берем данные

Сейчас это может показаться очевидным, но FROM будет очень важен позже, когда мы перейдем к соединениям и подзапросам.


FROM указывает на таблицу, по которой нужно делать запрос. Это может быть уже существующая таблица (как в примере выше), или таблица, создаваемая на лету через соединения или подзапросы.

3.2 WHERE - какие данные показываем

WHERE просто-напросто ведет себя как фильтр строк , которые мы хотим вывести. В нашем случае мы хотим видеть только те строки, где значение в колонке author - это “Dan Brown”.

3.3 SELECT - как показываем данные

Теперь, когда у нас есть все нужные нам колонки из нужной нам таблицы, нужно решить, как именно показывать эти данные. В нашем случае нужны только названия и идентификаторы книг, так что именно это мы и выберем с помощью SELECT . Заодно можно переименовать колонку используя AS .


Весь запрос можно визуализировать с помощью простой диаграммы:


4. Соединения (джойны)

Теперь мы хотим увидеть названия (не обязательно уникальные) всех книг Дэна Брауна, которые были взяты из библиотеки, и когда эти книги нужно вернуть:


SELECT books.title AS "Title", borrowings.returndate AS "Return Date" FROM borrowings JOIN books ON borrowings.bookid=books.bookid WHERE books.author="Dan Brown";

Результат:


Title Return Date
The Lost Symbol 2016-03-23 00:00:00
Inferno 2016-04-13 00:00:00
The Lost Symbol 2016-04-19 00:00:00

По большей части запрос похож на предыдущий за исключением секции FROM . Это означает, что мы запрашиваем данные из другой таблицы . Мы не обращаемся ни к таблице “books”, ни к таблице “borrowings”. Вместо этого мы обращаемся к новой таблице , которая создалась соединением этих двух таблиц.


borrowings JOIN books ON borrowings.bookid=books.bookid - это, считай, новая таблица, которая была сформирована комбинированием всех записей из таблиц "books" и "borrowings", в которых значения bookid совпадают. Результатом такого слияния будет:



А потом мы делаем запрос к этой таблице так же, как в примере выше. Это значит, что при соединении таблиц нужно заботиться только о том, как провести это соединение. А потом запрос становится таким же понятным, как в случае с «простым запросом» из пункта 3.


Давайте попробуем чуть более сложное соединение с двумя таблицами.


Теперь мы хотим получить имена и фамилии людей, которые взяли из библиотеки книги автора “Dan Brown”.


На этот раз давайте пойдем снизу вверх:


Шаг Step 1 - откуда берем данные? Чтобы получить нужный нам результат, нужно соединить таблицы “member” и “books” с таблицей “borrowings”. Секция JOIN будет выглядеть так:


borrowings JOIN books ON borrowings.bookid=books.bookid JOIN members ON members.memberid=borrowings.memberid

Результат соединения можно увидеть по ссылке .


Шаг 2 - какие данные показываем? Нас интересуют только те данные, где автор книги - “Dan Brown”


WHERE books.author="Dan Brown"

Шаг 3 - как показываем данные? Теперь, когда данные получены, нужно просто вывести имя и фамилию тех, кто взял книги:


SELECT members.firstname AS "First Name", members.lastname AS "Last Name"

Супер! Осталось лишь объединить три составные части и сделать нужный нам запрос:


SELECT members.firstname AS "First Name", members.lastname AS "Last Name" FROM borrowings JOIN books ON borrowings.bookid=books.bookid JOIN members ON members.memberid=borrowings.memberid WHERE books.author="Dan Brown";

Что даст нам:


First Name Last Name
Mike Willis
Ellen Horton
Ellen Horton

Отлично! Но имена повторяются (они не уникальны). Мы скоро это исправим.

5. Агрегирование

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


Давайте продолжим наш пример, в котором появляются повторяющиеся имена. Видно, что Ellen Horton взяла больше одной книги, но это не самый лучший способ показать эту информацию. Можно сделать другой запрос:


SELECT members.firstname AS "First Name", members.lastname AS "Last Name", count(*) AS "Number of books borrowed" FROM borrowings JOIN books ON borrowings.bookid=books.bookid JOIN members ON members.memberid=borrowings.memberid WHERE books.author="Dan Brown" GROUP BY members.firstname, members.lastname;

Что даст нам нужный результат:


First Name Last Name Number of books borrowed
Mike Willis 1
Ellen Horton 2

Почти все агрегации идут вместе с выражением GROUP BY . Эта штука превращает таблицу, которую можно было бы получить запросом, в группы таблиц. Каждая группа соответствует уникальному значению (или группе значений) колонки, которую мы указали в GROUP BY . В нашем примере мы конвертируем результат из прошлого упражнения в группу строк. Мы также проводим агрегирование с count , которая конвертирует несколько строк в целое значение (в нашем случае это количество строк). Потом это значение приписывается каждой группе.


Каждая строка в результате представляет собой результат агрегирования каждой группы.



Можно прийти к логическому выводу, что все поля в результате должны быть или указаны в GROUP BY , или по ним должно производиться агрегирование. Потому что все другие поля могут отличаться друг от друга в разных строках, и если выбирать их SELECT "ом, то непонятно, какие из возможных значений нужно брать.


В примере выше функция count обрабатывала все строки (так как мы считали количество строк). Другие функции вроде sum или max обрабатывают только указанные строки. Например, если мы хотим узнать количество книг, написанных каждым автором, то нужен такой запрос:


SELECT author, sum(stock) FROM books GROUP BY author;

Результат:


author sum
Robin Sharma 4
Dan Brown 6
John Green 3
Amish Tripathi 2

Здесь функция sum обрабатывает только колонку stock и считает сумму всех значений в каждой группе.

6. Подзапросы


Подзапросы это обычные SQL-запросы, встроенные в более крупные запросы. Они делятся на три вида по типу возвращаемого результата.

6.1 Двумерная таблица

Есть запросы, которые возвращают несколько колонок. Хороший пример это запрос из прошлого упражнения по агрегированию. Будучи подзапросом, он просто вернет еще одну таблицу, по которой можно делать новые запросы. Продолжая предыдущее упражнение, если мы хотим узнать количество книг, написанных автором “Robin Sharma”, то один из возможных способов - использовать подзапросы:


SELECT * FROM (SELECT author, sum(stock) FROM books GROUP BY author) AS results WHERE author="Robin Sharma";

Результат:



Можно записать как: ["Robin Sharma", "Dan Brown"]


2. Теперь используем этот результат в новом запросе:


SELECT title, bookid FROM books WHERE author IN (SELECT author FROM (SELECT author, sum(stock) FROM books GROUP BY author) AS results WHERE sum > 3);

Результат:


title bookid
The Lost Symbol 2
Who Will Cry When You Die? 3
Inferno 4

Это то же самое, что:


SELECT title, bookid FROM books WHERE author IN ("Robin Sharma", "Dan Brown");

6.3 Отдельные значения

Бывают запросы, результатом которых являются всего одна строка и одна колонка. К ним можно относиться как к константным значениям, и их можно использовать везде, где используются значения, например, в операторах сравнения. Их также можно использовать в качестве двумерных таблиц или массивов, состоящих из одного элемента.


Давайте, к примеру, получим информацию о всех книгах, количество которых в библиотеке превышает среднее значение в данный момент.


Среднее количество можно получить таким образом:


select avg(stock) from books;

Что дает нам:


7. Операции записи

Большинство операций записи в базе данных довольно просты, если сравнивать с более сложными операциями чтения.

7.1 Update

Синтаксис запроса UPDATE семантически совпадает с запросом на чтение. Единственное отличие в том, что вместо выбора колонок SELECT "ом, мы задаем знаения SET "ом.


Если все книги Дэна Брауна потерялись, то нужно обнулить значение количества. Запрос для этого будет таким:


UPDATE books SET stock=0 WHERE author="Dan Brown";

WHERE делает то же самое, что раньше: выбирает строки. Вместо SELECT , который использовался при чтении, мы теперь используем SET . Однако, теперь нужно указать не только имя колонки, но и новое значение для этой колонки в выбранных строках.


7.2 Delete

Запрос DELETE это просто запрос SELECT или UPDATE без названий колонок. Серьезно. Как и в случае с SELECT и UPDATE , блок WHERE остается таким же: он выбирает строки, которые нужно удалить. Операция удаления уничтожает всю строку, так что не имеет смысла указывать отдельные колонки. Так что, если мы решим не обнулять количество книг Дэна Брауна, а вообще удалить все записи, то можно сделать такой запрос:


DELETE FROM books WHERE author="Dan Brown";

7.3 Insert

Пожалуй, единственное, что отличается от других типов запросов, это INSERT . Формат такой:


INSERT INTO x (a,b,c) VALUES (x, y, z);

Где a , b , c это названия колонок, а x , y и z это значения, которые нужно вставить в эти колонки, в том же порядке. Вот, в принципе, и все.


Взглянем на конкретный пример. Вот запрос с INSERT , который заполняет всю таблицу "books":


INSERT INTO books (bookid,title,author,published,stock) VALUES (1,"Scion of Ikshvaku","Amish Tripathi","06-22-2015",2), (2,"The Lost Symbol","Dan Brown","07-22-2010",3), (3,"Who Will Cry When You Die?","Robin Sharma","06-15-2006",4), (4,"Inferno","Dan Brown","05-05-2014",3), (5,"The Fault in our Stars","John Green","01-03-2015",3);

8. Проверка

Мы подошли к концу, предлагаю небольшой тест. Посмотрите на тот запрос в самом начале статьи. Можете разобраться в нем? Попробуйте разбить его на секции SELECT , FROM , WHERE , GROUP BY , и рассмотреть отдельные компоненты подзапросов.


Вот он в более удобном для чтения виде:


SELECT members.firstname || " " || members.lastname AS "Full Name" FROM borrowings INNER JOIN members ON members.memberid=borrowings.memberid INNER JOIN books ON books.bookid=borrowings.bookid WHERE borrowings.bookid IN (SELECT bookid FROM books WHERE stock> (SELECT avg(stock) FROM books)) GROUP BY members.firstname, members.lastname;

Этот запрос выводит список людей, которые взяли из библиотеки книгу, у которой общее количество выше среднего значения.


Результат:


Full Name
Lida Tyler

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

Теги: Добавить метки