0

我正在創建一個應用程序作爲URL計數器。 我已經創建了一個表來存儲url和它的計數。主鍵錯誤Vs select命令

CREATE TABLE [dbo].[tblurlcounter](
    [id] [bigint] IDENTITY(1,1) NOT NULL, 
    [type] [varchar](500) COLLATE SQL_Latin1_General_CP1_CI_AS NULL, 
    [count] [bigint] NULL, 
CONSTRAINT [PK_tblurlcounter] PRIMARY KEY CLUSTERED 
(
    [id] ASC 
)WITH (PAD_INDEX = OFF, IGNORE_DUP_KEY = OFF) ON [PRIMARY] 
) ON [PRIMARY] 

我已經創建了一個存儲過程來在表中插入/更新url。 意味着什麼時候使用存儲過程將值「URL」插入到該表中,然後檢查它是否存在,然後更新其計數,如果不存在,則將其插入到count = 1的表中。

我的存儲過程是這樣的:

declare @count int 

select @count= [count] from tblurlcounter where [type] = @type 
if @count > 0 
begin 
update tblurlcounter set [count][email protected] + 1 where [type] = @type 
select @count + 1 

end 
else 
begin 
INSERT INTO [dbcounter].[dbo].[tblurlcounter] 
      ([type] 
      ,[count]) 
    VALUES 
      (@type 
      ,1) 
end 

這個應用程序將獲得在幾分鐘約80,000至100,000命中。所以我希望我的存儲過程能夠以良好的速度執行操作。我的意思是我的解決方案應該優化。

有人建議我改變我的表格,並創建它的名爲「類型」(我用來存儲url)作爲主鍵和存儲過程我應該首先嚐試插入記錄,如果它的投擲錯誤,然後檢查下一行中的錯誤並執行更新操作。

,所以我很困惑的是哪一個會更快,主鍵錯誤apporach或者我應該選擇查詢和基礎去選擇的結果,我應該執行插入/更新操作

現在我需要專家的建議是,哪種方法是正確的,如果有其他好的方法可用,那麼請給我建議。

感謝

+1

你的代碼目前擁有的競爭條件(不使用事務,並使用默認更強的隔離):兩次執行可能都觀察到的計數爲0,從而嘗試插。 – 2011-04-13 07:30:46

回答

2

對於負載,您需要有點聰明。我已經在此之前發佈過

基本上,不要先測試:嘗試INSERT。如果失敗,運行更新

https://stackoverflow.com/search?q=user%3A27535+JFDI

+0

@gbn:因爲我的檔案名爲「type」,長度爲500。我應該把它作爲主鍵嗎?主密鑰號碼 – 2011-04-13 08:14:09

+0

獨特的是。 – gbn 2011-04-13 08:23:44

+0

500對於索引可能會很大,可能是創建另一個列來保存類型列和索引的散列值? – Magnus 2011-04-13 08:49:29

2

你可以只嘗試做更新,如果不存在,沒有行會被更新,你可以用@@rowcount檢查。如果沒有,那麼你可以添加它,否則該值已經增加。您不需要@count變量,因此您必須鎖定該行,以便在分配該值之後但在更新該表之前,沒有任何值可以更改該值。

update tblurlcounter set [count] = [count] + 1 where [type] = @type 

if @@rowcount = 0 
begin 
    insert into tblurlcounter 
    ([type],[count]) 
    values 
    (@type, 1) 
end 
+1

這也有類似於我在我的評論中注意到的種族 - 當類型不存在時,兩次執行可以運行更新,然後都嘗試插入。 – 2011-04-13 08:55:20

2

當您執行多個語句時,您的方法將無法正常工作。也就是說,這條線之間發生的事情:

select @count = [count] from tblurlcounter where [type] = @type 

,要麼你的INSERT或UPDATE實際執行,存儲過程的一個單獨的執行還可以添加行,所以你可以與幾乎同時出現兩個插入結束。

相反,試試這個:

INSERT INTO [dbcounter].[dbo].[tblurlcounter] 
      ([type] 
      ,[count]) 
    VALUES 
      (@type 
      ,0) 
WHERE NOT EXISTS(select 1 from tblurlcounter where [type] = @type) 

UPDATE tblurlcounter SET [count]=[count] + 1 where [type] = @type 

這將添加一個新行,如果匹配的一個已不存在,則INSERT與存在性檢查相結合。更新語句可以在已知有一行需要更新的情況下安全運行。

你也確實需要你的類型列的索引。

+0

只有一個需要時,您的解決方案需要兩個查找。但我同意類型索引 – Magnus 2011-04-13 07:58:37

+0

插入然後更新是避免比賽的正確順序。我可能會插入'(@ type,1)'並在插入後插入'@@ ROWCOUNT = 1'時退出,但除此之外,這似乎是2005年解決方案的最佳選擇。 – 2011-04-13 08:59:47

-1

這篇文章很好地解釋瞭如果存在更新與更新(如果不存在插入方法)。

顯然是因爲表鎖,做一個select來檢查它是否先存在的代價較低。這樣我們就避免使用更新,這會更多地鎖定比select更多的數據。

http://weblogs.sqlteam.com/mladenp/archive/2007/07/30/60273.aspx

你可以使用:

如果存在(選擇*從tblurlcounter其中[類型] = @type)

+0

如果你的主鍵也是你的集羣鍵(它是默認的),這是一個可怕的想法,主要有兩個原因:'type'列是可變寬度(對於集羣鍵不好),並且太寬。而且,'type'列可能會改變 - 對於主鍵來說也是一個糟糕的主意! – 2011-04-13 07:59:16