Possible to implement a manual increment with just simple SQL INSERT?

后端 未结 11 1820
刺人心
刺人心 2021-01-05 11:54

I have a primary key that I don\'t want to auto increment (for various reasons) and so I\'m looking for a way to simply increment that field when I INSERT. By simply, I mean

相关标签:
11条回答
  • 2021-01-05 12:10

    How about creating a separate table to maintain the counter? It has better performance than MAX(id), as it will be O(1). MAX(id) is at best O(lgn) depending on the implementation.

    And then when you need to insert, simply lock the counter table for reading the counter and increment the counter. Then you can release the lock and insert to your table with the incremented counter value.

    0 讨论(0)
  • 2021-01-05 12:12

    Any critiques of this? Works for me.

    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
    
    0 讨论(0)
  • 2021-01-05 12:19

    It could be because there are no records so the sub query is returning NULL...try

    INSERT INTO tblTest(RecordID, Text) 
    VALUES ((SELECT ISNULL(MAX(RecordID), 0) + 1 FROM tblTest), 'asdf')
    
    0 讨论(0)
  • 2021-01-05 12:20

    If you're doing it in a trigger, you could make sure it's an "INSTEAD OF" trigger and do it in a couple of statements:

    DECLARE @next INT
    SET @next = (SELECT (MAX(id) + 1) FROM Table1)
    
    INSERT INTO Table1
    VALUES (@next, inserted.datablob)
    

    The only thing you'd have to be careful about is concurrency - if two rows are inserted at the same time, they could attempt to use the same value for @next, causing a conflict.

    Does this accomplish what you want?

    0 讨论(0)
  • 2021-01-05 12:21

    You understand that you will have collisions right?

    you need to do something like this and this might cause deadlocks so be very sure what you are trying to accomplish here

    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
    

    To explain the collision thing, I have provided some code

    first create this table and insert one row

    CREATE TABLE Table1(id int primary key not null, data_field char(100))
    GO
    Insert Table1 values(1,'[blob of data]')
    Go
    

    Now open up two query windows and run this at the same time

    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
    

    You will see a bunch of these

    Server: Msg 2627, Level 14, State 1, Line 7 Violation of PRIMARY KEY constraint 'PK__Table1__3213E83F2962141D'. Cannot insert duplicate key in object 'dbo.Table1'. The statement has been terminated.

    0 讨论(0)
  • 2021-01-05 12:25

    We have a similar situation where we needed to increment and could not have gaps in the numbers. (If you use an identity value and a transaction is rolled back, that number will not be inserted and you will have gaps because the identity value does not roll back.)

    We created a separate table for last number used and seeded it with 0.

    Our insert takes a few steps.

    --increment the number Update dbo.NumberTable set number = number + 1

    --find out what the incremented number is select @number = number from dbo.NumberTable

    --use the number insert into dbo.MyTable using the @number

    commit or rollback

    This causes simultaneous transactions to process in a single line as each concurrent transaction will wait because the NumberTable is locked. As soon as the waiting transaction gets the lock, it increments the current value and locks it from others. That current value is the last number used and if a transaction is rolled back, the NumberTable update is also rolled back so there are no gaps.

    Hope that helps.

    Another way to cause single file execution is to use a SQL application lock. We have used that approach for longer running processes like synchronizing data between systems so only one synchronizing process can run at a time.

    0 讨论(0)
提交回复
热议问题