В чем разница между этими запросами T-SQL, использующими OR? - PullRequest
8 голосов
/ 13 марта 2012

Я использую Microsoft SQL Server 2008 (SP1, x64). У меня есть два запроса, которые делают то же самое, или я так думаю, но у них совершенно разные планы запросов и производительность.

Запрос 1:

SELECT c_pk
FROM table_c
WHERE c_b_id IN (SELECT b_id FROM table_b WHERE b_z = 1)
  OR  c_a_id IN (SELECT a_id FROM table_a WHERE a_z = 1)

Запрос 2:

SELECT c_pk
FROM table_c
LEFT JOIN (SELECT b_id FROM table_b WHERE b_z = 1) AS b ON c_b_id = b_id
LEFT JOIN (SELECT a_id FROM table_a WHERE a_z = 1) AS a ON c_a_id = a_id
WHERE b_id IS NOT NULL
  OR  a_id IS NOT NULL

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

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

Некоторые вопросы:

  • почему планы запросов отличаются?
  • можно ли как-нибудь "научить" SQL Server, что запрос 2 равен запросу 1?

Все таблицы имеют (кластеризованные) первичные ключи и правильные индексы для всех столбцов:

CREATE TABLE table_a (
  a_pk   int NOT NULL PRIMARY KEY,
  a_id   int NOT NULL UNIQUE,
  a_z    int
)
GO
CREATE INDEX IX_table_a_z ON table_a (a_z)
GO

CREATE TABLE table_b (
  b_pk   int NOT NULL PRIMARY KEY,
  b_id   int NOT NULL UNIQUE,
  b_z    int
)
GO
CREATE INDEX IX_table_b_z ON table_b (b_z)
GO

CREATE TABLE table_c (
  c_pk   int NOT NULL PRIMARY KEY,
  c_a_id int,
  c_b_id int
)
GO
CREATE INDEX IX_table_c_a_id ON table_c (c_a_id)
GO
CREATE INDEX IX_table_c_b_id ON table_c (c_b_id)
GO

Таблицы не изменяются после заполнения изначально. Я единственный, кто их опрашивает. Они содержат миллионы записей (table_a: 5M, table_b: 4M, table_c: 12M), но использование только 1% дает аналогичные результаты.

Редактировать: я пытался добавить ИНОСТРАННЫЕ КЛЮЧИ для c_a_id и c_b_id, но это только замедляло запрос 1 ...

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

Ответы [ 3 ]

1 голос
/ 13 марта 2012

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

Читали ли вы:

Я имею в виду, что с IN в БД можно добиться большего успеха оптимизаций , таких как удаление дубликатов, остановка при первом совпадении и т. П. (И это из school воспоминания, так что я уверен, что это будет намного лучше).Так что я думаю вопрос не в том, почему QP отличается, а в том, насколько умными могут быть глубокие оптимизации.

0 голосов
/ 13 марта 2012

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

  1. Выделите запрос, щелкните его правой кнопкой мыши в SSMS и выберите «Анализ запроса в помощнике по настройке ядра СУБД».
  2. Запустите анализ, чтобы выяснить, нужны ли вам какие-либо дополнительные индексыили статистика построена.
  3. Прислушайтесь к советам SQL Server.
0 голосов
/ 13 марта 2012

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

SELECT c_pk 
FROM table_c 
WHERE  Exists( 
 SELECT 1
 FROM table_b 
 WHERE b_z = 1 and c_b_id = b_id 
) OR  Exists( 
 SELECT 1 
 FROM table_a 
 WHERE a_z = 1 and c_a_id = a_id
) 
...