Объединение запросов против нескольких запросов

являются ли запросы JOIN быстрее, чем несколько запросов? (Вы запускаете свой основной запрос, а затем запускаете много других вариантов выбора на основе результатов вашего основного запроса)

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

Если они быстрее, может ли кто-нибудь приблизиться очень грубо на сколько? Если это 1.5 x, мне все равно, но если это 10x, я думаю, что да.

13 ответов


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


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

  1. одиночный запрос с 5 соединениями

    запрос: 8.074508 секунд

    размер результата: 2268000

  2. 5 запросов подряд

    общее время запроса: 0.00262 секунд

    размер результату: 165 (6 + 50 + 7 + 12 + 90)

.

обратите внимание, что мы получаем одинаковые результаты в обоих случаях (6 х 50 х 7 х 12 х 90 = 2268000)

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

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

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

Фрэнк


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

Я могу понять, если один способ запроса займет 0,02 секунды, а другой за 20 секунд, это огромная разница. Но что, если один способ запроса занимает 0,0000000002 секунды, а другой-0,0000002 секунды ? В обоих случаях один способ является колоссальным 1000 раз быстрее, чем другой, но это действительно все еще "колоссальный" во втором случае ?

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


сделал быстрый тест, выбрав одну строку из таблицы строк 50,000 и присоединившись к одной строке из таблицы строк 100,000. В основном выглядело так:

$id = mt_rand(1, 50000);
$row = $db->fetchOne("SELECT * FROM table1 WHERE id = " . $id);
$row = $db->fetchOne("SELECT * FROM table2 WHERE other_id = " . $row['other_id']);

vs

