[英]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.