我有一个需要很长时间才能执行的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个子句的请求将以1:45分钟给我15行。
但是在游标中调用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 Engine使用UDF测试所有2500行并运行Clause1。而且我希望它能完成另一个,所以只能在16行调用UDF。
任何想法?
- Edit-- 看一下执行计划,它告诉我的UDF不会使用非常mutch。所以我认为它总是先把它拿走。所以我需要告诉sql server这部分请求是最糟糕的,它必须在最后一次采取它。你知道怎么做吗?
答案 0 :(得分:4)
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
答案 1 :(得分:2)
首先,正如其他人所说的那样,我不会尝试将业务逻辑封装到UDF中,因为很多时候您会试图在ON或WHERE子句中使用UDF,就像在OP中一样。但是,一种解决方案是将查询的较快部分封装到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)
答案 2 :(得分:1)
短期,请使用:
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而不是标量UDF。
答案 3 :(得分:0)
不确定但是你可以但是在WHERE子句中的嵌入式if语句中。已经在stackoverflow 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
答案 4 :(得分:0)
我会将查询更改为此...因为您正在进行内部联接..
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()调用。忽略你的1 = 1,它永远不适用,因为它总是返回true。
答案 5 :(得分:0)
首先,感谢大家。你回答的所有内容都让我非常了解。
因此解决方案似乎创建了一个视图并将其调用到请求中。
所以我先创建一个视图:
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秒。
Wourrayyy !!!