$id = mt_rand(1, 50000);
$db->fetchOne("SELECT table1.*, table2.*
    FROM table1
    LEFT JOIN table1.other_id = table2.other_id
    WHERE table1.id = " . $id);

два метода select заняли 3,7 секунды для 50,000 чтения, тогда как соединение заняло 2,0 секунды на моем домашнем медленном компьютере. Внутреннее соединение и левое соединение не имели значения. Выборка нескольких строк (например, использование в наборе) дала аналогичные результаты.


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

тогда еще лучше-добавьте "объяснить" в начало каждого запроса. Это покажет вам, сколько вложенных запросов MySQL использует для ответа на ваш запрос данных и сколько строк сканируется для каждого запроса.


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

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

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

спасибо,

BLT


вопрос: у этих записей есть один-к-одному отношения или один-ко-многим отношения?

TLDR ответ:

если один-на-один, использовать JOIN заявление.

если один ко многим, используйте один (или много) SELECT операторы с оптимизацией кода на стороне сервера.

почему и как использовать SELECT для оптимизации

SELECT ' ing (с несколькими запросами вместо соединений) на большой группе записей на основе отношения "один ко многим" создает оптимальную эффективность, как JOIN ' ing имеет проблему экспоненциальной утечки памяти. Возьмите все данные, а затем используйте серверный язык сценариев, чтобы отсортировать его:

SELECT * FROM Address WHERE Personid IN(1,2,3);

результаты:

Address.id : 1            // First person and their address
Address.Personid : 1
Address.City : "Boston"

Address.id : 2            // First person's second address
Address.Personid : 1
Address.City : "New York"

Address.id : 3            // Second person's address
Address.Personid : 2
Address.City : "Barcelona"

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

<?php
    foreach($addresses as $address) {
         $persons[$address['Personid']]->Address[] = $address;
    }
?>

когда не использовать JOIN для оптимизации

JOIN'ing большая группа записей на основе отношения один к одному с одной записью дает оптимальную эффективность по сравнению с несколькими SELECT операторы, один за другим, которые просто получают следующий тип записи.

но JOIN неэффективен при получение записей с отношениями "один ко многим".

пример: в базе данных блогов есть 3 таблицы интересов, Blogpost, Tag и Comment.

SELECT * from BlogPost
LEFT JOIN Tag ON Tag.BlogPostid = BlogPost.id
LEFT JOIN Comment ON Comment.BlogPostid = BlogPost.id;

если есть 1 blogpost, 2 теги и 2 комментария, вы получите такие результаты, как:

Row1: tag1, comment1,
Row2: tag1, comment2,
Row3: tag2, comment1,
Row4: tag2, comment2,

обратите внимание, как каждая запись дублируется. Итак, 2 комментария и 2 метки - это 4 строки. Что если у нас есть 4 комментария и 4 теги? Вы не получаете 8 строк - вы получаете 16 строк:

Row1: tag1, comment1,
Row2: tag1, comment2,
Row3: tag1, comment3,
Row4: tag1, comment4,
Row5: tag2, comment1,
Row6: tag2, comment2,
Row7: tag2, comment3,
Row8: tag2, comment4,
Row9: tag3, comment1,
Row10: tag3, comment2,
Row11: tag3, comment3,
Row12: tag3, comment4,
Row13: tag4, comment1,
Row14: tag4, comment2,
Row15: tag4, comment3,
Row16: tag4, comment4,

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

сколько стоят эти дубликаты? Память (в SQL server и коде, который пытается удалить дубликаты) и сетевые ресурсы (между SQL server и сервером кода).

источник:https://dev.mysql.com/doc/refman/8.0/en/nested-join-optimization.html ; https://dev.mysql.com/doc/workbench/en/wb-relationship-tools.html


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

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

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

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

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


Это будет быстрее в плане производительности? Возможно. Но он также потенциально блокирует больше объектов базы данных одновременно (в зависимости от вашей базы данных и вашей схемы) и тем самым уменьшает параллелизм. По моему опыту, люди часто вводят в заблуждение аргументом "меньше поездок по базе данных", когда на самом деле в большинстве OLTP-систем, где база данных находится в одной локальной сети, реальным узким местом редко является сеть.


вот ссылка с 100 полезными запросами, они тестируются в базе данных Oracle, но помните, что SQL является стандартом, чем отличаются Oracle, MS SQL Server, MySQL и другие базы данных-это диалект SQL:

http://javaforlearn.com/100-sql-queries-learn/


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

реальный вопрос заключается в том, как вы хотите получить доступ к данным. Single выбирает поддержку поздней привязки. Например, если вам нужна только информация о сотрудниках, вы можете выбрать из таблицы сотрудники. Иностранец ключевые связи могут использоваться для получения связанных ресурсов позднее и по мере необходимости. У выбора уже будет ключ, чтобы указать, поэтому они должны быть очень быстрыми, и вам нужно только получить то, что вам нужно. Латентность сети всегда должна приниматься во внимание.

соединения будут извлекать все данные сразу. Если вы создаете отчет или заполнение таблицы, это может быть именно то, что вы хотите. Скомпилировать и оптимизировать соединения просто будет быстрее, чем один выбор в этом случае. Помните, что Ad-hoc соединения могут быть не такими быстрыми-вы должны скомпилировать их (в сохраненный proc). Ответ скорости зависит от плана выполнения, в котором подробно описано, какие шаги предпринимает СУБД для извлечения данных.


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

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

это часто вызвано отношениями "многие-ко-одному-многим". Например, HoldOffHunger это!--15--> упомянул один запрос для сообщений, тегов и комментариев. Комментарии связаны с сообщением, как и бирки...но теги не связаны с комментариями.

+------------+     +---------+     +---------+
|  comment   |     |   post  |     |  tag    |
|------------|*   1|---------|1   *|---------|
| post_id    |-----| post_id |-----| post_id |
| comment_id |     | ...     |     | tag_id  |
| user_id    |     |         |     | ...     |
| ...        |     |         |     | ...     |
+------------+     +---------+     +---------+

в этом случае однозначно лучше, чтобы это были как минимум два отдельных запроса. Если вы попытаетесь объединить теги и комментарии, потому что между ними нет прямой связи, вы получите все возможные комбинации тегов и комментариев. many * many == manymany. Кроме того, поскольку сообщения и теги не связаны, вы можете выполнять эти два запроса параллельно, что приведет к потенциальной выгоде.

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

 +----------+     +------------+     +---------+
 |   user   |     |  comment   |     |   post  |
 |----------|1   *|------------|*   1|---------|
 | user_id  |-----| post_id    |-----| post_id |
 | username |     | user_id    |     | ...     |
 | ...      |     | ...        |     +---------+
 +----------+     +------------+

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


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

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