如何仅阻止同一用户在 SQL Server 中进行插入 - 可序列化隔离
我有下表现
CREATE TABLE [dbo].[Test]([Id] [int] NOT NULL,[Name] [varchar](50) NOT NULL,
CONSTRAINT [PK_Test] PRIMARY KEY CLUSTERED
(
[Id] ASC
)
WITH (PAD_INDEX = OFF,
STATISTICS_NORECOMPUTE = OFF,
IGNORE_DUP_KEY=OFF,
ALLOW_ROW_LOCKS=ON,
ALLOW_PAGE_LOCKS = ON) ON [PRIMARY]
) ON [PRIMARY]
GO
CREATE NONCLUSTERED INDEX [IX_Test] ON [dbo].[Test]
(
[Name] ASC
)
WITH (PAD_INDEX = OFF,
STATISTICS_NORECOMPUTE = OFF,
SORT_IN_TEMPDB = OFF,
IGNORE_DUP_KEY = OFF,
DROP_EXISTING = OFF,
ONLINE = OFF,
ALLOW_ROW_LOCKS = ON,
ALLOW_PAGE_LOCKS = ON) ON [PRIMARY]
GO
在我想阻止来自同一用户(但不同会话)的插入,直到正在进行的会话完成。例如,如果用户 John 使用插入启动会话,我希望阻止他的其他会话(也使用插入),直到第一个会话完成。换句话说,我需要阻止尚不存在的插入。
因此,我创建了以下两个脚本来在单独的 Management studio 查询窗口中运行它们: 第一(用户 John;waitfor 在第一个查询中只是为了能够模拟问题):
begin tran
select COUNT(*)
from Test with(updlock,holdlock)
where Name = 'John'
waitfor delay '00:00:05'
insert into Test(Id,Name)
select isnull(MAX(Id) + 1,1),'John' from Test
commit tran
第二(用户 Peter):
begin tran
select COUNT(*)
from Test with(updlock,holdlock)
where Name = 'Peter'
insert into Test(Id,Name)
select isnull(MAX(Id) + 1,1),'Peter' from Test
commit tran
现在,如果我从第一个 Management Studio 查询窗口启动第一个查询,然后在第二个查询窗口中启动第二个查询第一个查询已完成,第二个查询将被阻止,直到第一个查询完成。 好吧,也许这就是应该如何工作,但这不是我想要的:我希望仅当同一用户想要插入数据时才阻止第二个会话。在我的示例中,John 开始插入数据,但 Peter(第二个查询)被阻止,直到 John 的插入完成。
是否可以指示 sql server(我想带有锁定提示)仅在同一用户尝试插入数据时阻止第二个查询?例如,如果 John 开始插入数据以仅阻止 John 的其他会话,而不阻止 Peter 的会话?
I have the following table
CREATE TABLE [dbo].[Test]([Id] [int] NOT NULL,[Name] [varchar](50) NOT NULL,
CONSTRAINT [PK_Test] PRIMARY KEY CLUSTERED
(
[Id] ASC
)
WITH (PAD_INDEX = OFF,
STATISTICS_NORECOMPUTE = OFF,
IGNORE_DUP_KEY=OFF,
ALLOW_ROW_LOCKS=ON,
ALLOW_PAGE_LOCKS = ON) ON [PRIMARY]
) ON [PRIMARY]
GO
CREATE NONCLUSTERED INDEX [IX_Test] ON [dbo].[Test]
(
[Name] ASC
)
WITH (PAD_INDEX = OFF,
STATISTICS_NORECOMPUTE = OFF,
SORT_IN_TEMPDB = OFF,
IGNORE_DUP_KEY = OFF,
DROP_EXISTING = OFF,
ONLINE = OFF,
ALLOW_ROW_LOCKS = ON,
ALLOW_PAGE_LOCKS = ON) ON [PRIMARY]
GO
Now I want to prevent inserts from the same user (but different session) until the session in progress has completed. For example, if user John starts the session with insert, I want his other session (with insert, too) to be blocked until the first session completes. In other words, I need to block inserts that do not yet exists.
So i created the following two scripts to run them in separate Management studio query windows:
First (user John; waitfor is in first query only to be able to simulate the problem):
begin tran
select COUNT(*)
from Test with(updlock,holdlock)
where Name = 'John'
waitfor delay '00:00:05'
insert into Test(Id,Name)
select isnull(MAX(Id) + 1,1),'John' from Test
commit tran
Second (user Peter):
begin tran
select COUNT(*)
from Test with(updlock,holdlock)
where Name = 'Peter'
insert into Test(Id,Name)
select isnull(MAX(Id) + 1,1),'Peter' from Test
commit tran
Now if I start first query from the first Management Studio query window and then start second query in the second query window before the first has completed, the second query is blocked until the first one completes.
Well, maybe this is how this is supposed to work, but it is not something I want: I want second session to be blocked only if the same user wants to insert data. In my example, John starts to insert data, but Peter (second query) is blocked until John's insert completes.
Is it possible to instruct sql server (with locking hints, I suppose) to block second query only if the same user tries to insert data? For example, if John starts to insert data to only block John's other sessions, but not Peter's sessions?
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(1)
在这种情况下,您可能需要在数据库中设置一个特定于用户的标志(使用 user_id 和 is_writing 标志创建表)。当开始向表中写入时,首先检查标志是否已设置,如果未设置,则更新标志并写入表。写入完成后取消设置标志。
这就是我可能会解决这个问题的方法。但我愿意接受任何其他方式;)
In this case you probably need to set up a flag in the DB which is user specific (create table with user_id and is_writting flag). When you start to write in the table first check whether the flag is set -when not update the flag and write to the table. After the write finished unset the flag.
This is how I would probably solve this. But I'm open to any other ways ;)