Лучше ли разделить большой запрос на несколько меньших?


13

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

Мой вопрос заключается в том, следует ли нам рассмотреть возможность использования нескольких небольших запросов и перенести логические операции на уровень приложений, выполняя запросы к БД более чем за один вызов, или лучше иметь их все за один раз?
Например, рассмотрим следующий запрос:

SELECT *
FROM   `users`
WHERE  `user_id` IN (SELECT f2.`friend_user_id`
                     FROM   `friends` AS f1
                            INNER JOIN `friends` AS f2
                              ON f1.`friend_user_id` = f2.`user_id`
                     WHERE  f2.`is_page` = 0
                            AND f1.`user_id` = "%1$d"
                            AND f2.`friend_user_id` != "%1$d"
                            AND f2.`friend_user_id` NOT IN (SELECT `friend_user_id`
                                                            FROM   `friends`
                                                            WHERE  `user_id` = "%1$d"))
       AND `user_id` NOT IN (SELECT `user_id`
                             FROM   `friend_requests`
                             WHERE  `friend_user_id` = "%1$d")
       AND `user_image` IS NOT NULL
ORDER  BY RAND() 
LIMIT %2$d

Какой лучший способ сделать это?

Ответы:


14

Я собираюсь не согласиться на большие и сложные запросы с датагодом здесь. Я вижу это только как проблемы, если они дезорганизованы. С точки зрения производительности это почти всегда лучше, потому что у планировщика гораздо больше свободы в том, как искать информацию. Тем не менее, большие запросы должны быть написаны с учетом удобства обслуживания. В общем, я обнаружил, что этот простой, хорошо структурированный SQL легко отлаживать, даже если один запрос выполняется для 200+ строк. Это потому, что обычно у вас есть довольно хорошее представление о том, с какими проблемами вы сталкиваетесь, поэтому в запросе есть только несколько областей, которые вы должны проверить.

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

Длинные сложные запросы работают довольно хорошо как с точки зрения удобства сопровождения, так и с точки зрения производительности, где вы сохраняете простые предложения where и когда вы делаете как можно больше с объединениями вместо подвыборов. Цель состоит в том, чтобы сделать так, чтобы «записи не появлялись», чтобы вы получили несколько очень специфических мест в запросе для проверки (он отбрасывается в объединении или отфильтровывается в предложении where?), И поэтому группа обслуживания может реально поддерживать вещи.

Что касается масштабируемости, имейте в виду, что чем больше у планировщика гибкости, это тоже хорошо ...

Изменить: Вы упоминаете, что это MySQL, так что представления вряд ли будут работать так хорошо, и CTE не может быть и речи. Кроме того, приведенный пример не является особенно длинным или сложным, так что это не проблема.


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

8

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

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


2
недостаток строки простых запросов заключается в том, что состояние между ними значительно изменяется, что делает общую отладку приложения более сложной. Т.е. вы можете отлаживать большие запросы SQL часто в виде деревьев, но код приложения получает отлаженную инструкцию, проверяя, как состояние изменяется в инструкциях. Реальные проблемы связаны с тем фактом, что подвыборы и встроенные представления также являются их собственными деревьями ...
Крис Траверс,

В моем случае единственным, кто должен управлять БД и кодом, является я сам. И в основном мой вопрос касался производительности запроса.
Хамед Момени

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

5

Мои 2 цента на запросе 2 ключевых слов производительности и масштабируемости:

Производительность запросов. Параллелизм SQL Server уже делает очень хорошую работу, разбивая запросы на многопоточные поиски, поэтому я не уверен, какое улучшение производительности запросов вы увидите, выполнив его для SQL Server. Вам нужно будет взглянуть на план выполнения, чтобы увидеть степень параллелизма, которую вы получите при его выполнении, и сравнить результаты в обоих направлениях. Если вам в конечном итоге придется использовать подсказку для запроса, чтобы получить такую ​​же или лучшую производительность, то IMO того не стоит, так как подсказка для запроса может оказаться неоптимальной позже.

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


2
RE: ссылки на «SQL Server», хотя OP не указал ни одной конкретной СУБД, я подозреваю, что они находятся на MySQL с обратной галочки иLIMIT
Martin Smith,

@MartinSmith Вы подозреваете, правильно. Это MySQL.
Хамед Момени

2

Иногда нет другого выбора, кроме как разбить большой / сложный запрос на маленькие. Лучший способ определить это - использовать EXPLAINоператор с SELECTоператором. Число трассировок / сканирований, которые ваша база данных собирается выполнить для получения ваших данных, является произведением значений «строк», возвращаемых вашим EXPLAINзапросом. В нашем случае у нас был запрос, объединяющий 10 таблиц. Для конкретной записи, след составил 409 млн., Что привело к ведению блога нашей БД и увеличило загрузку ЦП нашего сервера БД более чем на 300%. Мы смогли получить ту же информацию, разделив запросы намного быстрее.

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

Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.