Нужна помощь с запросом SQL на SQL Server 2005 - PullRequest
1 голос
/ 27 апреля 2010

Мы видим странное поведение при запуске двух версий запроса на SQL Server 2005:

версия A:

SELECT otherattributes.* FROM listcontacts JOIN otherattributes
ON listcontacts.contactId = otherattributes.contactId WHERE listcontacts.listid = 1234
ORDER BY name ASC

версия B:

DECLARE @Id AS INT;
SET @Id = 1234;
SELECT otherattributes.* FROM listcontacts JOIN otherattributes
ON listcontacts.contactId = otherattributes.contactId  
WHERE listcontacts.listid = @Id
ORDER BY name ASC

Оба запроса возвращают 1000 строк; версия А занимает в среднем 15 с; версия B в среднем занимает 4 секунды. Может ли кто-нибудь помочь нам понять разницу во времени выполнения этих двух версий SQL?

Если мы вызовем этот запрос через именованные параметры, используя NHibernate, мы увидим следующий запрос через SQL Server profiler:

EXEC sp_executesql  N'SELECT otherattributes.* FROM listcontacts JOIN otherattributes ON listcontacts.contactId = otherattributes.contactId WHERE listcontacts.listid = @id ORDER BY name ASC',
     N'@id INT',
     @id=1234;

... и это имеет тенденцию работать так же плохо, как версия A.

Ответы [ 4 ]

2 голосов
/ 27 апреля 2010

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

Во втором случае оптимизатор не может прослушать значение идентификатора и, следовательно, создает план, который будет приемлем для любого переданного в списке идентификатора. И там, где я говорю «приемлемый», я не имею в виду «оптимальный».

Так что вы можете сделать, чтобы улучшить сценарий? Здесь есть несколько альтернатив:

1) Создайте хранимую процедуру для выполнения запроса, как показано ниже:

СОЗДАНИЕ ПРОЦЕДУРЫ Foo @Id INT КАК ВЫБЕРИТЕ другие атрибуты. * ИЗ списка контактов ПРИСОЕДИНЯЙТЕСЬ к другим атрибутам ON listcontacts.contactId = otherattributes.contactId ГДЕ listcontacts.listid = @Id ЗАКАЗАТЬ по названию ASC

GO

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

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

3) Добавьте OPTION (RECOMPILE) в конец оператора SQL. Вызывает перекомпиляцию этого оператора и может оптимизировать для входного значения

4) Добавьте OPTION (OPTIMIZE FOR (@Id = 1234)) в конец оператора SQL. Это приведет к оптимизации плана, который кэшируется, для этого конкретного входного значения. Прекрасно, если это очень распространенное значение, или наиболее распространенные значения подобны селективным, но не настолько велики, если распределение селективности более широко распространено.

2 голосов
/ 27 апреля 2010

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

0 голосов
/ 27 апреля 2010

Лучший способ увидеть, что происходит, - это сравнить планы выполнения, все остальное - спекуляция, основанная на ограниченных деталях, представленных в вопросе.

Чтобы увидеть план выполнения, перейдите в SQL Server Management Studio и запустите SET SHOWPLAN_XML ON, затем запустите версию запроса A, запрос не будет выполнен, но план выполнения будет отображен в XML. Затем запустите запрос версии B и посмотрите его план выполнения. Если вы все еще не можете определить разницу или решить проблему, опубликуйте оба плана выполнения, и кто-то здесь объяснит это.

0 голосов
/ 27 апреля 2010

Возможно, что вместо того, чтобы привести 1234 к тому же типу, что и listcontacts.listid, а затем выполнить сравнение с каждой строкой, это может привести к тому, что значение в каждой строке будет таким же, как 1234. Для первого требуется только одно приведениедля второго требуется приведение к каждой строке (и это, вероятно, для более чем 1000 строк, это может быть для каждой строки в таблице).Я не уверен, какому типу эта константа будет интерпретироваться, но она может быть «числовой», а не «int».

Если это причина, вторая версия быстрее, потому что она заставляет интерпретировать 1234как int и, таким образом, исключает необходимость приведения значения в каждой строке.

Однако, как предполагает предыдущий плакат, план запросов, показанный в SQL Server Management Studio, может указывать альтернативное объяснение.

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