Проблема с OR в операторе JOIN - PullRequest
1 голос
/ 30 августа 2009

У меня проблема с оператором SQL, подробно описанным ниже. Запрос возвращает результаты, которые мне нужны, но требует безумного количества времени для выполнения. У меня сейчас так много записей в БД, что страница обычно не загружается.

SELECT dscan.guid, dscan.drive, dscan.folder, dscan.filename, source.guid  
FROM source 
RIGHT JOIN dscan ON (
  (source.guid & '_dtr' = dscan.guid OR source.guid & '_dto' = dscan.guid OR source.guid = dscan.guid)  
  AND dscan.guid LIKE '%" & Replace(strSearch_guid, "'", "''") & "%'  
  AND dscan.filename NOT LIKE '.[_]%'  
  AND dscan.drive = 'Z:')  
WHERE source.guid Is Null  
ORDER BY dscan.drive, dscan.guid

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

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

Ответы [ 7 ]

1 голос
/ 30 августа 2009

Использование построенных значений сравнения предикатов и «Нравится» с подстановочными символами в начале потребует полного сканирования таблицы. Это будет основным ударом по производительности для больших таблиц, пока вы не перепроектируете свою схему, чтобы устранить это. Однако вы можете устранить снижение производительности от OR, объединив три отдельных оператора sql. попробуйте это:

    SELECT D.guid, D.drive, D.folder, D.filename, S.guid  
    FROM dscan D Left Join source S
        ON S.guid & '_dtr' = D.guid 
          AND D.guid LIKE '%" & Replace(strSearch_guid, "'", "''") & "%'   
          AND D.filename NOT LIKE '.[_]%'    
          AND D.drive = 'Z:')  
    WHERE S.guid Is Null  
  Union
    SELECT D.guid, D.drive, D.folder, D.filename, S.guid  
    FROM dscan D Left Join source S
        ON S.guid & '_dto' = D.guid  
          AND D.guid LIKE '%" & Replace(strSearch_guid, "'", "''") & "%'   
          AND D.filename NOT LIKE '.[_]%'    
          AND D.drive = 'Z:')  
    WHERE S.guid Is Null  
  Union
    SELECT D.guid, D.drive, D.folder, D.filename, S.guid  
    FROM dscan D Left Join source S
        ON S.guid = D.guid    
          AND D.guid LIKE '%" & Replace(strSearch_guid, "'", "''") & "%'   
          AND D.filename NOT LIKE '.[_]%'    
          AND D.drive = 'Z:')  
    WHERE S.guid Is Null  
    ORDER BY D.drive, D.guid
0 голосов
/ 30 августа 2009

Теперь давайте посмотрим, с чего начать ваш оператор выбора:

ВЫБРАТЬ dscan.guid, dscan.drive, dscan.folder, dscan.filename, source.guid
ОТ источника ПРАВИЛЬНОЕ СОЕДИНЕНИЕ dscan ON

и заканчивайте этим:

ГДЕ source.guid имеет значение Null
ЗАКАЗАТЬ по dscan.drive, dscan.guid

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

?? Что именно связывает две таблицы, если вы ищете source.guid null? Потому что, если source.guid имеет значение null, почему вы делаете что-то вроде:

источник ПРАВИЛЬНОЕ СОЕДИНЕНИЕ (source.guid & '_dtr' = dscan.guid ИЛИ source.guid & '_dto' = dscan.guid ИЛИ source.guid = dscan.guid)
AND dscan.guid LIKE '% "& Replace (strSearch_guid,"' "," '' ") &"% '
AND dscan.filename NOT LIKE '. [_]%'
AND dscan.drive = 'Z:')

Ваш запрос занимает много времени, потому что он теряется в соединении. Ваши попытки чтобы заставить его фильтровать слишком много вещей и фильтровать как "% бла бла%" не помогите со скоростью. Проверьте, есть ли у вас правильные индексы для исходной таблицы и таблицы dscan.

Почему ГДЕ source.guid равен NUll Вам нужно, чтобы source.guid совпадал с dscan.guide в таблице dscan.

0 голосов
/ 30 августа 2009

Я переписал ваш запрос:

 SELECT d.guid, 
        d.drive, 
        d.folder, 
        d.filename,
        src.guid
   FROM DSCAN d
   JOIN (SELECT s.quid,
                s.quid & '_dtr' AS DTR,
                s.quid & '_dto' AS DTO,
           FROM SOURCE s
          WHERE s.guid IS NOT NULL) src ON d.guid IN (s.quid, s.dtr, s.dto)
   WHERE d.guid LIKE '%' & REPLACE(strSearch_guid, "'", "''") & '%'
     AND d.filename NOT LIKE '.[_]%'  
     AND d.drive = 'Z:'
ORDER BY d.drive, d.guid

Я предположил, что у вас есть тип, относящийся к source.guid IS NULL в OP - не имело смысла, что вы захотите получить только записи NULL source.guid и затем объединить их.

Это:

RIGHT JOIN dscan ON (source.guid & '_dtr' = dscan.guid OR 
                     source.guid & '_dto' = dscan.guid OR 
                     source.guid = dscan.guid)

... будет использовать индекс, только если он существует, в столбце guid для создания значений, , а не для сравнения. Если вам нужно сделать это, лучше всего сконструировать их во встроенном представлении или с помощью CTE / Subquery Factoring.

0 голосов
/ 30 августа 2009

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

Кроме того, соединение со строковыми данными не дает наилучшей производительности. С другой стороны, если бы ваши столбцы были проиндексированы (добавьте индекс к ним), это помогло бы (если бы вы больше не конкатинировали строку)

Как решение, я не знаю, возможно ли это или требуется добавить столбцы в эту таблицу и включить версии строк с уже добавленными "расширениями", поэтому запросу не нужно будет их объединять?

Это некоторые обходные пути, а не реальное решение

0 голосов
/ 30 августа 2009

Возможно, вы могли бы изменить порядок вещей. Вы можете попробовать:

  • выбор сначала из dscan (я предполагаю, что правильное соединение в dscan означает, что вы хотите все строки из него). После этого вам может даже не потребоваться правильное соединение.
  • переупорядочение предложения ON, например. Сначала сравните вероятности неудачных сравнений, чтобы воспользоваться коротким замыканием. Поместите все И в первую и последнюю ИЛИ
  • перенести некоторые сравнения из предложения ON в предложение WHERE
0 голосов
/ 30 августа 2009

or s - пресловутые присоски производительности. Попробуйте вместо этого использовать предложение in .:

SELECT dscan.guid, dscan.drive, dscan.folder, dscan.filename, source.guid  
FROM source 
RIGHT JOIN dscan ON (
  dscan.guid in (source.guid & '_dtr', source.guid & '_dto', source.guid)
  AND dscan.guid LIKE '%" & Replace(strSearch_guid, "'", "''") & "%'  
  AND dscan.filename NOT LIKE '.[_]%'  
  AND dscan.drive = 'Z:')  
WHERE source.guid Is Null  
ORDER BY dscan.drive, dscan.guid

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

0 голосов
/ 30 августа 2009

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

((source.guid & '_dtr' = dscan.guid) ИЛИ (source.guid & '_dto' = dscan.guid) ИЛИ (source.guid = dscan.guid))

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