Ярлык выполнения SQL-запроса ИЛИ логика? - PullRequest
7 голосов
/ 26 января 2010

У меня есть три таблицы:

SmallTable
   (id int, flag1 bit, flag2 bit)
JoinTable
   (SmallTableID int, BigTableID int)
BigTable
   (id int, text1 nvarchar(100), otherstuff...)

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

Вот логика соединения:

SELECT * FROM
    SmallTable s
    INNER JOIN JoinTable j ON j.SmallTableID = s.ID
    INNER JOIN BigTable b ON b.ID = j.BigTableID
WHERE
    (s.flag1=1 OR b.text1 NOT LIKE 'pattern1%')
    AND (s.flag2=1 OR b.text1 <> 'value1')

Средний объединенный размер - несколько тысяч результатов. Все показанное проиндексировано.

Для большинства SmallTable записей flag1 и flag2 установлены на 1, поэтому на самом деле нет необходимости даже обращаться к индексу на BigTable.text1, но SQL Server все равно делает, что приводит к дорогостоящим индексированным Сканирование и вложенная петля.

Есть ли лучший способ намекнуть SQL Server, что, если flag1 и flag2 оба установлены на 1, он даже не должен беспокоиться о text1?

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

Ответы [ 6 ]

5 голосов
/ 26 января 2010

Булево вычисление SQL НЕ гарантирует короткое замыкание оператора. См. На коротком замыкании логического оператора SQL Server для ясного примера, показывающего, как допущение короткого замыкания оператора может привести к проблемам правильности и ошибкам во время выполнения.

С другой стороны, сам пример в моей ссылке показывает, что делает для SQL Server: предоставление пути доступа, который может использовать SQL. Таким образом, как и в случае всех проблем и вопросов производительности SQL, реальная проблема не в способе выражения текста SQL, а в дизайне хранилища. То есть. какие индексы имеет оптимизатор запросов для удовлетворения вашего запроса?

1 голос
/ 26 января 2010

Я не верю, что SQL Server, к сожалению, будет иметь такое короткое замыкание.

ТАК Я бы предложил сделать 2 запроса и объединить их вместе. Первый запрос с условиями s.flag1 = 1 и s.flag2 = 1 WHERE, а второй запрос объединяет таблицу BigTable с условиями s.flag1 <> 1 a s.flag2 <> 1.

Эта статья по данному вопросу заслуживает прочтения и включает в себя нижнюю строку:

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

Обновление:
Эта статья также интересна для прочтения и содержит несколько полезных ссылок по этой теме, включая технический чат с менеджером по разработке для группы обработчика запросов SQL Server, в котором кратко упоминается, что оптимизатор допускает оценку короткого замыкания. Общее впечатление, которое я получаю из различных статей: «Да, оптимизатор может определить возможность короткого замыкания, но вы не должны полагаться на него и не можете его форсировать». Следовательно, я думаю, что подход UNION может быть вашим лучшим выбором. Если он не придумает план, который использует возможность быстрого доступа, это будет связано с оптимизатором, основанным на затратах, который думает, что нашел разумный план, который этого не делает (это будет связано с индексами, статистикой и т. Д.) .

0 голосов
/ 14 июня 2019

Надеюсь, это сработает - осторожно с логикой быстрого доступа в case операторах вокруг aggregates, но ...

SELECT * FROM
    SmallTable s
    INNER JOIN JoinTable j ON j.SmallTableID = s.ID
    INNER JOIN BigTable b ON b.ID = j.BigTableID
WHERE 1=case when (s.flag1 = 1 and s.flag2 = 1) then 1
when (
       (s.flag1=1 OR b.text1 NOT LIKE 'pattern1%')
       AND (s.flag2=1 OR b.text1 <> 'value1')
    ) then 1
else 0 end
0 голосов
/ 26 января 2010

SQL Server обычно получает подсказку подзапроса (хотя он может отказаться от нее):

SELECT      * 
FROM        (
            SELECT * FROM SmallTable where flag1 <> 1 or flag2 <> 1
            ) s
INNER JOIN  JoinTable j ON j.SmallTableID = s.ID
...
0 голосов
/ 26 января 2010

Это не элегантно, но должно работать ...

SELECT * FROM
    SmallTable s
    INNER JOIN JoinTable j ON j.SmallTableID = s.ID
    INNER JOIN BigTable b ON b.ID = j.BigTableID
WHERE
    (s.flag1 = 1 and s.flag2 = 1) OR 
    (
       (s.flag1=1 OR b.text1 NOT LIKE 'pattern1%')
       AND (s.flag2=1 OR b.text1 <> 'value1')
    )
0 голосов
/ 26 января 2010

Не знаю, будет ли это быстрее без тестовых данных ... но звучит так, как будто

SELECT * FROM
    SmallTable s
    INNER JOIN JoinTable j ON j.SmallTableID = s.ID
    INNER JOIN BigTable b ON b.ID = j.BigTableID
WHERE
    (s.flag1=1) AND (s.flag2=1)
 UNION ALL
 SELECT * FROM
    SmallTable s
    INNER JOIN JoinTable j ON j.SmallTableID = s.ID
    INNER JOIN BigTable b ON b.ID = j.BigTableID
WHERE
    (s.flag1=0 AND b.text1 NOT LIKE 'pattern1%')
    AND (s.flag2=0 AND b.text1 <> 'value1')

Пожалуйста, дайте мне знать, что происходит

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

редактировать

как то так?

SELECT * FROM
    SmallTable s
    INNER JOIN JoinTable j ON j.SmallTableID = s.ID
    INNER JOIN BigTable b ON b.ID = j.BigTableID
WHERE
    (s.flag1=1) AND (s.flag2=1)
 UNION ALL
 SELECT * FROM
    SmallTable s
    INNER JOIN JoinTable j ON j.SmallTableID = s.ID
    INNER JOIN BigTable b ON b.ID = j.BigTableID
WHERE EXISTS
    (SELECT 1 from BigTable b
     WHERE   
    (s.flag1=0 AND b.text1 NOT LIKE 'pattern1%')
    AND (s.flag2=0 AND b.text1 <> 'value1')
)
...