繁体   English   中英

如何确保我的SQL varchar字段唯一

[英]How to ensure my SQL varchar field is unique

我敢肯定这确实很简单,但是我已经熬夜了,现在陷入困境。

我有一个功能可以克隆数据库中的记录,但是我需要确保新名称字段在数据库中是唯一的。

例如,第一条记录是

[ProjectName]   [ResourceCount]
'My Project'         8

然后,当我单击所需的克隆时

 'My Project Cloned', 8

但是,如果我再次按下按钮,它应该注意到克隆的名称存在并且吐出了

'My Project Cloned 2', 8

这有意义吗?

我可以用临时表和游标来做到这一点,但是必须有一种更好的方法来做到这一点?

使用SQL Server 2008 R2

该解决方案需要完全基于T-SQL,但这发生在单个存储过程中

因此,根据我对您问题的了解,以下是我的解决方法:

我的桌子:

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

然后在DealName列上创建一个唯一索引:

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

一旦有了唯一索引,就可以直接使用try / catch在T-SQL中直接处理任何异常,例如唯一约束冲突(错误2601)

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

您可以轻松地将此代码放入过程中,只需尝试插入带有资源计数的交易名称,如果违反了唯一约束,则进入catch块,找到后将想要的信息附加到交易名称上原始交易的资源计数,然后插入这些值。

这绝不是绝对的防弹方法,但是我发现该技术确实非常有用,不仅用于增强唯一性,而且您可以使用类似的处理异常编号的方式来处理死锁,主键违例和其他错误的负载,所有这些都在T- SQL。

您可以在这两列上定义UNIQUE INDEXCONSTRAINT

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

确保值唯一是容易的:创建唯一约束 如果插入了唯一值,则MSSQL将引发异常,您可以在应用程序中进行恢复。

基于计数器(Proj1,Proj2等)创建唯一名称的过程要多一些。

注意,最好在Web层中缓解这种情况,在Web层中,您可以执行存在性检查并在尝试插入之前通知用户项目名称“已在使用中”。 而且,如果这不是一个选择,那么确保唯一性的方法比枚举计数要简单得多。 附加日期时间或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

我使用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

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM