2014-01-14 32 views
2

我確定這很簡單,但我已經度過了一夜,現在陷入困境。如何確保我的SQL varchar字段是唯一的

我有一塊功能克隆數據庫中的記錄,但是我需要確保新名稱字段在數據庫中是唯一的。

例如,第一個記錄是

[ProjectName] [ResourceCount] 
'My Project'   8 

然後當我點擊克隆我想

'My Project Cloned', 8 

不過,如果我再次按下按鈕,應該注意的是,克隆的名稱存在,而吐出來

'My Project Cloned 2', 8 

這是否有道理?

我可以用臨時表和光標來做,但是必須有更好的方法來做到這一點?

使用SQL Server 2008 R2

該解決方案需要是雖然完全基於T-SQL,這發生在一個單一的存儲過程

+0

等等,什麼?爲什麼?爲什麼不直接連接ProjectName的日期和時間呢? –

+0

這是不是我們的工作,要求業務爲什麼......這只是做和死:) ..雖然這是業務請求,所以它必須完成 –

+1

是的,你必須提供有用的功能的業務。不,你不必完全按照你想要的方式來做這件事。如果此表上有一個標識列,並且行不會被刪除(或者只是軟刪除),那麼在選擇過程中沒有理由不能在視圖或類似中生成正確的編號。您不一定必須*存儲*這個字符串。即使您確實想要存儲號碼,分開存儲號碼並使* display *項目名稱爲計算列也更爲明智。 –

回答

0

我使用WHILE循環中的IF EXISTS解決此問題。

個人而言,我看不到有什麼不對這種方法,但顯然從我對你的問題的理解採取任何意見考慮

DECLARE @NameInvalid varchar(100) 
DECLARE @DealName varchar(100) 
DECLARE @Count int 
SET @Count = 1 
SET @NameInvalid = 'true' 

SELECT @DealName = DealName FROM Deal WHERE DealId = @DealId 

--Ensure we get a unique deal name 
WHILE(@NameInvalid = 'true') 
BEGIN 
    IF NOT EXISTS(SELECT DealName FROM Deal where DealName = @DealName + ' Cloned ' + cast(@Count as varchar(10))) 
    BEGIN 
     INSERT INTO Deal 
      (DealName) 
      SELECT @DealName + ' Cloned ' + cast(@Count as varchar(10)) 
       FROM Deal 
       WHERE DealID = @DealId 
      SET @NewDealId = @@IDENTITY 
     SET @NameInvalid = 'false' 
    END 
    ELSE 
    BEGIN 
     SET @NameInvalid = 'true' 
     SET @Count = @Count + 1 
    END 
END 
+0

這種方法沒有什麼「錯誤」,但有點短視。記錄的數量並不總是提供唯一的後綴,這就是爲什麼你必須循環。這種策略會打開一個巨大的競爭條件,其中另一個調用可以在存在檢查和插入之間插入DealName。它的程序性,容易出錯和緩慢。正如我所建議的那樣,使用輔助表(或者steoleary的解決方案)可以防止這些缺點,但這對您的要求可能是過分的。更強大,但矯枉過正。 –

+0

是的,最後它是過分的,因爲只有1或2個人使用該系統,而這段代碼很少被調用。不幸的是,我沒有時間讓你的答案nto爲我工作的SQL存儲過程。 –

2

可以定義在這兩列的UNIQUE INDEXCONSTRAINT從而:

CREATE UNIQUE INDEX IX_MyTable_ProjectName_ResourceCount 
ON dbo.MyTable ([ProjectName], [ResourceCount]); 
+0

約束不起作用,因爲我需要在存儲過程中處理它 –

+0

您可以很容易地處理存儲過程中的約束違規,請查看我的答案 – steoleary

1

確保價值獨一無二很容易:創建一個unique constraint。如果插入了唯一值,MSSQL將拋出異常,並且可以在應用程序中恢復。

基於計數器(Proj1,Proj2等)創建一個唯一的名稱會涉及更多一點。

