我有一个主键,我不想自动递增(由于各种原因),所以我正在寻找一种方法来简单地在我INSERT时增加该字段。简单地说,我的意思是没有存储过程和没有触发器,所以只需要一系列SQL命令(最好是一个命令)。
这是我到目前为止所尝试的内容:
BEGIN TRAN
INSERT INTO Table1(id, data_field)
VALUES ( (SELECT (MAX(id) + 1) FROM Table1), '[blob of data]');
COMMIT TRAN;
* Data abstracted to use generic names and identifiers
然而,执行时,命令错误,说
“此处不允许使用子查询 上下文。只有标量表达式 允许“
那么,我怎么能这样做/我做错了什么?
编辑:因为它被指出是一个考虑因素,所以要插入的表格已经保证至少有一行。
答案 0 :(得分:10)
你明白你会发生碰撞吗?
你需要做这样的事情,这可能会导致死锁,所以要非常肯定你要在这里完成什么
DECLARE @id int
BEGIN TRAN
SELECT @id = MAX(id) + 1 FROM Table1 WITH (UPDLOCK, HOLDLOCK)
INSERT INTO Table1(id, data_field)
VALUES (@id ,'[blob of data]')
COMMIT TRAN
为了解释碰撞事项,我提供了一些代码
首先创建此表并插入一行
CREATE TABLE Table1(id int primary key not null, data_field char(100))
GO
Insert Table1 values(1,'[blob of data]')
Go
现在打开两个查询窗口并同时运行
declare @i int
set @i =1
while @i < 10000
begin
BEGIN TRAN
INSERT INTO Table1(id, data_field)
SELECT MAX(id) + 1, '[blob of data]' FROM Table1
COMMIT TRAN;
set @i =@i + 1
end
你会看到一堆这些
服务器:Msg 2627,Level 14,State 1,Line 7 违反PRIMARY KEY约束'PK__Table1__3213E83F2962141D'。无法在对象'dbo.Table1'中插入重复键。 声明已经终止。
答案 1 :(得分:2)
请改为尝试:
INSERT INTO Table1 (id, data_field)
SELECT id, '[blob of data]' FROM (SELECT MAX(id) + 1 as id FROM Table1) tbl
我不建议这样做有多种原因(性能,交易安全等)
答案 2 :(得分:1)
可能是因为没有记录所以子查询返回NULL ...尝试
INSERT INTO tblTest(RecordID, Text)
VALUES ((SELECT ISNULL(MAX(RecordID), 0) + 1 FROM tblTest), 'asdf')
答案 3 :(得分:1)
我不知道是否有人仍在寻找答案,但这是一个似乎有效的解决方案:
-- Preparation: execute only once
CREATE TABLE Test (Value int)
CREATE TABLE Lock (LockID uniqueidentifier)
INSERT INTO Lock SELECT NEWID()
-- Real insert
BEGIN TRAN LockTran
-- Lock an object to block simultaneous calls.
UPDATE Lock WITH(TABLOCK)
SET LockID = LockID
INSERT INTO Test
SELECT ISNULL(MAX(T.Value), 0) + 1
FROM Test T
COMMIT TRAN LockTran
答案 4 :(得分:1)
我们有类似的情况,我们需要增加,并且不能在数字上有差距。 (如果您使用标识值并回滚事务,则不会插入该数字,并且您将有间隙,因为标识值不会回滚。)
我们为最后使用的号码创建了一个单独的表格,并将其播种为0。
我们的插入需要几个步骤。
- 增加数字 更新dbo.NumberTable set number = number + 1
- 找出增加的数字是什么 选择@number =数字 来自dbo.NumberTable
- 使用号码 使用@number
插入到dbo.MyTable中提交或回滚
这导致同时事务在一行中处理,因为每个并发事务将等待,因为NumberTable被锁定。一旦等待事务获得锁定,它就会递增当前值并将其锁定。当前值是最后使用的数字,如果回滚事务,NumberTable更新也会回滚,因此没有间隙。
希望有所帮助。
导致单个文件执行的另一种方法是使用SQL应用程序锁。我们已经将这种方法用于更长时间运行的过程,例如在系统之间同步数据,因此一次只能运行一个同步过程。
答案 5 :(得分:0)
如果您在触发器中执行此操作,则可以确保它是“INSTEAD OF”触发器,并在几个语句中执行:
DECLARE @next INT
SET @next = (SELECT (MAX(id) + 1) FROM Table1)
INSERT INTO Table1
VALUES (@next, inserted.datablob)
你唯一需要注意的是并发 - 如果同时插入两行,他们可能会尝试为@next使用相同的值,从而导致冲突。
这会达到你想要的效果吗?
答案 6 :(得分:0)
declare @nextId int
set @nextId = (select MAX(id)+1 from Table1)
insert into Table1(id, data_field) values (@nextId, '[blob of data]')
commit;
但也许更好的方法是使用标量函数getNextId('table1')
答案 7 :(得分:0)
做这种没有IDENTITY(自动增量)列的事情似乎很奇怪,这让我对架构本身提出质疑。我的意思是,严肃地说,这是IDENTITY专栏的完美情况。如果你解释这个决定背后的原因,它可能会帮助我们回答你的问题。 =)
话虽如此,有些选择是:
抱歉漫无边际。希望有所帮助。
答案 8 :(得分:0)
对此有何批评?适合我。
DECLARE @m_NewRequestID INT
, @m_IsError BIT = 1
, @m_CatchEndless INT = 0
WHILE @m_IsError = 1
BEGIN TRY
SELECT @m_NewRequestID = (SELECT ISNULL(MAX(RequestID), 0) + 1 FROM Requests)
INSERT INTO Requests ( RequestID
, RequestName
, Customer
, Comment
, CreatedFromApplication)
SELECT RequestID = @m_NewRequestID
, RequestName = dbo.ufGetNextAvailableRequestName(PatternName)
, Customer = @Customer
, Comment = [Description]
, CreatedFromApplication = @CreatedFromApplication
FROM RequestPatterns
WHERE PatternID = @PatternID
SET @m_IsError = 0
END TRY
BEGIN CATCH
SET @m_IsError = 1
SET @m_CatchEndless = @m_CatchEndless + 1
IF @m_CatchEndless > 1000
THROW 51000, '[upCreateRequestFromPattern]: Unable to get new RequestID', 1
END CATCH
答案 9 :(得分:0)
如何创建一个单独的表来维护计数器?它的性能将比MAX(id)
好,因为它将是O(1)。 MAX(id)
最多为O(lgn),具体取决于实现方式。
然后,当您需要插入时,只需锁定计数器表以读取计数器并递增计数器。然后,您可以释放锁并使用递增的计数器值插入到表中。
答案 10 :(得分:0)
有一个单独的表格,您可以在其中保存最新的 ID,并为每笔交易获取一个新的 ID。 它可能有点慢,但应该可以工作。
DECLARE @NEWID INT
BEGIN TRAN
UPDATE TABLE SET ID=ID+1
SELECT @NEWID=ID FROM TABLE
COMMIT TRAN
PRINT @NEWID -- Do what you want with your new ID
答案 11 :(得分:0)
没有任何事务范围的代码(我在我的工程师课程中使用它作为练习):
-- Preparation: execute only once
CREATE TABLE increment (val int);
INSERT INTO increment VALUES (1);
-- Real insert
DECLARE @newIncrement INT;
UPDATE increment
SET @newIncrement = val,
val = val + 1;
INSERT INTO Table1 (id, data_field)
SELECT @newIncrement, 'some data';
答案 12 :(得分:-1)
这应该有效:
INSERT INTO Table1 (id, data_field)
SELECT (SELECT (MAX(id) + 1) FROM Table1), '[blob of data]';
或者这个(替代其他平台的LIMIT):
INSERT INTO Table1 (id, data_field)
SELECT TOP 1
MAX(id) + 1, '[blob of data]'
FROM
Table1
ORDER BY
[id] DESC;