Запросы внутри запросов: есть ли лучший способ? - PullRequest
1 голос
/ 12 января 2011

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

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

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

SELECT 
  (SELECT username FROM users WHERE records.user_id = user_id) AS username,
  (SELECT last_name||', '||first_name FROM users WHERE records.user_id = user_id) AS name,
  in_timestamp,
  out_timestamp
FROM records
ORDER BY in_timestamp

Редко я буду выполнять подзапросы после предложения WHERE.
Рассмотрим этот пример:

SELECT
  user_id,
  (SELECT name FROM organizations WHERE (SELECT organization FROM locations WHERE records.location = location_id) = organization_id) AS organization_name
FROM records
ORDER BY in_timestamp

В этих двух случаях я бы увидел какое-либо улучшение, если бы решил переписать запросы, используя JOIN?

В качестве общего вопроса, каковы преимущества / недостаткииспользуя подзапросы или JOIN?Является ли один способ более правильным или принятым, чем другой?

Ответы [ 4 ]

2 голосов
/ 12 января 2011

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

Но в целом (и там, где это уместно) вы должны отдавать предпочтение объединениям, а не подвыборам.

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

1 голос
/ 12 января 2011

а) Я бы начал с того, что указал, что они не обязательно взаимозаменяемы. Вложение, как у вас, требует, чтобы было 0 или 1 совпадающее значение, иначе вы получите ошибку. Объединение не предъявляет таких требований и может исключать запись или вводить больше в зависимости от ваших данных и типа объединения.

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

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

d) Относительно «правильности» - я бы предпочел, чтобы объединения, подкрепленные надлежащими ограничениями на ваши данные, где это необходимо (например, уникальный идентификатор пользователя). Вы, как человек, можете сделать определенные предположения, но двигатель БД не может, если вы не скажете это. Чем больше он знает, тем лучше он (и вы) может выполнять свою работу.

1 голос
/ 12 января 2011

JOIN предпочтительнее для разделения [sub] запросов.
Если подвыбор (подзапрос AKA) не коррелирован с внешним запросом, весьма вероятно, что оптимизатор отсканирует таблицы в подвыборе один раз, поскольку значение вряд ли изменится. При наличии корреляции, как в приведенном примере, вероятность оптимизации за один проход становится очень маловероятной. В прошлом считалось, что выполняются коррелированные подзапросы, RBAR - Row By Agonizing Row. С JOIN, тот же результат может быть достигнут при обеспечении одного прохода по таблице.

Это правильная перезапись запроса:

   SELECT u.username,
          u.last_name||', '|| u.first_name AS name,
          r.in_timestamp,
          r.out_timestamp
     FROM RECORDS r 
LEFT JOIN USERS u ON u.user_id = r.user_id
 ORDER BY r.in_timestamp

... потому что подвыбор может возвращать NULL, если user_id не существует в таблице USERS. В противном случае вы можете использовать INNER JOIN:

  SELECT u.username,
         u.last_name ||', '|| u.first_name AS name,
         r.in_timestamp,
         r.out_timestamp
    FROM RECORDS r 
    JOIN USERS u ON u.user_id = r.user_id
ORDER BY r.in_timestamp

Производные таблицы / встроенные представления также возможны с использованием синтаксиса JOIN.

0 голосов
/ 12 января 2011

Соединения в большинстве случаев будут намного быстрее.

Давайте рассмотрим пример.

Позволяет использовать ваш первый запрос:

SELECT 
(SELECT username FROM users WHERE records.user_id = user_id) AS username,
  (SELECT last_name||', '||first_name FROM users WHERE records.user_id = user_id) AS name,
  in_timestamp,
  out_timestamp
FROM records
ORDER BY in_timestamp

Теперь рассмотрим, что у нас есть 100 записей в записях и 100 записей в пользователе. (Предполагается, что у нас нет индекса для user_id)

Итак, если мы понимаем ваш алгоритм, он говорит: Для каждой записи Просканируйте все 100 записей пользователей, чтобы узнать имя пользователя. Сканируйте все 100 записей пользователей, чтобы узнать фамилию и имя

То есть, мы сканировали таблицу пользователей 100 * 100 * 2 раза. Это действительно стоит. Если мы рассмотрим индекс для user_id, это улучшит ситуацию, но стоит ли это.

Теперь рассмотрим соединение (вложенный цикл будет давать почти тот же результат, что и выше, но рассмотрим хеш-соединение): Это как. Составьте хеш-карту пользователя. Для каждой записи Найдите запись сопоставления в Hashmap. Что, безусловно, будет намного быстрее, чем зацикливание и поиск записи.

Так ясно, присоединения должны быть благоприятными.

ПРИМЕЧАНИЕ. Пример использования 100 записей может дать идентичный план, но идея состоит в том, чтобы проанализировать, как это может повлиять на производительность.

...