4

我有一个需要很长时间才能完成的 SQL 查询(对于 SQL Server 2008 R2)。我想知道是否有更好的方法来做到这一点?

SELECT @count = COUNT(Name)
FROM Table1 t
WHERE t.Name = @name AND t.Code NOT IN (SELECT Code FROM ExcludedCodes)

表 1 中有大约 9000 万行,并按名称和代码进行索引。ExcludedCodes 中只有大约 30 行。

这个查询在一个存储过程中,被调用了大约 40k 次,完成该过程的总时间是 27 分钟。我相信这是我最大的瓶颈,因为它查询的行数和次数很多它做到了。

因此,如果您知道优化它的好方法,将不胜感激!如果它不能被优化,那么我想我会坚持 27 分钟......

编辑

我将时间更改NOT INNOT EXISTS10:59,因此仅此一项对我来说就是一个巨大的收获。我仍然会尝试按照下面的建议执行 group by 语句,但这需要完全重写存储过程并且可能需要一些时间......(正如我之前所说,我不是最擅长 SQL,但它正在开始在我身上成长。^^)

4

5 回答 5

5

除了让查询本身更快响应的变通方法之外,您是否考虑过在表中维护一个列来说明它是否在这个集合中?它需要大量维护,但如果ExcludedCodes表不经常更改,那么进行维护可能会更好。例如,您可以添加一个 BIT 列:

ALTER TABLE dbo.Table1 ADD IsExcluded BIT;

将其设为 NOT NULL 并默认为 0。然后您可以创建过滤索引:

CREATE INDEX n ON dbo.Table1(name)
  WHERE IsExcluded = 0;

现在您只需更新表一次:

UPDATE t
  SET IsExcluded = 1
  FROM dbo.Table1 AS t
  INNER JOIN dbo.ExcludedCodes AS x
  ON t.Code = x.Code;

并且正在进行中,您必须使用两个表上的触发器来维护它。有了这个,您的查询变为:

SELECT @Count = COUNT(Name)
  FROM dbo.Table1 WHERE IsExcluded = 0;

编辑

至于“NOT IN 比 LEFT JOIN 慢”,这是我仅在几千行上执行的简单测试:

在此处输入图像描述

编辑 2

我不确定为什么这个查询不会做你所追求的,并且比你的 40K 循环更有效率:

SELECT src.Name, COUNT(src.*)
  FROM dbo.Table1 AS src
  INNER JOIN #temptable AS t
  ON src.Name = t.Name
  WHERE src.Code NOT IN (SELECT Code FROM dbo.ExcludedCodes)
  GROUP BY src.Name;

或 LEFT JOIN 等价物:

SELECT src.Name, COUNT(src.*)
  FROM dbo.Table1 AS src
  INNER JOIN #temptable AS t
  ON src.Name = t.Name
  LEFT OUTER JOIN dbo.ExcludedCodes AS x
  ON src.Code = x.Code
  WHERE x.Code IS NULL
  GROUP BY src.Name;

我会在不到 27 分钟的时间内将钱花在其中任何一个查询上。我什至建议按顺序运行这两个查询将比您需要 27 分钟的一个查询快得多。

最后,您可能会考虑使用索引视图。我不知道您的表结构以及您是否违反任何限制,但值得调查恕我直言。

于 2012-06-25T19:39:24.980 回答
4

你说这被调用了大约 40K 次。为什么?它在游标中吗?如果是这样,你真的需要一个光标。你不能把你想要的 @name 值放在一个临时表中并索引它然后加入它吗?

select t.name, count(t.name) 
from table t
join #name n on t.name = n.name 
where NOT EXISTS (SELECT Code FROM ExcludedCodes WHERE Code = t.code)
group by t.name

这可能会在一个查询中为您提供所有结果,并且几乎可以肯定比 40K 单独查询要快。当然,如果您需要计算所有名称,那就更简单了

select t.name, count(t.name) 
    from table t
NOT EXISTS (SELECT Code FROM ExcludedCodes WHERE Code = t
group by t.name
于 2012-06-25T19:32:16.390 回答
2

NOT EXISTS通常性能优于NOT IN,但您应该在系统上对其进行测试。

SELECT @count = COUNT(Name)
FROM Table1 t
WHERE t.Name = @name AND NOT EXISTS (SELECT 1 FROM ExcludedCodes e WHERE e.Code = t.Code)

在不了解您的查询的更多信息的情况下,很难提供具体的优化建议(即适合复制/粘贴的代码)。它真的需要运行 40,000 次吗?如果可行的话,听起来您的存储过程需要返工。您可以在 proc 开始时执行上述一次并将结果插入临时表中,这可以保留索引Table1,然后加入该表而不是运行此查询。

这个特定的位甚至可能不是让您的查询运行 27 分钟的瓶颈。例如,您是在这 9000 万行上使用游标,还是在WHERE子句中使用标量值 UDF?

于 2012-06-25T19:27:30.897 回答
1

您是否考虑过执行一次查询并将数据填充到表变量或临时表中?就像是

insert into #temp (name, Namecount)
values Name, Count(name)
from table1 
where name not in(select code from excludedcodes)
group by name

并且不要忘记,只要排除代码表有些静态,您就可以使用过滤索引。

于 2012-06-25T19:31:33.950 回答
0

开始评估执行计划。哪个是计算最重的部分?关于两个表之间的关系,在索引列上使用 JOIN:索引将优化查询执行。

于 2012-06-25T19:31:56.593 回答