可以优化此SQL查询以更快地运行吗?

时间:2012-06-25 19:23:07

标签: sql sql-server-2008 tsql optimization

我有一个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)

Table1中有大约90万行,并由Name和Code索引。 ExcludedCodes只有大约30行。

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

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

修改

我将NOT IN更改为NOT EXISTS并将时间缩短为10:59,因此仅此一点就是我的巨大收获。我仍然会尝试按照下面的建议执行group by语句,但是这需要完全重写存储过程并且可能需要一些时间......(正如我之前所说,我不是最好的SQL,但它正在启动在我身上成长。^^)

5 个答案:

答案 0 :(得分: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”这里是一个简单的测试我只在几千行上执行:

enter image description here

编辑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分钟的查询。

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

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

答案 2 :(得分: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分钟的瓶颈。例如,您在WHERE子句中使用了超过这些9000万行的游标,还是使用了标量值的UDF?

答案 3 :(得分:1)

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

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

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

答案 4 :(得分:0)

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