2010-12-08 83 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

看來你做了很多不必要的工作,但也許我不掌握你的應用需求。