SQL: улучшение скорости - левое соединение на cond1 или cond2 - PullRequest
4 голосов
/ 16 февраля 2011
SELECT DISTINCT  a.*, b.*
FROM             current_tbl a
LEFT JOIN        import_tbl  b 
                 ON ( a.user_id = b.user_id 
                   OR ( a.f_name||' '||a.l_name = b.f_name||' '||b.l_name)
                 )
  • Две таблицы, которые в основном одинаковы
  • У меня нет доступа к структуре таблицы или вводу данных (таким образом, нет очистки первичных ключей)
  • Иногда user_id заполняется в одном, а не в другом
  • Иногда имена равны, иногда они не

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

Этот запрос выполняется за 33000 мс, в то время как индивидуальный - около 200 мс.

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

Пожалуйста, не спрашивайте план выполнения.

Ответы [ 8 ]

4 голосов
/ 16 февраля 2011

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

SELECT DISTINCT  a.*, b.*
FROM             current_tbl a
LEFT JOIN        import_tbl  b 
                 ON ( a.user_id = b.user_id 
                   OR (a.f_name = b.f_name and a.l_name = b.l_name)
                 )
4 голосов
/ 16 февраля 2011

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

OR ( a.f_name||' '||a.l_name = b.f_name||' '||b.l_name)

Измените его на:

OR ( a.f_name = b.f_name AND a.l_name = b.l_name)
2 голосов
/ 16 февраля 2011

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

Один неясный трюк, который я использовал для такой ситуации, - это выполнение GROUP BY из запроса UNION ALL. Идея выглядит так:

SELECT a_field1, a_field2, ...
  MAX(b_field1) as b_field1, MAX(b_field2) as b_field2, ...
FROM (
      SELECT a.field_1 as a_field1, ..., b.field1 as b_field1, ...
      FROM current_tbl a
        LEFT JOIN import_tbl b
          ON a.user_id = b.user_id
    UNION ALL
      SELECT a.field_1 as a_field1, ..., b.field1 as b_field1, ...
      FROM current_tbl a
        LEFT JOIN import_tbl b
          ON a.f_name = b.f_name AND a.l_name = b.l_name
  )
GROUP BY a_field1, a_field2, ...

И теперь база данных может выполнять каждое из двух объединений, используя наиболее эффективный план.

(Предупреждение о недостатке в этом подходе. Если строка в current_tbl присоединяется к нескольким строкам в import_tbl, то вы получите очень странное объединение данных.)

Случайный случайный совет. Если у вас нет оснований полагать, что есть потенциальные повторяющиеся строки, избегайте DISTINCT. Это вызывает неявную GROUP BY, которая может быть дорогой.

1 голос
/ 16 февраля 2011

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

SELECT DISTINCT  a.*, b.* 
FROM             current_tbl a 
LEFT JOIN        import_tbl  b  
                 ON ( a.user_id = b.user_id  
                   OR ( a.f_name = b.f_name AND a.l_name = b.l_name) 
                ) 
0 голосов
/ 01 марта 2011

Также еще две вещи - ИЗБАВЬТЕСЬ ОТ ОТЛИЧНОЙ ПОЛОЖЕНИЯ, если только она вам абсолютно не нужна .. n

и, что более важно, вы можете легко избавиться от конкатенации в своем соединении, например, вот так (извините за отсутствие у меня знаний о MySQL)

ВЫБЕРИТЕ ОТЛИЧИЕ a. , b. ОТ current_tbl a LEFT JOIN import_tbl b ON (a.user_id = b.user_id ИЛИ (a.f_name = b.f_name и a.l_name = b.l_name) )

У меня было несколько тестов на работе в похожей ситуации, которые показали 10-кратное улучшение производительности, избавившись от простой конкатенации в вашем объединении

0 голосов
/ 01 марта 2011

мой начальник на моей последней работе .. Клянусь .. он думал, что использование UNIONS всегда ВСЕГДА ИЛИ.

Например ... вместо того, чтобы писать

Выбрать * из сотрудников ГдеEmployee_id = 12 или employee_id = 47

он написал бы (и попросил меня написать)

Выбрать * из сотрудников, где employee_id = 12 UNION Выбрать * из сотрудников, где employee_id = 47

Оптимизатор SQL Sever сказал, что это правильно делать в НЕКОТОРЫХ ситуациях. У меня есть друг, который работает в команде SQL Server в Microsoft, я написал ему об этом по электронной почте, и он сказал мне, что моя статистика устарела или что-то в этом роде.в том же духе.

Я так и не получил хорошего ответа на вопрос, ПОЧЕМУ профсоюзы быстрее, это ДЕЙСТВИТЕЛЬНО нелогично.

Я не рекомендую вам ДЕЛАТЬ это, но в некоторых ситуацияхэто может помочь.

0 голосов
/ 16 февраля 2011

Попробуйте использовать подсказки JOIN:

http://msdn.microsoft.com/en-us/library/ms173815.aspx

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

Важно отметить, что Microsoft говорит это по поводу подсказок JOIN:

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

0 голосов
/ 16 февраля 2011

Вот еще один уродливый способ сделать это.

SELECT a.*
  , CASE WHEN b.user_id IS NULL THEN c.field1 ELSE b.field1 END as b_field1
  , CASE WHEN b.user_id IS NULL THEN c.field2 ELSE b.field2 END as b_field2
  ...
FROM current_tbl a
  LEFT JOIN import_tbl b
    ON a.user_id = b.user_id
  LEFT JOIN import_tbl c
    ON a.f_name = c.f_name AND a.l_name = c.l_name;

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

...