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 子句

我们应该在检索数据时避免 DB2 SQL 中的 IN 子句吗?

SQL Server 中的条件 WHERE 子句

在oracle中where 子句和having子句中的区别

VBA SQL 缺少 WHERE 子句的最后一个条件

如何缩短 T-SQL 中的 WHERE 子句