Почему это ускоряет мой SQL-запрос? - PullRequest
4 голосов
/ 07 августа 2009

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

Вот мой оригинальный запрос (занимает 20 секунд):

select Part.Id as PartId, Location.Id as LocationId
 FROM Part, PartEvent PartEventOuter, District, Location 
WHERE 
    PartEventOuter.EventType = '600'   AND PartEventOuter.AddressId = Location.AddressId  
    AND Part.DistrictId = District.Id   AND Part.PartTypeId = 15   
    AND District.SubRegionId = 11   AND PartEventOuter.PartId = Part.Id  
    AND PartEventOuter.EventDateTime <= '4/28/2009 4:30pm'   
    AND NOT EXISTS (
            SELECT PartEventInner.EventDateTime  
            FROM PartEvent PartEventInner
            WHERE PartEventInner.PartId = PartEventOuter.PartId
                AND PartEventInner.EventDateTime > PartEventOuter.EventDateTime 
                AND PartEventInner.EventDateTime  <= '4/30/2009 4:00pm')

Вот «оптимизированный» запрос (менее 1 секунды):

select Part.Id as PartId, Location.Id as LocationId
 FROM Part, PartEvent PartEventOuter, District, Location 
WHERE 
    PartEventOuter.EventType = '600'   AND PartEventOuter.AddressId = Location.AddressId  
    AND Part.DistrictId = District.Id   AND Part.PartTypeId = 15   
    AND District.SubRegionId = 11   AND PartEventOuter.PartId = Part.Id  
    AND PartEventOuter.EventDateTime <= '4/28/2009 4:30pm'   
    AND NOT EXISTS (
            SELECT PartEventInner.EventDateTime  
            FROM PartEvent PartEventInner
            WHERE PartEventInner.PartId = PartEventOuter.PartId
                **AND EventType = EventType**
                AND PartEventInner.EventDateTime > PartEventOuter.EventDateTime 
                AND PartEventInner.EventDateTime  <= '4/30/2009 4:00pm')

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

Ответы [ 6 ]

3 голосов
/ 07 августа 2009

вероятно, потому что вы получаете декартово произведение без вашего EventType = EventType

Из Википедии: http://en.wikipedia.org/wiki/SQL

"[SQL] слишком упрощает создание декартового объединения (объединение всех возможных комбинаций), что приводит к наборам результатов" убегания ", когда предложения WHERE вводятся неправильно. На практике декартовые объединения настолько редко используются, что требуют Явное ключевое слово CARTESIAN может быть оправдано. (В SQL 1992 было введено ключевое слово CROSS JOIN, которое позволяет пользователю ясно указать, что предполагается декартово соединение, но сокращение «запятая-соединение» без предиката все еще является приемлемым синтаксисом, который по-прежнему предлагает тот же ошибка.) "

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

http://www.fluffycat.com/SQL/Cartesian-Joins/

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

Есть большое количество записей с EventType = Null?
До того, как вы добавили дополнительное ограничение, ваш подзапрос должен был бы возвращать все эти нулевые записи, которые затем должны были бы сканироваться предикатом Not Exists для каждой строки во внешнем запросе ... Таким образом, чем больше вы ограничиваете, что возвращает подзапрос, меньше строк, которые нужно отсканировать, чтобы проверить, что не существует ...

Если это проблема, вероятно, будет еще быстрее, если вы ограничите записи также EventType = '600' в подзапросе ....

Select Part.Id as PartId, Location.Id as LocationId 
FROM Part, PartEvent PartEventOuter, District, Location 
WHERE PartEventOuter.EventType = '600'   
    AND PartEventOuter.AddressId = Location.AddressId      
    AND Part.DistrictId = District.Id   
    AND Part.PartTypeId = 15       
    AND District.SubRegionId = 11   
    AND PartEventOuter.PartId = Part.Id      
    AND PartEventOuter.EventDateTime <= '4/28/2009 4:30pm'       
    AND NOT EXISTS (SELECT PartEventInner.EventDateTime                  
                    FROM PartEvent PartEventInner
                    WHERE PartEventInner.PartId =  PartEventOuter.PartId
                       AND EventType = '600'                        
                       AND PartEventInner.EventDateTime > PartEventOuter.EventDateTime
                       AND PartEventInner.EventDateTime  <= '4/30/2009 4:00pm')
0 голосов
/ 07 августа 2009

Раньше подобные вещи встречались гораздо чаще, чем сейчас. Например, в Oracle 6 раньше учитывался порядок, в котором вы устанавливали ограничения в предложениях WHERE. Причина, по которой вы удивляетесь, заключается в том, что мы очень хорошо ожидаем, что механизм БД всегда будет выбирать лучший путь доступа, независимо от того, как вы структурируете свой SQL. Oracle 6 & 7 (после этого я перешел на MSSQL) также имеет расширение для подсказок, которое вы можете использовать, чтобы указать базе данных, как можно построить план запроса.

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

РЕГЕНЕРАЦИОННАЯ СТАТИСТИКА

затем повторите попытку и опубликуйте результаты здесь.

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

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

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

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

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

Странно, у вас есть индекс, определенный как EventType и EventDateTime в нем?

Редактировать:
Подождите, это EventType обнуляемый столбец? Column = Column оценивается как FALSE*, если его значение равно NULL. По крайней мере, используя настройки SQL Server по умолчанию.

Более безопасный эквивалент будет EventType IS NOT NULL. Видите ли вы, что дает тот же результат по скорости?


*: моя ссылка на T-SQL говорит, что она должна быть оценена в TRUE с ANSI_NULLS, установленным в OFF, но в моем окне запроса указано иначе * сбит с толку сейчас *.
Есть решение? TRUE, FALSE, NULL или UNKNOWN? :) Должен любить "бинарную" логику в SQL: (

...