Присоединиться против подзапроса

Я старый пользователь MySQL и всегда предпочитал JOIN над подзапросом. Но в настоящее время каждый использует подзапрос, и я ненавижу его; Я не знаю почему.

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

20 ответов

Решение

Взято из руководства MySQL ( 13.2.10.11 Перезапись подзапросов как объединений):

LEFT [OUTER] JOIN может быть быстрее, чем эквивалентный подзапрос, потому что сервер может быть в состоянии оптимизировать его лучше - факт, который не относится только к MySQL Server.

Таким образом, подзапросы могут быть медленнее, чем LEFT [OUTER] JOINS, но, на мой взгляд, их сила немного выше читаемость.

Подзапросы - это логически правильный способ решения задач в форме "Получить факты из A, при условии наличия фактов из B". В таких случаях логичнее вставлять B в подзапрос, чем объединение. Это также более безопасно в практическом смысле, так как вам не нужно быть осторожным в получении дублированных фактов от A из-за нескольких матчей против B.

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

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

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

В JOINСУБД может создать план выполнения, который лучше подходит для вашего запроса и может предсказать, какие данные должны быть загружены для обработки и сэкономить время, в отличие от подзапроса, где он будет выполнять все запросы и загружать все их данные для выполнения обработки.

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

В 2010 году я присоединился бы к автору этого вопроса и решительно проголосовал бы за JOIN, Но с гораздо большим опытом (особенно в MySQL) я могу сказать: да, подзапросы могут быть лучше. Я прочитал несколько ответов здесь. Некоторые заявили, что подзапросы выполняются быстрее, но им не хватает хорошего объяснения. Я надеюсь, что смогу дать один (очень) поздний ответ:

Прежде всего, позвольте мне сказать самое важное: существуют разные формы подзапросов.

И второе важное утверждение: размер имеет значение

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

Подзапросы в полях выбора

SELECT moo, (SELECT roger FROM wilco WHERE moo = me) AS bar FROM foo

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

Подзапросы в выражении Where

SELECT moo FROM foo WHERE bar = (SELECT roger FROM wilco WHERE moo = me)

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

Подзапросы в сообщении Join

SELECT moo, bar 
  FROM foo 
    LEFT JOIN (
      SELECT MIN(bar), me FROM wilco GROUP BY me
    ) ON moo = me

Это интересно. Мы объединяем JOIN с подзапросом. И здесь мы получаем реальную силу подзапросов. Представьте себе набор данных с миллионами строк в wilco но только несколько отличных me, Вместо того, чтобы объединяться с огромным столом, теперь у нас есть меньшая временная таблица, к которой можно присоединиться. Это может привести к гораздо более быстрым запросам, в зависимости от размера базы данных. Вы можете иметь тот же эффект с CREATE TEMPORARY TABLE ... а также INSERT INTO ... SELECT ..., что может обеспечить лучшую читаемость по очень сложным запросам (но может заблокировать наборы данных на повторяющемся уровне изоляции чтения).

Вложенные подзапросы

SELECT moo, bar
  FROM (
    SELECT moo, CONCAT(roger, wilco) AS bar
      FROM foo
      GROUP BY moo
      HAVING bar LIKE 'SpaceQ%'
  ) AS temp_foo
  GROUP BY bar
  ORDER BY bar

Вы можете вкладывать подзапросы на нескольких уровнях. Это может помочь в огромных наборах данных, если вам нужно сгруппировать или отсортировать результаты. Обычно DB-Server создает временную таблицу для этого, но иногда вам не нужно сортировать по всей таблице, только по набору результатов. Это может обеспечить гораздо лучшую производительность в зависимости от размера таблицы.

Заключение

Подзапросы не являются заменой JOIN и вы не должны использовать их как это (хотя это возможно). По моему скромному мнению, правильное использование подзапроса - это использование в качестве быстрой замены CREATE TEMPORARY TABLE ..., Хороший подзапрос сокращает набор данных таким образом, который вы не можете выполнить за ON заявление о JOIN, Если подзапрос имеет одно из ключевых слов GROUP BY или же DISTINCT и предпочтительно не находится в полях выбора или операторе where, тогда это может значительно улучшить производительность.

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

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

Прежде всего, чтобы сравнить два первых, вы должны различать запросы с подзапросами:

  1. класс подзапросов, у которых всегда есть соответствующий эквивалентный запрос, написанный с объединениями
  2. класс подзапросов, которые нельзя переписать с помощью объединений

Для первого класса запросов хорошая СУБД будет рассматривать соединения и подзапросы как эквивалентные и создавать одинаковые планы запросов.

В эти дни даже MySQL делает это.

Тем не менее, иногда это не так, но это не означает, что объединения всегда будут выигрывать - у меня были случаи, когда использование подзапросов в MySQL улучшало производительность. (Например, если что-то мешает планировщику mysql правильно оценить стоимость, и если планировщик не видит вариант соединения и вариант подзапроса как один и тот же, тогда подзапросы могут превзойти объединения, форсируя определенный путь).

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

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

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

(хотя Марсело Кантос упоминает об этом)

Я приведу пример из курсов Стэнфорда Lagunita по SQL.

Студенческий стол

+------+--------+------+--------+
| sID  | sName  | GPA  | sizeHS |
+------+--------+------+--------+
|  123 | Amy    |  3.9 |   1000 |
|  234 | Bob    |  3.6 |   1500 |
|  345 | Craig  |  3.5 |    500 |
|  456 | Doris  |  3.9 |   1000 |
|  567 | Edward |  2.9 |   2000 |
|  678 | Fay    |  3.8 |    200 |
|  789 | Gary   |  3.4 |    800 |
|  987 | Helen  |  3.7 |    800 |
|  876 | Irene  |  3.9 |    400 |
|  765 | Jay    |  2.9 |   1500 |
|  654 | Amy    |  3.9 |   1000 |
|  543 | Craig  |  3.4 |   2000 |
+------+--------+------+--------+

Применить таблицу

(заявки, поданные в конкретные университеты и специальности)

+------+----------+----------------+----------+
| sID  | cName    | major          | decision |
+------+----------+----------------+----------+
|  123 | Stanford | CS             | Y        |
|  123 | Stanford | EE             | N        |
|  123 | Berkeley | CS             | Y        |
|  123 | Cornell  | EE             | Y        |
|  234 | Berkeley | biology        | N        |
|  345 | MIT      | bioengineering | Y        |
|  345 | Cornell  | bioengineering | N        |
|  345 | Cornell  | CS             | Y        |
|  345 | Cornell  | EE             | N        |
|  678 | Stanford | history        | Y        |
|  987 | Stanford | CS             | Y        |
|  987 | Berkeley | CS             | Y        |
|  876 | Stanford | CS             | N        |
|  876 | MIT      | biology        | Y        |
|  876 | MIT      | marine biology | N        |
|  765 | Stanford | history        | Y        |
|  765 | Cornell  | history        | N        |
|  765 | Cornell  | psychology     | Y        |
|  543 | MIT      | CS             | N        |
+------+----------+----------------+----------+

Давайте попробуем найти оценки GPA для студентов, которые подали заявку на CS основной (независимо от университета)

Использование подзапроса:

select GPA from Student where sID in (select sID from Apply where major = 'CS');

+------+
| GPA  |
+------+
|  3.9 |
|  3.5 |
|  3.7 |
|  3.9 |
|  3.4 |
+------+

Среднее значение для этого набора результатов:

select avg(GPA) from Student where sID in (select sID from Apply where major = 'CS');

+--------------------+
| avg(GPA)           |
+--------------------+
| 3.6800000000000006 |
+--------------------+

Используя соединение:

select GPA from Student, Apply where Student.sID = Apply.sID and Apply.major = 'CS';

+------+
| GPA  |
+------+
|  3.9 |
|  3.9 |
|  3.5 |
|  3.7 |
|  3.7 |
|  3.9 |
|  3.4 |
+------+

среднее значение для этого набора результатов:

select avg(GPA) from Student, Apply where Student.sID = Apply.sID and Apply.major = 'CS';

+-------------------+
| avg(GPA)          |
+-------------------+
| 3.714285714285714 |
+-------------------+

Очевидно, что вторая попытка дает неверные результаты в нашем случае использования, учитывая, что она подсчитывает дубликаты для вычисления среднего значения. Также очевидно, что использование distinct с оператором на основе соединения не устранит проблему, учитывая, что он ошибочно удержит одно из трех вхождений 3.9 Гол. Правильный случай для учета ДВУХ (2) случаев 3.9 оценка, учитывая, что на самом деле у нас есть ДВА (2) студента с таким количеством баллов, которые соответствуют нашим критериям запроса.

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

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

Но в случае, если таблица имеет более 100 000 таблиц, тогда подзапрос является лучшим результатом.

У меня есть одна таблица, в которой 500 000 записей, которые я создал ниже запроса, и время его результата, как

SELECT * 
FROM crv.workorder_details wd 
inner join  crv.workorder wr on wr.workorder_id = wd.workorder_id;

