sql请求中的where子句应该最后解决
Posted
技术标签:
【中文标题】sql请求中的where子句应该最后解决【英文标题】:Where clause in sql request should be resolved last 【发布时间】:2010-06-22 16:04:26 【问题描述】:我有一个需要很长时间才能执行的 SQL 请求。所以我想让它变得更好,但不知道如何去做。 这是一个例子: 我的要求:
SELECT * from T1
Inner join T2 on T1.a = T2.b
Inner join T3 on T2.c = T3.d
WHERE 1=1
AND T2.e = 'a certain value' --I call i the Clause1
and dbo.MyUDF(T1.id) = 1 --I call i the Clause2
问题似乎来自 UDF 调用。 在没有 Clause1 和 Clause2 的情况下运行请求将给我 2500 行,需要 7 秒。
在没有 Clause2 的情况下运行请求将给我 16 行需要 9 秒。
使用所有 2 个子句运行请求将为我提供 15 行,耗时 1:45 分钟。
但在游标中调用 MyUdf 16X 需要 9 秒。
declare curs cursor
for SELECT T1.id from T1
Inner join T2 on T1.a = T2.b
Inner join T3 on T2.c = T3.d
WHERE 1=1
AND T2.e = 'a certain value'
open curs
fetch next from curs into @fid
while(@@FETCH_STATUS = 0)
BEGIN
select dbo.MyUdf(@fid)
fetch next from curs into @fid
END
close curs
deallocate curs
因此,de SQL 引擎似乎使用 UDF 和运行子句 1 测试了所有 2500 行。而且我希望它做另一个,这样 UDF 将只在 16 行上被调用。
有什么想法吗?
--编辑-- 看一下执行计划,它告诉我我的 UDF 不会使用很多 mutch。所以我认为它总是把它放在第一位。所以我需要告诉 sql server 这部分请求是最糟糕的,它必须在最后一步接受它。你知道怎么做吗?
【问题讨论】:
为什么你的所有查询中都有1=1
?
你想从 UDF 返回什么?
你需要发布dbo.MyUDF
的内容。 SQL 是基于集合的;模块化/OO 编程概念(通常)不适用。
Ben S:我在每个查询中得到 1=1,因为查询是由引擎生成的,而且这是不检查是否已经存在条件的简单方法。
UDF 用于过滤结果。它包含每个项目的保密规则。它有时会使用相同的 UDF 进行递归。
【参考方案1】:
WHERE 子句中的函数不是 SARGable,优化器将进行扫描,因为它无法确定函数返回什么
如果可能的话,在你的 where 子句中复制函数中的代码,它应该运行得更快
类似这样的原因是一样的
WHERE YEAR(DateColumn) = 2008
比
慢很多WHERE DateColumn >= '20080101'
AND DateColumn <'20090101'
第一个将导致扫描,第二个可能会导致查找(如果您有索引)
另见Only In A Database Can You Get 1000% + Improvement By Changing A Few Lines Of Code
【讨论】:
谢谢,但我知道。但是我的udf中的代码充满了规则,我不能在这里复制过去。也因为它被代码/数据库的其他部分使用,并且不想进行丑陋的维护【参考方案2】:首先,正如其他人所说,我不会尝试将业务逻辑封装到 UDF 中,因为很多时候您会像在 OP 中那样在 ON 或 WHERE 子句中使用 UDF。但是,一种解决方案是将查询的较快部分封装到 CTE 表中,如下所示:
With FasterResults As
(
Select T1.id, ...
From T1
Join T2
On T2.b = T1.a
Join T3
On T3.d = T2.c
Where 1=1
And T2.e = 'a certain value'
)
Select
From FasterResults As F
Where dbo.MyUDF(F.id) = 1
另一种解决方案(虽然远非理想)是使用FORCE ORDER
查询提示:
Select T1.id, ...
From T1
Join T2
On T2.b = T1.a
And T2.e = 'a certain value'
Join T3
On T3.d = T2.c
Join T1 As T12
On T12.a = T1.a
And dbo.MyUDF(F.id) = 1
Where 1=1
OPTION (FORCE ORDER)
【讨论】:
我认为你是对的。我正要发布这个,但我打算先尝试一下。您确定查询优化器将按顺序运行两个过滤器,而不是将它们全部优化为一个吗?我在想 UDF 不允许副作用的原因是允许优化器做这样的事情。 无法保证您的第一个解决方案会长期有效。你不能做出这样的假设 也是“FORCE ORDER 指定连接顺序”,所以只适用于连接顺序,不适用于谓词求值顺序。 @AlexKuznetsov - 是的,我明白了。但是,您可以通过将条件放在最后一个连接中来轻松解决此问题。尽管如此,它甚至都不是一个好的解决方案。将 UDF 重构为内联 UDF 或视图会是更好的解决方案。【参考方案3】:短期,使用这个:
SELECT * from T1
Inner join T2 on T1.a = T2.b
Inner join T3 on T2.c = T3.d
WHERE 1=1
AND T2.e = 'a certain value' --I call i the Clause1
and CASE WHEN T2.e = 'a certain value'
THEN dbo.MyUDF(T1.id)
ELSE 1
END = 1
从长远来看,考虑使用内联 UDF 而不是标量。
【讨论】:
问题是我的请求是由引擎生成的,很难写出像你这样的例子。【参考方案4】:不确定,但您可以在 WHERE 子句中嵌入 if 语句。这已经在 *** here 上问过。
这只是伪代码,但可能类似于:
SELECT * from T1
Inner join T2 on T1.a = T2.b
Inner join T3 on T2.c = T3.d
WHERE 1=1
AND
IF T2.e = 'a certain value'
THEN
T2.e = 'a certain value' --I call i the Clause1
and dbo.MyUDF(T1.id) = 1 --I call i the Clause2
【讨论】:
【参考方案5】:我会将查询更改为此...因为您正在执行内部连接..
select T1.*
from T2
inner join T1
on t2.b = t1.a
inner join T3
on t2.c = t3.d
where
t2.e = 'a certain value'
and dbo.MyUDF( T1.id ) = 1
由于 t2 是 WHERE 子句的主表,我会将它作为我的主 From 源,并链接到 OTHER 表中,因为无论如何内部连接都包括所有 3 个。然后,继续您的 UDF() 调用。忽略你的 where 1=1,它永远不适用,因为它总是返回 true。
【讨论】:
【参考方案6】:首先,感谢大家。你的回答让我学到了很多。
所以解决方案似乎是创建一个视图并将其调用到请求中。
所以我先创建一个视图:
select id,MyUDF(id)
From T1
然后将请求改成:
SELECT * from T1
Inner join T2 on T1.a = T2.b
Inner join T3 on T2.c = T3.d
inner join MyView V on T1.id = V.id
WHERE 1=1
AND T2.e = 'a certain value' --I call i the Clause1
and v.value = 1 --I call i the Clause2
这需要 15 秒。
呜呜呜!!!
【讨论】:
以上是关于sql请求中的where子句应该最后解决的主要内容,如果未能解决你的问题,请参考以下文章
SQL Server:IF .. ELSE 中的 Where 子句