我有一个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,但它正在启动在我身上成长。^^)
答案 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”这里是一个简单的测试我只在几千行上执行:
编辑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:索引将优化查询执行。