Mysql query несколько запросов в одном. Оптимизация MySQL запросов

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

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

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

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

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

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

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

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

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

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

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

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

    написать

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

Я уже писал о самых различных SQL-запросах , но пришло время поговорить и о более сложных вещах, например, SQL-запрос на выборку записей из нескольких таблиц .

Когда мы с Вами делали выборку из одной таблицы, то всё было очень просто:

SELECT названия_нужных_полей FROM название_таблицы WHERE условие_выборки

Всё очень просто и тривиально, но при выборке сразу из нескольких таблиц становится всё несколько сложнее. Одна из трудностей - это совпадение имён полей. Например, в каждой таблице есть поле id .

Давайте рассмотрим такой запрос:

SELECT * FROM table_1, table_2 WHERE table_1.id > table_2.user_id

Многим, кто не занимался подобными запросами, покажется, что всё очень просто, подумав, что здесь добавились только названия таблиц перед названиями полей. Фактически, это позволяет избежать противоречий между одинаковыми именами полей. Однако, сложность не в этом, а в алгоритме работы подобного SQL-запроса .

Алгоритм работы следующий: берётся первая запись из table_1 . Берётся id этой записи из table_1 . Дальше полностью смотрится таблица table_2 . И добавляются все записи, где значение поля user_id меньше id выбранной записи в table_1 . Таким образом, после первой итерации может появиться от 0 до бесконечного количества результирующих записей. На следующей итерации берётся следующая запись таблицы table_1 . Снова просматривается вся таблица table_2 , и вновь срабатывает условие выборки table_1.id > table_2.user_id . Все записи, удовлетворившие этому условию, добавляются в результат. На выходе может получиться огромное количество записей, во много раз превышающих суммарный размер обеих таблиц.

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

Предыдущий SQL-запрос , как таковой, редко используется. Он был просто дан для объяснения алгоритма выборки из нескольких таблиц . А теперь же разберём более приземистый SQL-запрос . Допустим, у нас есть две таблицы: с товарами (есть поле owner_id , отвечающего за id владельца товара) и с пользователями (есть поле id ). Мы хотим одним SQL-запросом получить все записи, причём чтобы в каждой была информация о пользователе и его одном товаре. В следующей записи была информация о том же пользователе и следущем его товаре. Когда товары этого пользователя кончатся, то переходить к следующему пользователю. Таким образом, мы должны соединить две таблицы и получить результат, в котором каждая запись содержит информацию о пользователе и об одном его товаре .

Подобный запрос заменит 2 SQL-запроса : на выборку отдельно из таблицы с товарами и из таблицы с пользователями. Вдобавок, такой запрос сразу поставит в соответствие пользователя и его товар.

Сам же запрос очень простой (если Вы поняли предыдущий):

SELECT * FROM users, products WHERE users.id = products.owner_id

Алгоритм здесь уже несложный: берётся первая запись из таблицы users . Далее берётся её id и анализируются все записи из таблицы products , добавляя в результат те, у которых owner_id равен id из таблицы users . Таким образом, на первой итерации собираются все товары у первого пользователя. На второй итерации собираются все товары у второго пользователя и так далее.

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

В данной небольшой статье речь пойдет о базах данных в частности MySQL, выборке и подсчете. Работая с базами данных, часто требуется совершить подсчет количества строк COUNT() с определенным условием или без, это сделать крайне просто следующим запросом

Просмотр кода MYSQL

Запрос вернет значение, с количеством строк в таблице.

Подсчет с условием

Просмотр кода MYSQL

Запрос вернет значение, с количеством строк в таблице удовлетворяющих данному условию: var = 1

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

Просмотр кода MYSQL

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

Просмотр кода MYSQL

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

Просмотр кода TEXT

c1|c2|c3 -------- 1 |5 |8

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

Следующий пример запроса, содержащего несколько COUNT в одном запросе MySQL, построен несколько иначе, в нем используются конструкции IF(условие, значение1, значение2), а также суммирование SUM(). Позволяющие произвести отбор данных по заданным критериям в рамках одного запроса, затем суммировать их, и вывести несколько значений в качестве результата.

Просмотр кода MYSQL

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

Просмотр кода TEXT

total|c1|c2|c3 -------------- 14 |1 |5 |8

Далее я приведу сравнительную статистику скорости выполнения трех вариантов запросов, для выборки нескольких COUNT(). Для тестирования скорости выполнения запросов, было выполнено по 1000 запросов каждого типа, с таблицей содержащей более трех тысяч записей. При этом каждый раз запрос содержал SQL_NO_CACHE для отключение кеширования результатов базой данных.

Скорость выполнения
Три отдельных запроса: 0.9 сек
Один запрос с подзапросами: 0.95 сек
Один запрос с конструкцией IF и SUM: 1.5 сек

Вывод. И так, мы имеет несколько вариантов построения запросов к базе данных MySQL с несколькими COUNT(), первый вариант с отдельными запросами не очень удобен, но имеет наилучший результат по скорости. Второй вариант с подзапросами несколько удобнее, но при этом скорость его выполнение немного ниже. И наконец третий лаконичный вариант запроса с конструкциями IF и SUM, кажущийся самым удобным, имеет самую низкую скорость выполнения, которая почти в два раза ниже первых двух вариантов. По этому, при задаче оптимизации работы БД, я рекомендую использовать второй вариант запроса содержащий подзапросы с COUNT(), во первых его скорость выполнения близка к самому быстрому результату, во вторых такая организация внутри одного запроса достаточно удобна.