我有一张约有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");
第一次执行此命令时,它会在很多时间内超时。我试图弄清楚这里有什么更好的方法来避免这种超时和/或提高性能。
感谢。
答案 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 /记录。你拥有的百万条记录。在这种情况下,加快速度的唯一方法是在物理数据库级别,即数据在物理上存储在硬盘驱动器上的方式等。
例如:
拥有更快的驱动器将提高性能
如果这种查询被反复触发,那么盒子上的更多内存将有所帮助,因为引擎可以将数据缓存在内存中而且不需要进行物理读取
如果您对表进行分区,那么引擎可以并行查找操作并更快地获得结果。
如果你的表包含许多你并不总是需要的其他字段,那么将表拆分为两个表,其中table1包含guid和最小的字段集,table2包含其余字段将加快由于磁盘I / O需求不足
很多其他的东西要看这里
另请注意,当您发送没有参数的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
您似乎正在做很多不必要的工作,但也许我没有掌握您的申请要求。