Результат: 13,3 секунды

select * 
from crv.workorder_details 
where workorder_id in (select workorder_id from crv.workorder)

Результат: 1,65 секунды

Документация MSDN для SQL Server говорит

Многие операторы Transact-SQL, которые включают подзапросы, могут быть альтернативно сформулированы как объединения. Другие вопросы могут быть заданы только с подзапросами. В Transact-SQL обычно нет разницы в производительности между оператором, который включает подзапрос, и семантически эквивалентной версией, которая этого не делает. Однако в некоторых случаях, когда необходимо проверять существование, объединение дает лучшую производительность. В противном случае вложенный запрос должен обрабатываться для каждого результата внешнего запроса, чтобы гарантировать удаление дубликатов. В таких случаях объединенный подход даст лучшие результаты.

так что если вам нужно что-то вроде

select * from t1 where exists select * from t2 where t2.parent=t1.id

попробуйте вместо этого использовать соединение. В других случаях это не имеет значения.

Я говорю: создание функций для подзапросов устраняет проблему беспорядка и позволяет реализовать дополнительную логику для подзапросов. Поэтому я рекомендую по возможности создавать функции для подзапросов.

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

  • Общее правило заключается в том, что в большинстве случаев объединения выполняются быстрее (99%).
  • Чем больше таблиц данных, тем медленнее подзапросы.
  • Чем меньше таблиц данных, тем меньше у подзапросов скорость соединения.
  • Подзапросы проще, легче для понимания и легче для чтения.
  • Большинство веб-фреймворков и фреймворков приложений, а также их ORM и Active record генерируют запросы с подзапросами, потому что с подзапросами легче разделить ответственность, поддерживать код и т. Д.
  • Для небольших веб-сайтов или приложений подзапросы - это нормально, но для больших веб-сайтов и приложений вам часто приходится переписывать сгенерированные запросы, чтобы объединить запросы, особенно если запрос использует много подзапросов в запросе.

Некоторые люди говорят, что "некоторые СУБД могут переписать подзапрос в объединение или в присоединение к подзапросу, когда он считает, что один выполняется быстрее, чем другой", но это утверждение относится к простым случаям, конечно, не для сложных запросов с подзапросами, которые на самом деле вызывают проблемы с производительностью.

Запустите очень большую базу данных из старой Mambo CMS:

SELECT id, alias
FROM
  mos_categories
WHERE
  id IN (
    SELECT
      DISTINCT catid
    FROM mos_content
  );

0 секунд

SELECT
  DISTINCT mos_content.catid,
  mos_categories.alias
FROM
  mos_content, mos_categories
WHERE
  mos_content.catid = mos_categories.id;

~3 секунды

Объяснение показывает, что они проверяют одинаковое количество строк, но одна занимает 3 секунды, а одна почти мгновенная. Мораль истории? Если важна производительность (когда это не так?), Попробуйте несколько способов и посмотрите, какой из них самый быстрый.

А также...

SELECT
  DISTINCT mos_categories.id,
  mos_categories.alias
FROM
  mos_content, mos_categories
WHERE
  mos_content.catid = mos_categories.id;

0 секунд

Опять те же результаты, такое же количество проверенных строк. Я предполагаю, что DISTINCT mos_content.catid требует гораздо больше времени, чем DISTINCT mos_categories.id.

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

   SELECT title FROM books WHERE author_id = (SELECT id FROM authors WHERE last_name = 'Bar' AND first_name = 'Foo');

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

   SELECT title FROM books WHERE author_id IN (SELECT id FROM authors WHERE last_name ~ '^[A-E]');

Это, очевидно, отличается от, скажем, LEFT-JOIN, где вы просто хотите объединить материал из таблиц A и B, даже если условие соединения не находит подходящей записи в таблице B и т. Д.

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

Версия MySQL: 5.5.28-0ubuntu0.12.04.2-log

У меня также сложилось впечатление, что JOIN всегда лучше, чем подзапрос в MySQL, но EXPLAIN - лучший способ сделать суждение. Вот пример, где подзапросы работают лучше, чем JOIN.

Вот мой запрос с 3 подзапросами:

