当条件包含 IS NULL OR LEN(@localvariable) 时,SQL 非常慢

Posted

技术标签:

【中文标题】当条件包含 IS NULL OR LEN(@localvariable) 时,SQL 非常慢【英文标题】:SQL very slow when where condition contains IS NULL OR LEN(@localvariable) 【发布时间】:2021-10-08 23:45:31 【问题描述】:

当我在where 条件中包含以下行时,我的查询要慢得多

-- @locCandidates IS NULL OR LEN(@locCandidates) = 0

我将订单号(可选)和候选人姓名(可选)作为过滤器传递。

可以用逗号分隔多个候选人的姓名搜索。

谁能指出性能缓慢的原因,我还能怎么写我的最终选择?

这里是 SQL:

DECLARE @Candidates NVARCHAR(MAX)   = 'ann' 
    
DECLARE @locCandidates NVARCHAR(max) = @Candidates     
 create table #Temp
(
    CandidateName Varchar(50)
)

DECLARE @candidateFiltered AS TABLE(Id INT);  
IF @locCandidates IS NOT NULL AND LEN(@locCandidates) > 0  
BEGIN  
    Insert Into #Temp SELECT * FROM   dbo.fn_Split(@locCandidates, ',')
    Insert into @candidateFiltered
    SELECT w.Id    FROM dbo.Workers w, #Temp  WHERE CONCAT(LOWER(w.FirstName), ' ', LOWER(w.LastName)) LIKE CONCAT('%',LOWER(CandidateName),'%');  
    If(OBJECT_ID('tempdb..#temp') Is Not Null)
    Begin
        Drop Table #Temp
    End
END    
    
SELECT oc.OrderId       
FROM dbo.Orders o 
INNER JOIN dbo.OrderCandidates oc ON oc.OrderId = o.Id    
WHERE oc.WorkerId IN (SELECT [Id] FROM @candidateFiltered) 
OR (@locCandidates IS NULL OR LEN(@locCandidates) = 0 )--Alternative to this??
       
  

【问题讨论】:

像您突出显示的那样进行标量比较很快。您确定 if 语句控制的不是 CTE 吗?考虑将 fn_split 的结果放入临时表中。 visualstudiomagazine.com/articles/2015/04/01/… Bad habits to kick : using old-style JOINs - 旧式 逗号分隔的表格列表 样式已替换为 ANSI 中的 proper ANSI JOIN 语法-92 SQL 标准(差不多 30 年前),不鼓励使用它 由于@LocCandidates 是固定的并且您已经在测试它,您可以添加一个else 并在每个if 分支中有两个版本的查询,并且不需要在@LocCandidates 内联测试全部。 OR 通常会很慢...试试LEN(COALESCE(@locCandidates,'')) = 0 本文详细解释:sqlservercentral.com/blogs/revisiting-catch-all-queries 【参考方案1】:

@locCandidates 是一个文本字段。文本操作成本很高。如果您拥有的二维连接有很多记录,例如 100 000 000,那么无论评估 LEN 操作的成本是多少,例如,它都会执行 100 000 000 次。假设它需要 0.000001 秒。然后需要0.000001 * 100000000秒,也就是100秒,一分钟多。取而代之的是,您可以使用 if-else 条件,因此这种循环评估只执行一次,而不是 100 000 000 次左右。

【讨论】:

您还可以预先计算条件并将逻辑结果添加到更适合您的查询中。 你是这个意思吗? @Anna 抱歉,我不明白这个问题。你能详细说明一下吗?【参考方案2】:

您可以在 WHERE 子句中使用 IF 语句代替 OR

IF @locCandidates IS NULL OR LEN(@locCandidates) = 0
BEGIN
    SELECT oc.OrderId       
    FROM dbo.Orders o 
    INNER JOIN dbo.OrderCandidates oc ON oc.OrderId = o.Id      
END
ELSE 
BEGIN
    SELECT oc.OrderId       
    FROM dbo.Orders o 
    INNER JOIN dbo.OrderCandidates oc ON oc.OrderId = o.Id    
    WHERE oc.WorkerId IN (SELECT [Id] FROM @candidateFiltered) 
END 

【讨论】:

这个答案和我的有什么不同?

以上是关于当条件包含 IS NULL OR LEN(@localvariable) 时,SQL 非常慢的主要内容,如果未能解决你的问题,请参考以下文章

sqlsql查询is not null速度慢的一种处理方式

selenium的等待条件

当字符串不匹配时,我希望NULL返回而不是返回整数

DataFrame 列(数组类型)包含 Null 值和空数组(len =0)。如何将 Null 转换为空数组?

SQL Server-聚焦WHERE Column=@Param OR @Param IS NULL有问题?

JPA Left Join IS NULL条件不起作用