简体   繁体   中英

Insert same MAX value in multiple rows at a time in SQL?

I have a procedure that inserts n rows at a time. Each time it should pick up the MAX(BatchNo) and insert it along with other columns.

Select @maxbatchno = MAX(batchno) from table1;

insert into table1 (name,phone,batchno) 
select name,phone, maxbatchno from #temp;

Wouldn't there be concurrency issues with the above query? If there are multiple users trying to run this procedure wouldn't there be duplicates?

I think you are mixing entities. You have a "batch" entity, but it is hidden. If you have a table for that and use the table, you won't have a problem.

I actually assume that you want a NEW batch number, which would be larger than the previous value. Your version is just re-using the previous value, over and over. So:

create tables batches (
    batchId int identity(1, 1) primary key,
    createdAt datetime default getdate()
    -- other columns here could be useful
);

create table @ids (batchId int);

insert into batches
    output inserted.batchId into @ids
    default values;

insert into table1 (name, phone, batchno) 
    select t.name, t.phone, i.batchId
    from #temp t cross join
         @ids i;

With this approach, you don't have to worry about concurrency issues.

Note: You could use a sequence instead. But I think that "batch" is a first-class entity and should have its own table.

(Talking from experience with similar scenarios here)

If you have a demo coming up very soon I would just go with:

SET TRANSACTION ISOLATION LEVEL SERIALIZABLE;  
BEGIN TRAN;

-- do your stuff here but don't forget the overall error handling...

COMMIT

However, as soon as you have the time - perhaps even after this demo - I highly recommend doing some stress testing (multi-threaded!) which incorporate comparing this SET TRANSACTION ISOLATION LEVEL SERIALIZABLE; approach with the results from the more granular sp_getapplock approach, which can be used if you absolutely want to make sure the inserts are processed single-threaded and your expected load is going to be very high. If your expected load won't be that high I'd just go with the first option, but I would be very interested to see the test results in terms of usability and performance. Here a sp_getapplock example:

DECLARE @result INT;

EXEC @result = sp_getapplock
  @Resource = 'MySessionsNameForThisApplock'
, @LockMode = 'Exclusive'
, @LockOwner = 'Session';

IF @result < 0
BEGIN
  RAISERROR('Could not get applock - MySessionsNameForThisApplock.', 16, 1);
END;
ELSE
BEGIN
  -- do your stuff here but don't forget the overall error handling...

  EXEC @result = sp_releaseapplock
    @Resource = 'MySessionsNameForThisApplock'
  , @LockOwner = 'Session';
END;

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

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