2010-12-08 85 views
1

我有一个包含大约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"); 

这个命令被执行超时上的第一次不少场合。我试图找出在这里避免这种超时和/或提高性能的更好方法。

谢谢。

+0

那么Table_A的实际结构是什么?你知道,**的细节**。什么聚集索引键,什么非聚集索引,这种东西。 – 2010-12-08 00:45:54

回答

2

首先添加一个索引,如果没有一个,在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) 
1

如果我正确理解你的问题,在你的客户端代码,你有一个逗号分隔的(串)的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之前,这应该很容易测试。

2

这里有很多关于为什么你不应该使用Guid作为主键的信息,尤其是在无序的情况下。这将是第一个要解决的问题。就你的查询而言,你可以尝试Paul或Tim的建议,但据我所知,EXCEPT和NOT IN将使用相同的执行计划,尽管在某些情况下OUTER JOIN可能更有效。

2

如果您使用的是MS SQL 2008,那么您可以/应该使用TableValue参数。基本上你会以DataTable的形式将你的guid发送到你的存储过程。

然后在你的存储过程中,你可以使用参数作为一个“表”并做一个连接或EXCEPT或你有什么得到你的结果。

由于MS SQL服务器中的函数非常慢,因此此方法比使用函数进行分割要快。

但我猜是由于这个查询需要大量的磁盘I/O,所以需要时间。既然你在你的UId列上搜索,并且因为它们是“随机”的,所以在这里没有索引可以帮到你。引擎将不得不求助于桌面扫描。这意味着您需要一些严重的磁盘I/O性能才能在“正确时间”内获得结果。

不推荐在索引中使用Uid数据类型。但是,这可能对您的情况没有影响。但让我问你:

你从你的应用程序发送的guid只是一个随机的guid列表,或者在这里是一些业务关系或实体关系?这可能是因为你的数据模型对于你正在做的事情是不正确的。那么,你如何确定你需要搜索什么?

但是,出于参数的原因,我们假设你的GUID只是一个随机选择,然后没有真正使用的索引,因为数据库引擎必须执行表扫描来挑选出每个必需的GUID /记录从你有百万条记录。在这样的情况下加快速度的唯一途径是在物理数据库级别,那是你的数据是如何物理存储在硬盘驱动器等

例如:

  1. 有更快的驱动器将提高性能

  2. 如果这种查询是被解雇了个遍,然后在盒子上更多的内存将帮助,因为发动机可以缓存在内存中的数据也不会需要做物理读取

  3. 如果您对表进行分区,那么引擎可以并行化查找操作并更快地获得结果。

  4. 如果你的表中包含很多你并不总是需要的其他字段,然后将表分成两个表,其中table1包含guid和最少的一组字段,而table2包含其余的字段将加快查询相当多的由于磁盘I/O的要求是少

  5. 地块的其他东西看这里

还要注意的是,当你在即席发送SQL语句没有参数引擎必须在每次执行时创建一个计划。在这种情况下,这不是什么大问题,但请记住,每个计划都将缓存在内存中,从而推出可能已被缓存的任何数据。

最后,您可以在这种情况下始终增加commandTimeOut属性以超过超时问题。

现在需要多少时间,你希望得到什么样的改善?

1

我在质疑你如何处理这些信息。

如果插入钥匙进入这个表之后,你可以简单地尝试将他们的第一手资料 - 这是很多在多用户环境中更快,更坚实然后查询后第一次插入:

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); 

...只是我的两分钱

0

在任何情况下,都没有本质上改造成的GUID ,为了所有的意图和目的,独特? (即通用唯一 - 在哪里产生并不重要)。我甚至不打算事先做测试;只需使用GUID PK插入行,如果插入失败,则丢弃GUID。但它不应该失败,除非这些不是真正的GUID。

http://en.wikipedia.org/wiki/GUID

http://msdn.microsoft.com/en-us/library/ms190215.aspx

看来你做了很多不必要的工作,但也许我不掌握你的应用需求。