Руководство по SQL. Вложенные запросы.

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

  • Вложенный запрос должен быть заключён в родительский запрос.
  • Вложенный запрос может содержать только одну колонку в операторе SELECT.
  • Оператор ORDER BY не может быть использован во вложенном запросе. Для обеспечения функционала ORDER BY, во вложенном запросе может быть использован GROUP BY.
  • Вложенные запросы, возвращающие более одной записи могут использоваться с операторами нескольких значений, как оператор IN.
  • Вложенный запрос не может заканчиваться в функции.
  • SELECT не может включать никаких ссылок на значения BLOB, ARRAY, CLOB и NCLOB.
  • Оператор BETWEEN не может быть использован вместе с вложенным запросом.

Примеры:

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


SELECT имя_колонки [, имя_колонки2 ]
FROM   таблица1 [, таблица2 ]
WHERE  имя_колонки ОПЕРАТОР
      (SELECT имя_колонки [, имя_колонки2 ]
      FROM таблица1 [, таблица2 ]
      [WHERE])

Предположим, что у нас есть таблица developers, которая содержит следующие записи:


+----+-------------------+-----------+------------+--------+
| ID | NAME              | SPECIALTY | EXPERIENCE | SALARY |
+----+-------------------+-----------+------------+--------+
|  1 | Eugene Suleimanov | Java      |          2 |   2500 |
|  2 | Peter Romanenko   | Java      |          3 |   3500 |
|  3 | Andrei Komarov    | C++       |          3 |   2500 |
|  4 | Konstantin Geiko  | C#        |          2 |   2000 |
|  5 | Asya Suleimanova  | UI/UX     |          2 |   1800 |
|  6 | Ludmila Geiko     | UI/UX     |          2 |   1800 |
|  7 | Ivan Ivanov       | C#        |          1 |    900 |
+----+-------------------+-----------+------------+--------+

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


SELECT  * FROM developers 
          WHERE ID IN (SELECT ID 
                       FROM developers 
                       WHERE SALARY > 2000);


Предположим, что у нас есть клон таблицы developers, который имеет имя developers_clone и имеет следующую структуру:


+------------+--------------+------+-----+---------+-------+
| Field      | Type         | Null | Key | Default | Extra |
+------------+--------------+------+-----+---------+-------+
| ID         | int(11)      | NO   | PRI | NULL    |       |
| NAME       | varchar(100) | NO   |     | NULL    |       |
| SPECIALTY  | varchar(100) | YES  |     | NULL    |       |
| EXPERIENCE | int(11)      | NO   |     | NULL    |       |
| SALARY     | int(11)      | YES  |     | NULL    |       |
+------------+--------------+------+-----+---------+-------+

И не содержит данных:


mysql> SELECT * FROM developers_clone;
Empty set (0.00 sec)

Теперь попробуем выполнить для этой же таблицы следующий запрос:


mysql> INSERT INTO developers_clone 
                   SELECT * FROM developers 
                   WHERE ID IN (SELECT ID 
                                FROM developers);

В результате выполнения данного запроса таблица developers_clone будет содержать следующие данные:


+----+-------------------+-----------+------------+--------+
| ID | NAME              | SPECIALTY | EXPERIENCE | SALARY |
+----+-------------------+-----------+------------+--------+
|  1 | Eugene Suleimanov | Java      |          2 |   2500 |
|  2 | Peter Romanenko   | Java      |          3 |   3500 |
|  3 | Andrei Komarov    | C++       |          3 |   2500 |
|  4 | Konstantin Geiko  | C#        |          2 |   2000 |
|  5 | Asya Suleimanova  | UI/UX     |          2 |   1800 |
|  6 | Ludmila Geiko     | UI/UX     |          2 |   1800 |
|  7 | Ivan Ivanov       | C#        |          1 |    900 |
+----+-------------------+-----------+------------+--------+

Другими словами, мы скопировали все данные из таблицы developers в таблицу developers_clone.

