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

后端 未结 11 1821
刺人心
刺人心 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:26

    This should work:

    INSERT INTO Table1 (id, data_field)
    SELECT (SELECT (MAX(id) + 1) FROM Table1), '[blob of data]';
    

    Or this (substitute LIMIT for other platforms):

    INSERT INTO Table1 (id, data_field)
    SELECT TOP 1
        MAX(id) + 1, '[blob of data]'
    FROM
       Table1
    ORDER BY
       [id] DESC;
    
    0 讨论(0)
  • 2021-01-05 12:28

    I don't know if somebody is still looking for an answer but here is a solution that seems to work:

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

    Try this instead:

    INSERT INTO Table1 (id, data_field)
    SELECT id, '[blob of data]' FROM (SELECT MAX(id) + 1 as id FROM Table1) tbl
    

    I wouldn't recommend doing it that way for any number of reasons though (performance, transaction safety, etc)

    0 讨论(0)
  • 2021-01-05 12:29
    declare @nextId int
    set @nextId = (select MAX(id)+1 from Table1)
    
    insert into Table1(id, data_field) values (@nextId, '[blob of data]')
    
    commit;
    

    But perhaps a better approach would be using a scalar function getNextId('table1')

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

    It seems very odd to do this sort of thing w/o an IDENTITY (auto-increment) column, making me question the architecture itself. I mean, seriously, this is the perfect situation for an IDENTITY column. It might help us answer your question if you'd explain the reasoning behind this decision. =)

    Having said that, some options are:

    • using an INSTEAD OF trigger for this purpose. So, you'd do your INSERT (the INSERT statement would not need to pass in an ID). The trigger code would handle inserting the appropriate ID. You'd need to use the WITH (UPDLOCK, HOLDLOCK) syntax used by another answerer to hold the lock for the duration of the trigger (which is implicitly wrapped in a transaction) & to elevate the lock type from "shared" to "update" lock (IIRC).
    • you can use the idea above, but have a table whose purpose is to store the last, max value inserted into the table. So, once the table is set up, you would no longer have to do a SELECT MAX(ID) every time. You'd simply increment the value in the table. This is safe provided that you use appropriate locking (as discussed). Again, that avoids repeated table scans every time you INSERT.
    • use GUIDs instead of IDs. It's much easier to merge tables across databases, since the GUIDs will always be unique (whereas records across databases will have conflicting integer IDs). To avoid page splitting, sequential GUIDs can be used. This is only beneficial if you might need to do database merging.
    • Use a stored proc in lieu of the trigger approach (since triggers are to be avoided, for some reason). You'd still have the locking issue (and the performance problems that can arise). But sprocs are preferred over dynamic SQL (in the context of applications), and are often much more performant.

    Sorry about rambling. Hope that helps.

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