3

我的生产环境中有一个查询需要很长时间才能执行。我没有写这个查询,但我必须找到一种方法让它更快,因为它目前正在引起很大的性能问题。我需要用 Left Join 替换 NOT IN 但不知道如何重写它。现在看起来像跟随

SELECT TOP 1 IT.ITEMID
FROM   (SELECT CAST(ITEMID AS NUMERIC) + 1 ITEMID
        FROM   Items
        WHERE  ISNUMERIC(ITEMID) = 1
               AND CAST(ITEMID AS NUMERIC) >= 50000) IT
WHERE  IT.ITEMID NOT IN (SELECT CAST(ITEMID AS NUMERIC) ITEMID
                         FROM   Items
                         WHERE  ISNUMERIC(ITEMID) = 1)
ORDER  BY IT.ITEMID 

请建议我应该如何使用 Left Join 重写它以获得更好的性能。非常感谢任何帮助/指导。

4

4 回答 4

6

试试这个——

;WITH cte AS 
(
     SELECT DISTINCT ITEMID = 
                CASE WHEN ISNUMERIC(ITEMID) = 1 
                    THEN ITEMID 
                END
     FROM Items
)
SELECT TOP 1 ITEMID = ITEMID + 1
FROM cte t
WHERE ITEMID >= 50000
     AND NOT EXISTS(
          SELECT 1
          FROM cte t2
          WHERE t.ITEMID + 1 = t2.ITEMID
     )
ORDER BY t.ITEMID
于 2013-06-21T07:16:58.133 回答
5

正如评论中提到的,查询的 NOT EXISTS 版本在 SQLServer 中通常比 LEFT JOIN 更快 - 为了完整起见,这是两个版本:

现有查询的左连接变体:

with cte as
(SELECT CAST(it.ITEMID AS NUMERIC) ITEMID 
 FROM Items
 WHERE ISNUMERIC(ITEMID) = 1)
select top 1 i.ITEMID + 1 ITEMID
FROM cte i
LEFT JOIN cte ni ON i.ITEMID + 1 = ni.ITEMID
WHERE i.ITEMID >= 50000 AND ni.ITEMID IS NULL

不存在现有查询的变体:

with cte as
(SELECT CAST(it.ITEMID AS NUMERIC) ITEMID 
 FROM Items
 WHERE ISNUMERIC(ITEMID) = 1)
select top 1 i.ITEMID + 1 ITEMID
FROM cte i
WHERE i.ITEMID >= 50000 AND NOT EXISTS
(SELECT NULL 
 FROM cte ni 
 WHERE i.ITEMID + 1 = ni.ITEMID)
于 2013-06-21T07:23:27.183 回答
4

我同意@ypercube 的观点,理智的做法是修复您的架构。

如果由于某种原因这不是一个选项,那么在运行时将整个事物具体化到一个索引临时表中可能会成为一项糟糕的工作。

CREATE TABLE #T
(
ITEMID NUMERIC(18,0) PRIMARY KEY
                     WITH ( IGNORE_DUP_KEY = ON)
)    

INSERT INTO #T  
SELECT CASE WHEN ISNUMERIC(ITEMID) = 1 THEN ITEMID END
FROM Items
WHERE CASE WHEN ISNUMERIC(ITEMID) = 1 THEN ITEMID END >= 50000  


SELECT TOP 1 ITEMID+1
FROM #T T1
WHERE NOT EXISTS (SELECT * FROM #T T2 WHERE T2.ITEMID = T1.ITEMID +1)
ORDER BY ITEMID
于 2013-06-21T12:18:19.887 回答
4

正如@gbn 在评论中指出的那样,CAST 和使索引无效的谓词函数无论如何都使用,因此将其从NOT INtoLEFT JOIN / IS NULL或 to转换是没有意义的NOT EXISTS。并且NOT EXISTS通常比LEFT NULL在 SQL-Server 中执行得更好。

NOT IN由于存在空值(在比较的列中或由表达式产生)和由于列/表达式的可空性而导致计划效率低下时的问题(错误的、意外的结果),不建议这样做。

并且ISNUMERIC()并不总是像您认为的那样做(正如@ Damien_The_Unbeliever 在另一条评论中指出的那样。)在某些情况下 IsNumeric 结果为 1 但转换失败。

因此,明智的做法是 - 在我看来 - 在表中添加另一列并将(可以转换的值)转换为数字并将它们存储在该列中。然后,您可以编写查询而不进行强制转换,并且可以使用该列上的索引。

如果您无法以任何方式更改表格(通过添加新列或物化视图),那么您可以尝试测试其他答案提供的各种重写。

于 2013-06-21T07:29:06.680 回答