EXPLAIN SELECT vrl.list_id,vrl.ontology_id,vrl.position,l.name AS list_name, vrlih.position AS previous_position, vrl.moved_date 
FROM `vote-ranked-listory` vrl 
INNER JOIN lists l ON l.list_id = vrl.list_id 
INNER JOIN `vote-ranked-list-item-history` vrlih ON vrl.list_id = vrlih.list_id AND vrl.ontology_id=vrlih.ontology_id AND vrlih.type='PREVIOUS_POSITION' 
INNER JOIN list_burial_state lbs ON lbs.list_id = vrl.list_id AND lbs.burial_score < 0.5 
WHERE vrl.position <= 15 AND l.status='ACTIVE' AND l.is_public=1 AND vrl.ontology_id < 1000000000 
 AND (SELECT list_id FROM list_tag WHERE list_id=l.list_id AND tag_id=43) IS NULL 
 AND (SELECT list_id FROM list_tag WHERE list_id=l.list_id AND tag_id=55) IS NULL 
 AND (SELECT list_id FROM list_tag WHERE list_id=l.list_id AND tag_id=246403) IS NOT NULL 
ORDER BY vrl.moved_date DESC LIMIT 200;

ПОЯСНИТЕ показывает:

+----+--------------------+----------+--------+-----------------------------------------------------+--------------+---------+-------------------------------------------------+------+--------------------------+
| id | select_type        | table    | type   | possible_keys                                       | key          | key_len | ref                                             | rows | Extra                    |
+----+--------------------+----------+--------+-----------------------------------------------------+--------------+---------+-------------------------------------------------+------+--------------------------+
|  1 | PRIMARY            | vrl      | index  | PRIMARY                                             | moved_date   | 8       | NULL                                            |  200 | Using where              |
|  1 | PRIMARY            | l        | eq_ref | PRIMARY,status,ispublic,idx_lookup,is_public_status | PRIMARY      | 4       | ranker.vrl.list_id                              |    1 | Using where              |
|  1 | PRIMARY            | vrlih    | eq_ref | PRIMARY                                             | PRIMARY      | 9       | ranker.vrl.list_id,ranker.vrl.ontology_id,const |    1 | Using where              |
|  1 | PRIMARY            | lbs      | eq_ref | PRIMARY,idx_list_burial_state,burial_score          | PRIMARY      | 4       | ranker.vrl.list_id                              |    1 | Using where              |
|  4 | DEPENDENT SUBQUERY | list_tag | ref    | list_tag_key,list_id,tag_id                         | list_tag_key | 9       | ranker.l.list_id,const                          |    1 | Using where; Using index |
|  3 | DEPENDENT SUBQUERY | list_tag | ref    | list_tag_key,list_id,tag_id                         | list_tag_key | 9       | ranker.l.list_id,const                          |    1 | Using where; Using index |
|  2 | DEPENDENT SUBQUERY | list_tag | ref    | list_tag_key,list_id,tag_id                         | list_tag_key | 9       | ranker.l.list_id,const                          |    1 | Using where; Using index |
+----+--------------------+----------+--------+-----------------------------------------------------+--------------+---------+-------------------------------------------------+------+--------------------------+

Тот же запрос с JOINs:

EXPLAIN SELECT vrl.list_id,vrl.ontology_id,vrl.position,l.name AS list_name, vrlih.position AS previous_position, vrl.moved_date 
FROM `vote-ranked-listory` vrl 
INNER JOIN lists l ON l.list_id = vrl.list_id 
INNER JOIN `vote-ranked-list-item-history` vrlih ON vrl.list_id = vrlih.list_id AND vrl.ontology_id=vrlih.ontology_id AND vrlih.type='PREVIOUS_POSITION' 
INNER JOIN list_burial_state lbs ON lbs.list_id = vrl.list_id AND lbs.burial_score < 0.5 
LEFT JOIN list_tag lt1 ON lt1.list_id = vrl.list_id AND lt1.tag_id = 43 
LEFT JOIN list_tag lt2 ON lt2.list_id = vrl.list_id AND lt2.tag_id = 55 
INNER JOIN list_tag lt3 ON lt3.list_id = vrl.list_id AND lt3.tag_id = 246403 
WHERE vrl.position <= 15 AND l.status='ACTIVE' AND l.is_public=1 AND vrl.ontology_id < 1000000000 
AND lt1.list_id IS NULL AND lt2.tag_id IS NULL 
ORDER BY vrl.moved_date DESC LIMIT 200;

и вывод:

