[英]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 INDEX或CONSTRAINT :
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.