应该在最后解析sql请求中的where子句

时间:2010-06-22 16:04:26

标签: sql sql-server

我有一个需要很长时间才能执行的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这部分请求是最糟糕的,它必须在最后一次采取它。你知道怎么做吗?

6 个答案:

答案 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 !!!