【问题标题】:Which is faster (ISNULL(@SKU, '') = '') or (@SKU IS NULL OR @SKU = '')哪个更快 (ISNULL(@SKU, '') = '') 或 (@SKU IS NULL OR @SKU = '')
【发布时间】:2020-05-28 18:46:44
【问题描述】:

有一种情况是我使用IF @SKU IS NULL OR @SKU = '',但我的朋友说与IF ISNULL(@SKU, '') = '' 相比需要更多时间。所以你应该使用IF ISNULL(@SKU, '') = ''。但我认为我使用的是正确的。所以请建议我哪一个跑得更快。

这是我的存储过程:

CREATE PROCEDURE USP_GetExistingRefunds  
(  
  @OrderNo VARCHAR(50),  
  @SKU VARCHAR(255),   
  @ProfileID INT   
)  
AS   
BEGIN    
  --IF ISNULL(@SKU, '') = '' --this work faster or 
  IF @SKU IS NULL OR @SKU = '' --this work faster 
  BEGIN 
    SELECT OrderNo, SKU, ISNULL(Quantity, 0) Quantity, ISNULL(Amount, 0) Amount  
    FROM StoreRefundOrder SRO   
    INNER JOIN StoreRefundOrderItem SROI ON SRO.ID = SROI.RefundOrderID   
    WHERE SRO.OrderNo = @OrderNo    
    AND ProfileID = @ProfileID    
  END 
  ELSE
  BEGIN 
    SELECT OrderNo, SKU, ISNULL(SUM(Quantity), 0) Quantity, ISNULL(SUM(Amount), 0) Amount  
    FROM StoreRefundOrder SRO   
    INNER JOIN StoreRefundOrderItem SROI ON SRO.ID = SROI.RefundOrderID   
    WHERE SRO.OrderNo = @OrderNo   
    AND SROI.SKU = @SKU  
    AND ProfileID = @ProfileID   
    GROUP BY OrderNo, SKU   
  END    
END

【问题讨论】:

  • 即使在性能上有任何不同,我怀疑它是否重要
  • @DaleK,你说得对,我只是想念它。谢谢
  • 在数据库性能的伟大计划中,这是一个微不足道的情况,不值得花任何时间。您会遇到影响更大的问题。
  • 作为一个例子,它建议总是为你的对象添加模式前言,例如而不是FROM StoreRefundOrder SRO 使用FROM dbo.StoreRefundOrder SRO(假设您的方案是dbo)。
  • 你有没有用实际的执行计划开始你的分析,以确保你没有参与premature optimization?你知道瓶颈在哪里吗? (见performance rant。)

标签: sql-server performance tsql stored-procedures


【解决方案1】:

在 IF/ELSE 程序批处理的上下文中

这没有任何区别。确实需要 0.00 毫秒来确定一个值是空白还是未知,而需要 0.00 毫秒来确定 ISNULL(@SKU, '') = ''。如果存在差异,IMO 可能会以纳秒为单位进行测量。同样,这是在程序批处理的上下文中,因为该语句只被评估一次。

在过滤器的上下文中(例如 ON、WHERE 或 HAVING CLAUSE)

这里的差异实际上是巨大的,不能低估。这涉及到参数和变量很难解释,因此,为简洁起见,我向您展示了一个包含此示例数据的示例:

IF OBJECT_ID('tempdb..#things','U') IS NOT NULL DROP TABLE #things;

SELECT TOP (10000) Txt = SUBSTRING(LEFT(NEWID(),36),1,ABS(CHECKSUM(NEWID())%x.N))
INTO   #things 
FROM       (VALUES(1),(30),(40),(NULL)) AS x(N)
CROSS JOIN sys. all_columns;

UPDATE #things SET Txt = NEWID() WHERE txt = '0';

CREATE NONCLUSTERED INDEX nc_things1 ON #things(Txt);

以下查询将查找包含或不包含空格或空值的行

-- Finding things that are blank or NULL
SELECT t.Txt
FROM   #things AS t
WHERE  t.Txt IS NULL OR t.Txt = '';

-- Finding things that are NOT blank or NULL
SELECT t.Txt
FROM   #things AS t
WHERE  NOT(t.Txt IS NULL OR t.Txt = '');

SELECT t.Txt
FROM   #things AS t
WHERE  t.Txt > '';

-- Finding things that are blank or NULL
SELECT t.Txt
FROM   #things AS t
WHERE  ISNULL(t.Txt,'') = '';

-- Finding things that are NOT blank or NULL
SELECT t.Txt
FROM   #things AS t
WHERE  ISNULL(t.Txt,'') <> '';