+----+-------------+-------+--------+-----------------------------------------------------+--------------+---------+---------------------------------------------+------+----------------------------------------------+
| id | select_type | table | type   | possible_keys                                       | key          | key_len | ref                                         | rows | Extra                                        |
+----+-------------+-------+--------+-----------------------------------------------------+--------------+---------+---------------------------------------------+------+----------------------------------------------+
|  1 | SIMPLE      | lt3   | ref    | list_tag_key,list_id,tag_id                         | tag_id       | 5       | const                                       | 2386 | Using where; Using temporary; Using filesort |
|  1 | SIMPLE      | l     | eq_ref | PRIMARY,status,ispublic,idx_lookup,is_public_status | PRIMARY      | 4       | ranker.lt3.list_id                          |    1 | Using where                                  |
|  1 | SIMPLE      | vrlih | ref    | PRIMARY                                             | PRIMARY      | 4       | ranker.lt3.list_id                          |  103 | Using where                                  |
|  1 | SIMPLE      | vrl   | ref    | PRIMARY                                             | PRIMARY      | 8       | ranker.lt3.list_id,ranker.vrlih.ontology_id |   65 | Using where                                  |
|  1 | SIMPLE      | lt1   | ref    | list_tag_key,list_id,tag_id                         | list_tag_key | 9       | ranker.lt3.list_id,const                    |    1 | Using where; Using index; Not exists         |
|  1 | SIMPLE      | lbs   | eq_ref | PRIMARY,idx_list_burial_state,burial_score          | PRIMARY      | 4       | ranker.vrl.list_id                          |    1 | Using where                                  |
|  1 | SIMPLE      | lt2   | ref    | list_tag_key,list_id,tag_id                         | list_tag_key | 9       | ranker.lt3.list_id,const                    |    1 | Using where; Using index                     |
+----+-------------+-------+--------+-----------------------------------------------------+--------------+---------+---------------------------------------------+------+----------------------------------------------+

Сравнение rows столбец сообщает разницу, и запрос с JOINs использует Using temporary; Using filesort,

Конечно, когда я выполняю оба запроса, первый выполняется за 0,02 секунды, второй не завершается даже через 1 минуту, поэтому EXPLAIN объяснил эти запросы правильно.

Если у меня нет ВНУТРЕННЕГО СОЕДИНЕНИЯ на list_tag таблица т.е. если я удалю

AND (SELECT list_id FROM list_tag WHERE list_id=l.list_id AND tag_id=246403) IS NOT NULL  

из первого запроса и соответственно:

INNER JOIN list_tag lt3 ON lt3.list_id = vrl.list_id AND lt3.tag_id = 246403

из второго запроса EXPLAIN возвращает одинаковое количество строк для обоих запросов, и оба эти запроса выполняются одинаково быстро.

Подзапросы имеют возможность вычислять функции агрегации на лету. Например, найдите минимальную цену книги и получите все книги, которые продаются по этой цене. 1) Использование подзапросов:

SELECT titles, price
FROM Books, Orders
WHERE price = 
(SELECT MIN(price)
 FROM Orders) AND (Books.ID=Orders.ID);

2) с помощью JOIN

SELECT MIN(price)
     FROM Orders;
-----------------
2.99

SELECT titles, price
FROM Books b
INNER JOIN  Orders o
ON b.ID = o.ID
WHERE o.price = 2.99;

Разница видна только тогда, когда вторая объединяющая таблица имеет значительно больше данных, чем первичная таблица. У меня был опыт, как показано ниже...

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

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

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

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

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

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

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

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

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

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

Я просто думаю о той же проблеме, но я использую подзапрос в части FROM. Мне нужно подключиться и запросить из больших таблиц, у "ведомой" таблицы 28 миллионов записей, но результат только 128, так что малый результат - большие данные! Я использую функцию MAX() на нем.

Во-первых, я использую LEFT JOIN, потому что я думаю, что это правильный путь, mysql может оптимизировать и т. Д. Во второй раз, просто для тестирования, я переписываю для дополнительного выбора против JOIN.

Время выполнения левого соединения: 1.12 с Время выполнения SUB-SELECT: 0.06 с

В 18 раз быстрее выбор, чем объединение! Просто в ад Чокито. Подвыбор выглядит ужасно, но результат...

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

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

Если вы хотите ускорить свой запрос с помощью соединения:

Для "внутреннего соединения / соединения" не используйте условие where, вместо этого используйте его в условии "ON". Например:

     select id,name from table1 a  
   join table2 b on a.name=b.name
   where id='123'

 Try,

    select id,name from table1 a  
   join table2 b on a.name=b.name and a.id='123'

Для "Левое / Правое соединение" не используйте в условии "ВКЛ", потому что, если вы используете левое / правое соединение, оно получит все строки для любой одной таблицы. Таким образом, нет смысла использовать его в "Вкл.". Итак, попробуйте использовать условие "Где"

Другие вопросы по тегам