2009-11-20 40 views
2

我有一个插入使用NOT IN的条件检查。 NOT IN子查询中有大约230k行。帮助优化这个查询与一个巨大的IN语句

INSERT INTO Validate.ItemError (ItemId, ErrorId, DateCreated) 
(
    SELECT ItemId, 10, GetUTCDate() 
    FROM Validate.Item 
    INNER JOIN Refresh.Company 
    ON Validate.Item.IMCompanyId = Refresh.Company.IMCompanyId 
    WHERE Refresh.Company.CompanyId = 14 
    AND 
    (
     IMAccountId IS NULL OR NOT IMAccountId IN 
     (
      SELECT RA.IMAccountId 
      FROM Refresh.Account RA 
      INNER JOIN Refresh.BalancePool BP 
      ON RA.BalancePoolId = BP.BalancePoolId 
      WHERE BP.CompanyId = 14 
     ) 
    ) 
) 

当我运行该原样大约需要30多分钟(亚克西!)。 Validate.Item表中的值的数量可以是从150行到超过200k的任何值,所以你可以看到这可能是一个痛苦。

表中所有相关字段都有索引,并且没有任何过分分段。

我首先想到的是要做到这一点的碎片,扔到一个WHILE循环:

DECLARE @StartId int, @EndId int, @MaxId int 

SELECT @MaxId = MAX(AccountId) FROM Refresh.Account 
SET @StartId = 1 
SET @EndId = 1000 

WHILE (@StartId < @MaxId) 
BEGIN 
    INSERT INTO Validate.ItemError (ItemId, ErrorId, DateCreated) 
    (
     SELECT ItemId, 10, GetUTCDate() 
     FROM Validate.Item 
     INNER JOIN Refresh.Company 
     ON Validate.Item.IMCompanyId = Refresh.Company.IMCompanyId 
     WHERE Refresh.Company.CompanyId = 14 
     AND 
     (
      IMAccountId IS NULL 
      OR NOT IMAccountId IN 
      (
       SELECT RA.IMAccountId 
       FROM Refresh.Account RA 
       INNER JOIN Refresh.BalancePool BP 
       ON RA.BalancePoolId = BP.BalancePoolId 
       WHERE BP.CompanyId = 14 
       AND RA.AccountId BETWEEN @StartId AND @EndId 
      ) 
     ) 
    ) 
    SET @StartId = @StartId + 1000 
    SET @EndId = @EndId + 1000 
END 

这样做,这样我网大约每圈一分钟的时间;乘以230倍,我们有一个更可笑的数字。

请告诉我你们有更好的想法如何优化这个。没有这个查询,整个过程只需要8秒;这只是Refresh.Account表的巨大尺寸,它将所有内容抛入混乱中。

TIA!

武神

+1

你可能已经优化很差,你可能想先解决/索引的表。 – 2009-11-20 14:59:58

+0

@Kragen:你是如何缩进代码的?任何工具? – shahkalpesh 2009-11-20 15:04:58

+1

您可否请现在发布查询计划? – Quassnoi 2009-11-20 15:17:28

回答

0

这里是否使用NOT EXISTS帮助吗?

(SELECT ItemId, 10, GetUTCDate() 
FROM Validate.Item INNER JOIN Refresh.Company ON 
Validate.Item.IMCompanyId = Refresh.Company.IMCompanyId 
WHERE Refresh.Company.CompanyId = 14 
AND (IMAccountId IS NULL OR NOT EXISTS (SELECT TOP 1 RA.IMAccountId FROM 
Refresh.Account RA INNER JOIN Refresh.BalancePool BP 
ON RA.BalancePoolId = BP.BalancePoolId WHERE BP.CompanyId = 14 AND 
RA.IMAcccountID = Validate.Item.IMAccountId))) 

我不知道,如果查询是正确的。

但是,我在子查询中使用NOT EXISTS以及TOP 1
另外,子查询通过增加额外的AND RA.IMAcccountID = Validate.Item.IMAccountId来限制记录。

编辑:我希望你能明白我想做的事情。
而不是根据Refresh.Account中的所有行对其进行检查,而是限制行并试图找到至少1个匹配IMAccountID的匹配行 - 根据您的原始查询(使用NOT IN ...),这不应该存在。

+0

你比我快:) – Erlock 2009-11-20 15:07:17

+0

你们所有人都摇滚!这让我花了1秒钟的时间查询,完全是我需要的。非常感谢! – Valkyrie 2009-11-20 16:38:28

1

使用NOT EXISTS来代替:

...OR NOT EXISTS (SELECT 1 FROM 
Refresh.Account RA INNER JOIN Refresh.BalancePool BP 
ON RA.BalancePoolId = BP.BalancePoolId WHERE BP.CompanyId = 14 AND RA.IMAccountId = xxx.IMAccountId))) 

子查询以下EXISTS只会返回第一条记录令人满意的标准。 (请记住将xxx替换为右表的别名)

+0

几乎与shahkalpesh一样,但不需要“TOP 1”。 – 2009-11-20 15:10:37

1

而不是做一个“不在”,你可以做一个左连接到相关表并检查空键吗?不知道该查询是否100%正确:

INSERT INTO Validate.ItemError (ItemId, ErrorId, DateCreated) 
SELECT ItemId, 10, GetUTCDate() 
FROM Validate.Item 
INNER JOIN Refresh.Company ON Validate.Item.IMCompanyId = Refresh.Company.IMCompanyId 
LEFT JOIN Refresh.Account 
    INNER JOIN Refresh.BalancePool BP ON BP.BalancePoolId = RA.BalancePoolId 
ON Refresh.Account.IMAccountId = Validate.Item.IMAccountId 
WHERE Refresh.Company.CompanyId = 14 
AND Validate.Item.IMAccountId IS NULL OR Refresh.Account.IMAccountId IS NULL 
2

摆脱OR条件。

它增加了一个全扫描,并防止优化器使用否则将使用的ANTI JOIN

该查询返回相同的:

SELECT ItemId, 10, GetUTCDate() 
FROM Validate.Item 
INNER JOIN 
     Refresh.Company 
ON  Validate.Item.IMCompanyId = Refresh.Company.IMCompanyId 
WHERE Refresh.Company.CompanyId = 14 
     AND NOT EXISTS 
     (
     SELECT RA.IMAccountId 
     FROM Refresh.Account RA 
     INNER JOIN 
       Refresh.BalancePool BP 
     ON  RA.BalancePoolId = BP.BalancePoolId 
     WHERE BP.CompanyId = 14 
       AND RA.IMAccounID = Validate.Item.IMAccountId 
     )