繁体   English   中英

SQL Server 2008执行计划问题

[英]SQL Server 2008 execution plan question

我有一个问题发给sql guru。

有两个表几乎相同的结构。

根据传入存储过程的参数,我需要从一个或另一个表中收集数据。

如何以最好的方式做到这一点?

请不要建议将这些表合并为一个表 - 这是不合适的。

我做了以下(MS SQL 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运算符?

谢谢

你能用一个简单的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

你真的需要在Erland Sommarskog的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的一半。

我建议的第一件事就是将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 

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

请注意,如果你要把它变成更复杂的东西,关于小Bobby Tables:可能滥用sp_ExecuteSQL并打开大洞,但是使用正确 - 使用参数化动态SQL - 它就像存储过程一样好。

如果你可以创建一个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(但它没有太大的影响),因为无论如何一方总是不返回任何行。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM