T-SQL查询超时/性能问题
我有一个包含大约100万条记录的表。表结构如下所示。 UID列是主键和唯一标识符类型。T-SQL查询超时/性能问题
表-A(含一百万条记录)
UID Name
-----------------------------------------------------------
E8CDD244-B8E4-4807-B04D-FE6FDB71F995 DummyRecord
我也有一个叫做fn_Split('Guid_1,Guid_2,Guid_3,....,Guid_n')
函数,它接受逗号 分离式GUID的列表,并还给包含的GUID表变量。
从我的应用程序代码,我传递一个SQL查询来获取新的GUID [键是与应用程序代码,但不是在数据库表]
var sb = new StringBuilder();
sb
.Append(" SELECT NewKey ")
.AppendFormat(" FROM fn_Split ('{0}') ", keyList)
.Append(" EXCEPT ")
.Append("SELECT UID from Table_A");
这个命令被执行超时上的第一次不少场合。我试图找出在这里避免这种超时和/或提高性能的更好方法。
谢谢。
首先添加一个索引,如果没有一个,在table_a.uid,但我假设有。
一些替代的查询尝试,
select newkey
from fn_split
left outer join table_a
on newkey = uid
where uid IS NULL
select newkey
from fn_split(blah)
where newkey not in (select uid
from table_a)
select newkey
from fn_split(blah) f
where not exists(select uid
from table_a a
where f.newkey = a.uid)
如果我正确理解你的问题,在你的客户端代码,你有一个逗号分隔的(串)的GUID字符串。只有客户不在TableA中时,这些GUIDS才能被客户使用。你可以调用它创建一个包含潜在可用GUIDS服务器上的一个临时表中的SP,然后做到这一点:
select guid from #myTempTable as temp
where not exists
(
select uid from TABLEA where uid = temp.guid
)
你可以你的GUID字符串传递给SP;它会使用你的函数填充临时表;然后将ADO.NET DataTable返回给客户端。在你编写SP之前,这应该很容易测试。
这里有很多关于为什么你不应该使用Guid作为主键的信息,尤其是在无序的情况下。这将是第一个要解决的问题。就你的查询而言,你可以尝试Paul或Tim的建议,但据我所知,EXCEPT和NOT IN将使用相同的执行计划,尽管在某些情况下OUTER JOIN可能更有效。
如果您使用的是MS SQL 2008,那么您可以/应该使用TableValue参数。基本上你会以DataTable的形式将你的guid发送到你的存储过程。
然后在你的存储过程中,你可以使用参数作为一个“表”并做一个连接或EXCEPT或你有什么得到你的结果。
由于MS SQL服务器中的函数非常慢,因此此方法比使用函数进行分割要快。
但我猜是由于这个查询需要大量的磁盘I/O,所以需要时间。既然你在你的UId列上搜索,并且因为它们是“随机”的,所以在这里没有索引可以帮到你。引擎将不得不求助于桌面扫描。这意味着您需要一些严重的磁盘I/O性能才能在“正确时间”内获得结果。
不推荐在索引中使用Uid数据类型。但是,这可能对您的情况没有影响。但让我问你:
你从你的应用程序发送的guid只是一个随机的guid列表,或者在这里是一些业务关系或实体关系?这可能是因为你的数据模型对于你正在做的事情是不正确的。那么,你如何确定你需要搜索什么?
但是,出于参数的原因,我们假设你的GUID只是一个随机选择,然后没有真正使用的索引,因为数据库引擎必须执行表扫描来挑选出每个必需的GUID /记录从你有百万条记录。在这样的情况下加快速度的唯一途径是在物理数据库级别,那是你的数据是如何物理存储在硬盘驱动器等
例如:
有更快的驱动器将提高性能
如果这种查询是被解雇了个遍,然后在盒子上更多的内存将帮助,因为发动机可以缓存在内存中的数据也不会需要做物理读取
如果您对表进行分区,那么引擎可以并行化查找操作并更快地获得结果。
如果你的表中包含很多你并不总是需要的其他字段,然后将表分成两个表,其中table1包含guid和最少的一组字段,而table2包含其余的字段将加快查询相当多的由于磁盘I/O的要求是少
地块的其他东西看这里
还要注意的是,当你在即席发送SQL语句没有参数引擎必须在每次执行时创建一个计划。在这种情况下,这不是什么大问题,但请记住,每个计划都将缓存在内存中,从而推出可能已被缓存的任何数据。
最后,您可以在这种情况下始终增加commandTimeOut属性以超过超时问题。
现在需要多少时间,你希望得到什么样的改善?
我在质疑你如何处理这些信息。
如果插入钥匙进入这个表之后,你可以简单地尝试将他们的第一手资料 - 这是很多在多用户环境中更快,更坚实然后查询后第一次插入:
create procedure TryToInsert @GUID uniqueidentifier, @Name varchar(n) as
begin try
insert into Table_A (UID,Name)
values (@GUID, @Name);
return 0;
end try
begin catch
return 1;
end;
在你可以在客户端拆分密钥列表所有情况下获得更快的结果 - 你可以查询是无效的键:
select UID
from Table_A
where UID in ('new guid','new guid',...);
如果GUID是随机的,你应该使用NEWSEQUENTIALID()与您聚集主键:
create table Table_A (
UID uniqueidentifier default newsequentialid() primary key,
Name varchar(n) not null
);
有了这个,你可以插入和查询您的新插入的数据在一个步骤:
insert into Table_A (Name)
output inserted.*
values (@Name);
...只是我的两分钱
在任何情况下,都没有本质上改造成的GUID ,为了所有的意图和目的,独特? (即通用唯一 - 在哪里产生并不重要)。我甚至不打算事先做测试;只需使用GUID PK插入行,如果插入失败,则丢弃GUID。但它不应该失败,除非这些不是真正的GUID。
http://en.wikipedia.org/wiki/GUID
http://msdn.microsoft.com/en-us/library/ms190215.aspx
看来你做了很多不必要的工作,但也许我不掌握你的应用需求。
那么Table_A的实际结构是什么?你知道,**的细节**。什么聚集索引键,什么非聚集索引,这种东西。 – 2010-12-08 00:45:54