前三个查询是 SARGable,后两个不是因为 ISNULL。即使有一个索引可以帮助我,ISNULL 也使它在这里毫无用处。要求某人在电话簿中查找姓名以“A”开头的每个人与查找姓名以“A”结尾的每个人之间的区别。

SARGable 谓词允许查询查找索引的一部分,其中非 SARGable 谓词强制查询扫描整个表,无论存在多少匹配行(如果有)。当您处理连接到许多其他表的数百万/数十亿行时,差异可能是在几秒钟内运行的查询,而在某些情况下,可能会运行数小时甚至数周(我​​见过一些)。

执行计划:

请注意,最后一个WHERE t.Txt &gt; '' 也可以使用。任何非空文本值都是&gt; '',如果 t.Txt 为 NULL,那么它也将评估为 false。我包括这个是因为这个表达式适用于过滤的索引。唯一的问题是您不能在隐式转换可以将其转换为数字 0 或更少的文本字段上使用它。请注意这些查询:

IF '' =   0  PRINT 'True' ELSE PRINT 'False'; -- Returns True
IF '' =  '0' PRINT 'True' ELSE PRINT 'False'; -- Returns False
IF '' >  -1  PRINT 'True' ELSE PRINT 'False'; -- Returns True
IF '' > '-1' PRINT 'True' ELSE PRINT 'False'; -- Returns False

【讨论】:

    【解决方案2】:

    IF @SKU IS NULL OR @SKU ='' 正在检查 null 和空白。在第二种情况下 if Isnull(@sku,'') 您正在检查 null 并为 null 值分配 ''。两者是不同的情况。

    【讨论】:

    • OP 已经说过缺少'' 是一个错字。而且您没有解决性能问题-这是问题所在!
    【解决方案3】:

    (ISNULL(@SKU, '') = '')(@SKU IS NULL OR @SKU = '') 哪个更快

    在这种情况下真的没关系。如果您要与列进行比较,那么(SKU IS NULL OR SKU = '') 会更可取,因为它可以使用索引,但是与变量进行单个比较的任何差异都将在微秒的数量级,并且与SELECT 语句的执行时间相比相形见绌。

    为了简化 IF 语句,我可能会按如下方式反转它

      IF @SKU <> '' --Not null or empty string
      BEGIN 
        SELECT OrderNo, SKU, ISNULL(SUM(Quantity), 0) Quantity, ISNULL(SUM(Amount), 0) Amount  
        FROM StoreRefundOrder SRO   
        INNER JOIN StoreRefundOrderItem SROI ON SRO.ID = SROI.RefundOrderID   
        WHERE SRO.OrderNo = @OrderNo   
        AND SROI.SKU = @SKU  
        AND ProfileID = @ProfileID   
        GROUP BY OrderNo, SKU  
      END 
      ELSE
      BEGIN 
         SELECT OrderNo, SKU, ISNULL(Quantity, 0) Quantity, ISNULL(Amount, 0) Amount  
        FROM StoreRefundOrder SRO   
        INNER JOIN StoreRefundOrderItem SROI ON SRO.ID = SROI.RefundOrderID   
        WHERE SRO.OrderNo = @OrderNo    
        AND ProfileID = @ProfileID  
      END  
    

    【讨论】:

    • 这在使用SET ANSI_NULLS OFF 时不起作用(尽管它“很快”会被弃用)
    • @kobik - 是的,但我强烈建议人们不要使用该选项,并且我不会编写代码来满足它。
    【解决方案4】:

    有点长的评论。

    正如许多人已经注意到的那样,在这种情况下,您的两个选项并没有真正产生任何明显的差异。但在未来,当您想到几种不同的编码方式时,您可以快速轻松地实施标准实践来进行自己的测试。

    在代码块的顶部,添加以下命令:

    SET STATISTICS TIME, IO ON;
    

    您可以使用TIMEIO,但我几乎总是想同时看到它们,所以我总是同时打开它们。

    此添加的输出将在您的一个或多个查询运行后显示在您的消息窗口中,并为您提供有关哪种方法更快或对 SQL Server 引擎造成较小压力的切实信息。

    您需要对每个选项运行一些测试。尤其是暖缓存/冷缓存,但几次迭代是获得平均或消除异常结果的最佳方法。

    我很奇怪,所以我总是用以下代码关闭我的代码块:

    SET STATISTICS TIME, IO OFF;
    

    但严格来说,这是不必要的。我只是想重置我更改的任何内容,只是为了避免忘记重置重要的东西。

    Kendra Little 有an informative blog post on using STATISTICS.

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2015-04-23
      • 2021-04-23
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多