重写SQL查询 - 我需要用Join替换NOT IN

时间:2013-06-21 07:08:52

标签: sql-server performance tsql sql-server-2008-r2

我的生产环境中有一个查询需要很长时间才能执行。我没有写这个查询,但我必须找到一种方法来加快它的速度,因为它现在导致了一个很大的性能问题。我需要用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 个答案:

答案 0 :(得分: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

答案 1 :(得分:5)

正如评论中所提到的,SQLServer中查询的NOT EXISTS版本通常比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)

答案 2 :(得分:4)

正如@gbn所指出的那样,CAST和谓词上的函数无论如何都会使索引无效,因此将此转换为NOT INLEFT JOIN / IS NULLNOT EXISTS没有意义。 NOT EXISTS通常在SQL-Server中的性能优于LEFT NULL

由于存在问题(错误的,意外的结果),当存在空(在比较的列中或由表达式产生)和由于列/尝试的可空性而导致的低效计划时,不建议使用<NOT IN

并且ISNUMERIC()并不总是按照你的想法行事(正如@ Damien_The_Unbeliever在另一条评论中所说的那样。)有些情况下,IsNumeric结果为1但是演员表失败。

因此,理所当然的事情是 - 在我看来 - 在表中添加另一列并将(可以转换的值)转换为数字并将它们存储在该列中。然后你可以在不进行强制转换的情况下编写查询,并且可以使用该列的索引。

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

答案 3 :(得分: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