Вопрос о плане выполнения SQL Server 2008 - PullRequest
3 голосов
/ 08 июля 2010

У меня вопрос к гуру sql.

Есть две таблицы с почти идентичной структурой.

На основании параметра, переданного в хранимую процедуру, мне нужно собрать данные из той или иной таблицы.

Как это сделать наилучшим образом?

Пожалуйста, не предлагайте объединять эти таблицы в одну - это не подходит.

Я сделал это следующим образом (MSSQL Server 2008):

Select *
FROM
    String s
    JOIN (
        SELECT id
            ,TypeCode
            ,ProdNo         
                FROM Table1
        WHERE @param = 1 AND TypeCode= 'INV'

        UNION

        SELECT id
            ,TypeCode
            ,ProdNo
        FROM Table2
        WHERE @param = 2 AND TypeCode= 'INV'

    ) m ON m.Id = s.Id
WHERE s.Id = 256

, но когда я посмотрел на план выполнения, я был удивлен, потому что он получал данные из обеих таблиц в параллельных потоках и только после этого фильтровал по значению @param.

Я думал, что фильтрация будет выполняться на первом этапе, а данные будут собираться из одной таблицы.

Можно ли сделать выбор только из одной таблицы, не разбивая запрос на два запроса и не используя IFоператор?

спасибо

Ответы [ 5 ]

4 голосов
/ 08 июля 2010

вам действительно нужно прочитать это Условия динамического поиска в T-SQL Эрланда Соммарского . Вам не нужно беспокоиться о повторении кода, это не домашнее задание. Просто беспокойтесь о том, чтобы план выполнения использовал индекс. При создании «красивого» кода SQL единственное, что нужно учитывать - это отступ и регистр, любые другие изменения могут привести к замедлению плана запроса. Я видел тривиальные изменения в супер быстром запросе, в результате супер медленный запрос. GO FOR SPEED (использование индекса) и дублирующий код при необходимости. также см .: Проклятие и благословения динамического SQL

Вы пометили вопрос sql-server-2008, поэтому если вы используете SQL 2008 SP1 CU5 (10.0.2746) и SQL 2008 R2 CU1 (10.50.1702) и более поздние версии, появляется новое поведение (как описано в статья «Условия динамического поиска» (см. выше) OPTION(RECOMPILE), которая появляется не во всех версиях SQL 2008 или 2005. Это поведение в основном оценивает значения @Local_Variables во время выполнения и соответствующим образом перекомпилирует запрос. В вашем случае это должно привести к тому, что при компиляции будет удалена половина вашего UNION.

4 голосов
/ 08 июля 2010

Не могли бы вы просто использовать простое выражение IF?

IF @Param = 1 
  BEGIN
    EXEC SQL
  END
ELSE IF @Param = 2
  BEGIN
    EXEC SQL
  END 
ELSE
  RAISERROR('Invalid Parameter', 16, 1)

Или же вы можете динамически построить запрос и выполнить его с помощью хранимой процедуры sp_executesql.

DECLARE @Sql NVARCHAR(100)

SET @Sql = N'SELECT * FROM ('

IF @Param = 1
  SET @Sql = @Sql + N'SELECT 1 a, 2 b, 3 c'
ELSE IF @param = 2
  SET @Sql = @Sql + N'SELECT 4 a, 5 b, 6 c'
ELSE
  RAISERROR('Invalid Parameter', 16, 1)

SET @Sql = @Sql + ') tbl'
EXEC sp_executesql @sql
1 голос
/ 08 июля 2010

Первое, что я бы посоветовал, это поместить фильтр ID в объединение.Я также изменил UNION на UNION ALL.Это позволяет избежать оценки строк DISTINCT

Select * 
FROM 
    String s 
    JOIN ( 
        SELECT id 
            ,TypeCode 
            ,ProdNo          
                FROM Table1 
        WHERE @param = 1 AND TypeCode= 'INV' AND id = 256

        UNION ALL

        SELECT id 
            ,TypeCode 
            ,ProdNo 
        FROM Table2 
        WHERE @param = 2 AND TypeCode= 'INV' AND id = 256

    ) m ON m.Id = s.Id 
WHERE s.Id = 256 
0 голосов
/ 08 июля 2010

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

Select 
    * 
FROM 
    String s 
    JOIN ( 
        SELECT 
             id, TypeCode, ProdNo
        FROM 
             tDUMMY INNER JOIN Table1 ON TypeCode= 'INV'
        WHERE
             @param = 1

 UNION ALL

        SELECT 
             id, TypeCode, ProdNo
        FROM 
             tDUMMY INNER JOIN Table2 ON TypeCode= 'INV'
        WHERE
             @param = 2
     ) m ON m.Id = s.Id 
WHERE s.Id = 256

теоретически оптимизатор запросов должен сначала отфильтроватьтаблицу tDUMMY, а затем попытайтесь объединиться.Поэтому, если @param = 1, второй запрос должен выполняться намного быстрее (он снова проверит 1 строку tDUMMY, но он не должен проверять table2)

Примечание - я также сделал этоUNION ALL (но это не окажет большого влияния), потому что одна сторона всегда все равно не будет возвращать строки.

0 голосов
/ 08 июля 2010

SQL Server не такой умный - при написании запросов вы должны только гарантировать, что вы отправляете наименьшее количество SQL для получения нужных вам данных (без отправки лишних операторов), но также предоставлять большую часть информации (через фильтры), гдеМожно дать оптимизатору запросов как можно больше подсказок о данных.Как вы видели, он будет выполнять весь SQL, который вы ему отправляете.

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

DECLARE @sql NVARCHAR(1000)

SET @sql = 'SELECT id, TypeCode, ProdCode'
IF @param = 1
   SET @sql = @sql + ' FROM table1'
IF @param = 2
   SET @sql = @sql + ' FROM table2'
SET @sql = @sql + ' WHERE TypeCode = ''INV'''

EXECUTE sp_ExecuteSQL @sql

Просто знайте, если вы собираетесь превратить это в нечто большеесложный, о маленьких таблицах Бобби: можно злоупотреблять sp_ExecuteSQL и открывать дыры, но при правильном использовании - с параметризованным динамическим SQL - это так же хорошо, как хранимая процедура.

...