大型数据集上SQL Server中的慢速不同查询

大型数据集上SQL Server中的慢速不同查询,sql,sql-server,sql-server-2005,Sql,Sql Server,Sql Server 2005,我们使用SQLServer2005跟踪大量不断传入的数据(每秒5-15次更新)。我们注意到,在它投入生产几个月后,其中一个表开始花费大量时间进行查询 该表有3列: id--自动编号(群集) typeUUID——插入前生成的GUID;用于将类型分组在一起 typeName——类型名称(duh…) 我们运行的一个查询是typeName字段上的一个独特查询: SELECT DISTINCT [typeName] FROM [types] WITH (nolock); typeName字段上有一个

我们使用SQLServer2005跟踪大量不断传入的数据(每秒5-15次更新)。我们注意到,在它投入生产几个月后,其中一个表开始花费大量时间进行查询

该表有3列:

  • id
    --自动编号(群集)
  • typeUUID
    ——插入前生成的GUID;用于将类型分组在一起
  • typeName
    ——类型名称(duh…)
我们运行的一个查询是
typeName
字段上的一个独特查询:

SELECT DISTINCT [typeName] FROM [types] WITH (nolock);
typeName
字段上有一个非聚集、非唯一的升序索引。该表目前包含大约2亿条记录。当我们运行此查询时,查询需要5米58秒才能返回!也许我们不了解索引是如何工作的。。。但我不认为我们误解了他们那么多

为了进一步测试这一点,我们运行了以下查询:

SELECT DISTINCT [typeName] FROM (SELECT TOP 1000000 [typeName] FROM [types] WITH (nolock)) AS [subtbl]
这个查询大约在10秒钟后返回,正如我所预料的,它正在扫描表

这里有什么我们遗漏的吗?为什么第一个查询需要这么长时间

编辑:啊,很抱歉,第一次查询返回76条记录,谢谢你

跟进:谢谢大家的回答,现在对我来说更有意义了(我不知道为什么以前没有…)。在没有索引的情况下,它会扫描200万行的表,在有索引的情况下,它会扫描200万行的索引

SQLServer确实更喜欢索引,它确实会提高一点性能,但没有什么好激动的。重建索引确实将查询时间从600万缩短到了300多万,这是一种改进,但还不够。我只是想向我的老板建议我们规范化表结构


再次感谢大家的帮助

我的第一个想法是统计。要查找上次更新的内容,请执行以下操作:

SELECT
    name AS index_name, 
    STATS_DATE(object_id, index_id) AS statistics_update_date
FROM
    sys.indexes 
WHERE
    object_id = OBJECT_ID('MyTable');
编辑:在重建索引时更新统计数据,我看到这些索引没有得到维护

我的第二个想法是,指数还在吗?顶部查询仍应使用索引。
我刚刚在一个有5700万行的表上进行了测试,两个表都使用了索引。

您确实误解了索引。即使它确实使用了索引,它仍然会对2亿个条目进行索引扫描。这将需要很长的时间,再加上执行DISTINCT(导致排序)所需的时间,运行它是一件坏事。在查询中看到DISTINCT总是会引起一个危险信号,并导致我重新检查查询。在这种情况下,您可能有一个规范化问题?

我怀疑SQL Server是否会尝试使用索引,它必须执行几乎相同的工作量(给定窄表),读取所有2亿行,而不管它是查看表还是索引。如果对
typeName
上的索引进行了聚类,则可能会减少所需的时间,因为它不需要在分组之前进行排序


如果类型的基数较低,那么维护一个包含不同
type
值列表的汇总表如何?插入/更新主表时的触发器将对摘要表进行检查,并在找到新类型时插入新记录。

正如其他人已经指出的那样-在表上执行SELECT DISTINCT(typename)操作时,无论发生什么情况,最终都将进行完整的表扫描

因此,这实际上是一个限制需要扫描的行数的问题

问题是:你需要不同的类型名做什么?你的200米行中有多少行是不同的?您是否只有少数(最多几百个)不同的类型名称

如果是这样,您可以使用一个单独的表DISTINCT_TYPENAMES或其他东西,并通过执行完整的表扫描来填充这些表,然后在主表中插入新行时,只需始终检查它们的typename是否已经在DISTINCT_TYPENAMES中,如果没有,则添加它

这样,您将拥有一个单独的小表,其中只包含不同的TypeName条目,查询和/或显示速度将非常快


马克

我应该试试这样的东西:

SELECT typeName FROM [types] WITH (nolock)
group by typeName;

和其他一样,我想说您需要规范化该列。

索引可以帮助您快速查找行。但您要求数据库列出整个表的所有唯一类型。索引对此无能为力

您可以运行夜间作业,该作业运行查询并将其存储在不同的表中。如果需要最新数据,可以存储夜间扫描中包含的最后一个ID,并合并结果:

select type
from nightlyscan
union
select distinct type
from verybigtable
where rowid > lastscannedid
另一个选项是将大表规范化为两个表:

talbe1: id, guid, typeid
type table: typeid, typename

如果类型的数量相对较少,这将非常有益。

使用
DISTINCT
关键字时,SQL Server优化器存在问题。解决方案是通过分别中断不同的查询来强制它保持相同的查询计划

因此,我们接受了以下问题:

SELECT DISTINCT [typeName] FROM [types] WITH (nolock);
并将其分为以下几部分:

SELECT typeName INTO #tempTable1 FROM types WITH (NOLOCK)
SELECT DISTINCT typeName FROM #tempTable1

另一种绕过它的方法是使用
分组方式,这会得到不同的优化计划。

循环方法应该使用多个寻道(但会失去一些并行性)。对于与总行数(低基数)相比具有相对较少不同值的情况,可能值得一试

这个想法来源于:

看起来还有其他一些方法(特别是递归CTE@Paul White):


我可能遗漏了一些东西,但是如果创建一个具有不同值的视图并查询它会增加负载开销,那么效率会更高吗

如果结果集明显较小,并且每次写入时都会产生过多的填充开销,那么这几乎会立即响应select,尽管考虑到视图本身可能很琐碎

它确实提出了这样一个问题:与您希望使用distinct和imp的频率相比,有多少次写入
select typeName into #Result from Types where 1=0;

declare @t varchar(100) = (select min(typeName) from Types);
while @t is not null
begin
    set @t = (select top 1 typeName from Types where typeName > @t order by typeName);    
    if (@t is not null)
        insert into #Result values (@t);
end

select * from #Result;
create view alltypes
with schemabinding as
select typename, count_big(*) as kount
from dbo.types
group by typename

create unique clustered index idx
on alltypes (typename)
select distinct typename
into alltypes
from types

alter table alltypes
add primary key (typename)

alter table types add foreign key (typename) references alltypes