2017-02-10 53 views
2

我有需要10秒声明,表X上10K项目执行:为什么我的不相关的子查询很慢?

1版

SELECT * 
FROM X 
WHERE pk = 77843 
    AND (a IS NULL OR a NOT IN (SELECT DISTINCT(b) 
           FROM X 
           WHERE pk = 77843 
           AND l IS NOT NULL)) 

子查询是不相关的,这意味着它没有提及外部查询。这意味着子查询只应执行一次。

版本2:

现在,如果我提取子查询和预先查询在< 1S执行执行计算。

DECLARE @listOfb table (id int) 

INSERT INTO @listOfb(id) 
    (SELECT DISTINCT(b) as Numbers 
    FROM X 
    WHERE pk = 77843 
     AND l IS NOT NULL) 

SELECT * 
FROM X 
WHERE pk = 77843 
    AND (a IS NULL OR a NOT IN (SELECT * FROM @listOfb)) 

那么为什么版本2比版本1快得多呢?

更新

我加入了(我认为所谓的)版本1的执行计划: 查询被删除10k左右行。

enter image description here

+3

你看过**执行计划**的两个查询吗? –

+0

'DISTINCT'不是一个函数(在列上),它是'SELECT DISTINCT'的一部分,适用于整个选定的行。删除那些冗余括号以使事情更清晰! 'SELECT DISTINCT(a),b ...'最好写为'SELECT DISTINCT a,b ...',但也可以写为'SELECT DISTINCT a,(b)...' – jarlh

+1

,不需要在这里执行SELECT DISTINCT ... – jarlh

回答

1

尝试用公共表表达式和UNION:

;WITH CTE 
AS 
(
    SELECT * 
    FROM X 
    WHERE pk = 77843 
) 
SELECT * 
FROM CTE 
WHERE a IS NULL 
UNION ALL 
SELECT * 
FROM CTE C1 
WHERE a IS NOT NULL AND 
     NOT EXISTS (SELECT * FROM CTE C2 WHERE C1.a = C2.b AND l IS NULL) 
+1

不需要第一个Select,'NOT EXISTS'无论如何都会返回带有'a'中的NULL的行。 – dnoeth

1

你应该看看这个article

我没有在同一台服务器的版本,但我会尝试解除主查询中的'从x',但也在子查询上解锁。

SELECT * 
FROM X (NOLOCK) 
WHERE pk = 77843 

从我的一点经验,取决于表的大小和它的索引,我发现有时性能上的差异,而查询两次相同表(特别是与相同条件“PK = 77843”和/或更新/删除操作)。

关于您的最新评论。我没有从执行计划中看到多次执行子查询的位置。在我看来,第一个index_seek锁定了pk列[主要查询],并且当到达同一列上的第二个index_seek [子查询](我猜因为我看不到屏幕截图中的所有细节)这会导致性能问题。

但是,这样做的原因是,当您单独执行这两个查询(具有几乎相同的条件)时,性能会更好。