T-SQL查询超时/性能问题

时间:2010-12-08 00:31:06

标签: c# sql-server tsql sql-server-2008

我有一张约有100万条记录的表格。表结构如下所示。 UID列是主键和uniqueidentifier类型。

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

第一次执行此命令时,它会在很多时间内超时。我试图弄清楚这里有什么更好的方法来避免这种超时和/或提高性能。

感谢。

6 个答案:

答案 0 :(得分: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 :(得分:2)

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

答案 2 :(得分:2)

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

然后在您的存储过程中,您可以将参数用作“表格”并进行加入或除外,或者您可以获得什么结果。

此方法比使用函数分割更快,因为MS SQL服务器中的函数非常慢。

但我想是时候了,因为这个查询需要大量的磁盘I / O.由于您正在搜索UId列,因为它们是“随机的”,因此没有索引可以帮助您。引擎将不得不求助于表扫描。这意味着您需要一些严肃的磁盘I / O性能才能在“美好时光”中获得结果。

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

你从你的应用程序发送的guid,只是一个随机的guid列表,或者在这里是一些业务关系或实体关系?您的数据模型可能与您尝试执行的操作不一致。那么你如何确定你必须搜索哪些guids?

但是,为了论证,让我们假设您的guids只是一个随机选择,然后没有真正使用的索引,因为数据库引擎必须进行表扫描以从中挑选出每个所需的guid /记录。你拥有的百万条记录。在这种情况下,加快速度的唯一方法是在物理数据库级别,即数据在物理上存储在硬盘驱动器上的方式等。

例如:

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

  2. 如果这种查询被反复触发,那么盒子上的更多内存将有所帮助,因为引擎可以将数据缓存在内存中而且不需要进行物理读取

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

  4. 如果你的表包含许多你并不总是需要的其他字段,那么将表拆分为两个表,其中table1包含guid和最小的字段集,table2包含其余字段将加快由于磁盘I / O需求不足

  5. ,查询相当多
  6. 很多其他的东西要看这里

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

    最后,在这种情况下,您总是可以增加commandTimeOut属性以超越超时问题。

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

答案 3 :(得分:1)

如果我理解你的问题,在你的客户端代码中你有一个以逗号分隔的字符串(字符串)GUID。只有在TableA中不存在这些GUID时,它们才可供客户端使用。您是否可以调用SP在服务器上创建包含可能可用的GUID的临时表,然后执行以下操作:

        select guid from #myTempTable as temp
        where not exists
           (
            select uid from TABLEA where uid = temp.guid
            )

您可以将您的GUID字符串传递给SP;它将使用您的函数填充临时表;然后将ADO.NET DataTable返回给客户端。在你甚至懒得写SP之前,这应该很容易测试。

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

在所有情况下,您都可以在客户端拆分KeyList以获得更快的结果 - 并且您可以查询无效的密钥:

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

......只是我的两分钱

答案 5 :(得分:0)

在任何情况下,对于所有意图和目的而言,GUID本质上不是唯一的吗? (即普遍唯一 - 无论生成的地方都无关紧要)。我甚至不打算事先做好测试;只需使用GUID PK插入行,如果插入失败,则丢弃GUID。但它不应该失败,除非这些不是真正的GUID。

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

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

您似乎正在做很多不必要的工作,但也许我没有掌握您的申请要求。