题
我已经表[文件],有下列模式
CREATE TABLE [dbo].[File]
(
[FileID] [int] IDENTITY(1,1) NOT NULL,
[Name] [varchar](256) NOT NULL,
CONSTRAINT [PK_File] PRIMARY KEY CLUSTERED
(
[FileID] ASC
)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY]
) ON [PRIMARY]
这个想法是,FileID被用作关键为表和名称的完全合格的道路,表示文件。
我一直试图要做的是创建一个存储程序,将检查,看如果名字是已经在使用如果是这样,然后使用该记录的人创建一个新的记录。
但是,当我压力测试的代码有许多线程的执行存储的过程中,一旦我得到不同的错误。
这个代码版本将创建一个僵局,并把一个异常的僵局。
CREATE PROCEDURE [dbo].[File_Create]
@Name varchar(256)
AS
SET TRANSACTION ISOLATION LEVEL SERIALIZABLE
BEGIN TRANSACTION xact_File_Create
SET XACT_ABORT ON
SET NOCOUNT ON
DECLARE @FileID int
SELECT @FileID = [FileID] FROM [dbo].[File] WHERE [Name] = @Name
IF @@ROWCOUNT=0
BEGIN
INSERT INTO [dbo].[File]([Name])
VALUES (@Name)
SELECT @FileID = [FileID] FROM [dbo].[File] WHERE [Name] = @Name
END
SELECT * FROM [dbo].[File]
WHERE [FileID] = @FileID
COMMIT TRANSACTION xact_File_Create
GO
这个版本代码我最终得到行使用相同的数据在名称一栏。
CREATE PROCEDURE [dbo].[File_Create]
@Name varchar(256)
AS
BEGIN TRANSACTION xact_File_Create
SET NOCOUNT ON
DECLARE @FileID int
SELECT @FileID = [FileID] FROM [dbo].[File] WHERE [Name] = @Name
IF @@ROWCOUNT=0
BEGIN
INSERT INTO [dbo].[File]([Name])
VALUES (@Name)
SELECT @FileID = [FileID] FROM [dbo].[File] WHERE [Name] = @Name
END
SELECT * FROM [dbo].[File]
WHERE [FileID] = @FileID
COMMIT TRANSACTION xact_File_Create
GO
我想知道什么是正确的方式做到这一类型的行动?一般来说,这是一个图案我想利用其中所列数据是唯一在任何一个单列或多个列和另一列被用作关键。
感谢
解决方案
如果你正在寻找大量的名字,你可能会想它的索引(作为独特的,也许甚至聚集如果是这样的 主 搜索场)。你不用@FileID从第一个选择,我会选择最(*)从文件where Name=@的姓名,看看它是否大于零(这将防止SQL从保留的任何锁在桌从搜索阶段,没有列选择)。
你是在正确的道路上的序列化水平,因为你的行动会影响的后续查询的成功或失败的名称。原因的版本没有设定会导致重复是两个选择跑的同时,发现没有记录,所以两者都继续插入(其创建的重复).
僵局,与前一个版本是最有可能是由于缺乏一个索引制作的搜索过程需要很长的时间。当你加载服务器下一个序列化事务,其他一切都将必须等待的操作来完成。索引 应该 动作快,但只是测试将显示,如果是速度不够快。注意,你可以回应失败的事务通过重新提交:在真实世界的情况,希望负荷,将是暂时的。
编辑:通过使你的表编制索引,但不是使用序列化的,你结束了三种情况:
- 名字是找到,身份证被捕获并使用。 常见的
- 名字是找不到,插入如预期的那样。 常见的
- 名字是找不到插入失败,因为另一种确切匹配被张贴在毫秒的第一个。 非常罕见的
我希望这最后一种情况是真正特殊的,因此使用的一个例外捕获的这种非常罕见的情况下,最好是接序列化,它具有性能严重的后果。
如果你真的有一个期望,这将是常见的职位毫秒的另一个同样 新的 姓名、然后用一个序列化事务结合的指数。它会慢的,在一般情况,但是速度更快,当这些员额被发现。
其他提示
首先,创建名称列的唯一索引。然后从客户端代码首先检查是否存在通过选择并写到FileID把名称在where子句中的名称 - 如果有的话,使用写到FileID。如果没有,插入一个新的。
使用exists函数可能干净的东西了一点。
if (Exists(select * from table_name where column_name = @param)
begin
//use existing file name
end
else
//use new file name