我向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运算符?
由于
答案 0 :(得分:4)
你能用一个简单的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 :(得分:4)
你真的需要阅读这个Dynamic Search Conditions in T-SQL by Erland Sommarskog。你不应该担心重复代码,这不是一些家庭作业。只是担心让执行计划使用索引。当使SQL代码“漂亮”时,唯一要考虑的是缩进和放大例如,任何其他更改都可能导致查询计划变慢。我在超慢查询中看到了对超快查询结果的微不足道的更改。 GO FOR SPEED(索引使用)和必要的重复代码。另见:The Curse and Blessings of Dynamic 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的一半在编译时被淘汰。
答案 2 :(得分:1)
我建议的第一件事就是将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
答案 3 :(得分:0)
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 - 它就像存储一样好过程
答案 4 :(得分:0)
如果你可以创建一个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(但不会产生太大的影响),因为无论如何,一方总是不会返回任何行。