Теперь мы изменим данные в таблице developers воспользовавшись данными из таблицы developers_clone с помощью следующего запроса:


mysql> UPDATE developers 
       SET SALARY = SALARY * 1.25 
       WHERE EXPERIENCE IN (SELECT EXPERIENCE 
                            FROM developers_clone 
                            WHERE EXPERIENCE >=2);

В результате этого наша таблица содержащая изначальные данные:


+----+-------------------+-----------+------------+--------+
| ID | NAME              | SPECIALTY | EXPERIENCE | SALARY |
+----+-------------------+-----------+------------+--------+
|  1 | Eugene Suleimanov | Java      |          2 |   2500 |
|  2 | Peter Romanenko   | Java      |          3 |   3500 |
|  3 | Andrei Komarov    | C++       |          3 |   2500 |
|  4 | Konstantin Geiko  | C#        |          2 |   2000 |
|  5 | Asya Suleimanova  | UI/UX     |          2 |   1800 |
|  6 | Ludmila Geiko     | UI/UX     |          2 |   1800 |
|  7 | Ivan Ivanov       | C#        |          1 |    900 |
+----+-------------------+-----------+------------+--------+

Будет хранить следующие данные:


+----+-------------------+-----------+------------+--------+
| ID | NAME              | SPECIALTY | EXPERIENCE | SALARY |
+----+-------------------+-----------+------------+--------+
|  1 | Eugene Suleimanov | Java      |          2 |   3125 |
|  2 | Peter Romanenko   | Java      |          3 |   4375 |
|  3 | Andrei Komarov    | C++       |          3 |   3125 |
|  4 | Konstantin Geiko  | C#        |          2 |   2500 |
|  5 | Asya Suleimanova  | UI/UX     |          2 |   2250 |
|  6 | Ludmila Geiko     | UI/UX     |          2 |   2250 |
|  7 | Ivan Ivanov       | C#        |          1 |    900 |
+----+-------------------+-----------+------------+--------+

И наконец, попробуем выполнить удаление данных из таблицы с помощью вложенного запроса:


mysql> DELETE FROM developers 
       WHERE  EXPERIENCE IN (SELECT EXPERIENCE FROM developers_clone 
                             WHERE EXPERIENCE >= 2);


В результате таблица developers содерит следующие записи:


+----+-------------+-----------+------------+--------+
| ID | NAME        | SPECIALTY | EXPERIENCE | SALARY |
+----+-------------+-----------+------------+--------+
|  7 | Ivan Ivanov | C#        |          1 |    900 |
+----+-------------+-----------+------------+--------+

Очистим таблицу developers:


mysql> TRUNCATE developers;

Таблица пуста:


mysql> SELECT * FROM developers;
Empty set (0.00 sec)

Теперь восстановим данные таблицы developers, с помощью резервной таблицы developers_clone используя следующий запрос:


mysql> INSERT INTO developers 
       SELECT * FROM developers_clone 
       WHERE ID IN (SELECT ID 
                    FROM developers_clone);

Наша таблица developers имеет исходный вид:


mysql> SELECT * FROM developers;
+----+-------------------+-----------+------------+--------+
| ID | NAME              | SPECIALTY | EXPERIENCE | SALARY |
+----+-------------------+-----------+------------+--------+
|  1 | Eugene Suleimanov | Java      |          2 |   2500 |
|  2 | Peter Romanenko   | Java      |          3 |   3500 |
|  3 | Andrei Komarov    | C++       |          3 |   2500 |
|  4 | Konstantin Geiko  | C#        |          2 |   2000 |
|  5 | Asya Suleimanova  | UI/UX     |          2 |   1800 |
|  6 | Ludmila Geiko     | UI/UX     |          2 |   1800 |
|  7 | Ivan Ivanov       | C#        |          1 |    900 |
+----+-------------------+-----------+------------+--------+
7 rows in set (0.00 sec)

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