請注意,這可以最好地緩解Web層,您可以在其中執行存在檢查並在嘗試插入項目名稱「已被使用之前通知用戶」。而且,如果這不是一個選項,那麼確保唯一性的方法要比列舉的計數要簡單得多。追加datetime或guid會使事情變得相對容易,並且會極大地(如果不是完全的話)避免競爭條件。

如果您絕對必須按照要求在t-sql中實現,那麼將計數器列的某處(即我的「序列」表格)應該有助於最小化競爭條件。即使在下面的例子中,我懷疑你可能會在高頻呼叫下看到一些爭論。

--setup 
/* 
    --your existing table 
    create table dbo.Project 
    (
     [ProjectName] varchar(100) primary key, 
     [ResourceCount] int 
    ); 

    --a new table to transactionally constrain the increment 
    create table dbo.ProjectNameSequence (ProjectName varchar(100) primary key, Seq int); 

    --cleanup 
    --drop table dbo.ProjectNameSequence 
    --drop table dbo.Project 

*/ 

declare @ProjectName varchar(100), @ResourceCount int; 

set @ProjectName = 'Test Project XX'; 
set @ResourceCount = 9; 


merge dbo.ProjectNameSequence [d] 
using (values(@ProjectName)) [s] (ProjectName) on 
     d.ProjectName = s.ProjectName 
when matched then update set Seq += 1 
when not matched then insert values(@ProjectName, 1) 
output @ProjectName + case inserted.Seq when 1 then '' else cast(inserted.Seq as varchar) end, 
     @ResourceCount 
into dbo.Project; 


select * from dbo.Project 
+0

需要在存儲過程中完成.. 。我想我現在使用IF EXISTS和WHILE循環 –

+0

@GavinMannion,你可以在存儲過程的主體中使用合併語句。 –

3

所以,這裏是我會怎麼處理它:

我表:

CREATE TABLE [dbo].[deal] 
(
[dealName] varchar(100), 
[resourceCount] int 
) 

然後創建了dealName列的唯一索引:

CREATE UNIQUE NONCLUSTERED INDEX [UQ_DealName] ON [dbo].[deal] 
(
[dealName] ASC 
) 

一旦你的唯一索引,然後你可以只用try/catch語句

SET NOCOUNT ON; 

DECLARE @dealName VARCHAR(100) = 'deal' 
DECLARE @resourceCount INT = 8 
DECLARE @count INT 

BEGIN TRY 
    BEGIN TRANSACTION 
    INSERT INTO dbo.deal (dealName,resourceCount) 
    VALUES (@dealName, @resourceCount) 
    COMMIT TRANSACTION 
END TRY 
BEGIN CATCH 
    IF @@ERROR = 2601 
    BEGIN 
     ROLLBACK TRANSACTION 
     SET @count = (SELECT COUNT(dealName) FROM dbo.deal WHERE resourceCount = @resourceCount) 
     SET @resourceCount = (SELECT resourceCount FROM dbo.deal WHERE dealName = @dealName) 
     SET @dealName = @dealName + ' Cloned ' + CAST(@count AS VARCHAR(100)) 
     BEGIN TRANSACTION 
      INSERT INTO dbo.deal (dealName,resourceCount) 
      VALUES (@dealName,@resourceCount) 
     COMMIT TRANSACTION 
    END 
END CATCH 
SELECT * FROM dbo.deal 

您可以輕鬆地把這個處理任何例外,如違反唯一約束(錯誤2601)直接在T-SQL代碼插入到一個過程中,它所做的就是嘗試插入一個具有資源計數的交易名稱,如果唯一約束被違反,它將進入catch塊,在查找資源計數後追加所需的信息到交易名稱原始交易,然後插入這些值。我發現這種技術非常有用,不僅僅是爲了強制唯一性,還可以使用類似的方式處理異常數字來處理死鎖,主鍵違規和其他錯誤,全部在T-SQL中。

+0

最後的數字與資源數量無關......我只是希望末尾的數字增加,直到找到一個唯一的名稱 –

+0

您可以輕鬆地用其他值替換該數字以計算交易次數用這個名字,我的主要目的是向你展示如何完全處理存儲過程中的約束違規。 – steoleary

相關問題