How do you check if IDENTITY_INSERT is set to ON or OFF in SQL Server?

前端 未结 8 964
暗喜
暗喜 2020-12-09 00:52

I\'ve searched for this, but threads in which it appeared tended to have answers from people who didn\'t understand the question.

Take the following syntax:

<
相关标签:
8条回答
  • 2020-12-09 01:07

    Since SET IDENTITY_INSERT is a session sensitive, it is managed in buffer level without storing somewhere. This means we do not need to check the IDENTITY_INSERT status as we never use this key word in current session.

    Sorry, no help for this.

    Great question though :)

    Source: Here

    Update There are ways maybe to do this, also seen in the site I linked, IMO, it is too much effort to be useful.

    if
    
    (select max(id) from MyTable) < (select max(id) from inserted)
    
    --Then you may be inserting a record normally
    
    BEGIN
        set @I = 1 --SQL wants something to happen in the "IF" side of an IF/ELSE
    END
    
    ELSE --You definitely have IDENTITY_INSERT on.  Done as ELSE instead of the other way around so that if there is no inserted table, it will run anyway
    
    
    BEGIN
    .... Code that shouldn't run with IDENTITY_INSERT on
    END
    
    0 讨论(0)
  • 2020-12-09 01:08

    Here is my solution. It is very similar to @jmoreno's answer.

    You would call it like this

    DECLARE @IdentityInsert VARCHAR(20)
    EXEC dbo.GetIdentityInsert 'YourDb', 'YourSchema', 'YourTable', @IdentityInsert OUT
    SELECT @IdentityInsert 
    

    This returns a 1-row recordset with column name IDENTITY_INSERT, that can be either ON, OFF, or NO_IDENTITY (if the given table doesn't have an identity column). It also sets the output parameter @IdentityInsert. So you can adjust the code to whichever method you prefer.

    It would be nice to get this into a user-defined function, but unfortunately I couldn't find a way to avoid the TRY..CATCH block, which you cannot use in user-defined functions.

    -- ================================================================================
    -- Check whether the table specified has its IDENTITY_INSERT set to ON or OFF.
    -- If the table does not have an identity column, NO_IDENTITY is returned.
    -- Tested on SQL 2008.
    -- ================================================================================
    CREATE PROCEDURE dbo.GetIdentityInsert
    
          @dbname sysname
        , @schemaname sysname
        , @table sysname
        , @IdentityInsert VARCHAR(20) OUTPUT
    
    AS
    
    BEGIN
    
        SET NOCOUNT ON
    
        DECLARE @OtherTable nvarchar(max)
        DECLARE @DbSchemaTable nvarchar(max)
    
        DECLARE @ErrorMessage NVARCHAR(4000);
        DECLARE @ErrorSeverity INT;
        DECLARE @ErrorState INT;
        DECLARE @ErrorNumber INT;
        DECLARE @object_id INT;
    
        SET @DbSchemaTable = @dbname + '.' + @schemaname + '.' + @table
    
        SET @object_id = OBJECT_ID(@DbSchemaTable)
        IF @object_id IS NULL
        BEGIN
            RAISERROR('table %s doesn''t exist', 16, 1, @DbSchemaTable)
            RETURN
        END
    
    
        BEGIN TRY
    
            SET @object_id = OBJECT_ID(@DbSchemaTable)
    
            IF OBJECTPROPERTY(@object_id,'TableHasIdentity') = 0
            BEGIN
                SET @IdentityInsert = 'NO_IDENTITY'
            END
            ELSE
            BEGIN
                -- Attempt to set IDENTITY_INSERT on a temp table. This will fail if any other table
                -- has IDENTITY_INSERT set to ON, and we'll process that in the CATCH
                CREATE TABLE #GetIdentityInsert(ID INT IDENTITY)
                SET IDENTITY_INSERT #GetIdentityInsert ON
                SET IDENTITY_INSERT #GetIdentityInsert OFF
                DROP TABLE #GetIdentityInsert
    
                -- It didn't fail, so IDENTITY_INSERT on @table must set to OFF
                SET @IdentityInsert = 'OFF'
            END
        END TRY
    
    
        BEGIN CATCH
    
            SELECT 
                @ErrorMessage = ERROR_MESSAGE(),
                @ErrorSeverity = ERROR_SEVERITY(),
                @ErrorState = ERROR_STATE(),
                @ErrorNumber = ERROR_NUMBER();
    
            IF @ErrorNumber = 8107  --IDENTITY_INSERT is already set on a table
            BEGIN
                SET @OtherTable = SUBSTRING(@ErrorMessage, CHARINDEX(char(39), @ErrorMessage)+1, 2000)
                SET @OtherTable = SUBSTRING(@OtherTable, 1, CHARINDEX(char(39), @OtherTable)-1)
    
                IF @OtherTable = @DbSchemaTable 
                BEGIN
                    -- If the table name is the same, then IDENTITY_INSERT on @table must be ON
                    SET @IdentityInsert = 'ON'
                END
                ELSE
                BEGIN
                    -- If the table name is different, then IDENTITY_INSERT on @table must be OFF
                    SET @IdentityInsert =  'OFF'
                END
            END
            ELSE
            BEGIN
                RAISERROR (@ErrorNumber, @ErrorMessage, @ErrorSeverity, @ErrorState);
                --THROW     Use this if SQL 2012 or higher
            END
    
        END CATCH
    
        SELECT [IDENTITY_INSERT] = @IdentityInsert
    END
    GO
    
    0 讨论(0)
  • 2020-12-09 01:15

    you can also use the ObjectProperty method to determine if a table has an identity:

    DECLARE @MyTableName nvarchar(200)
    SET @MyTableName = 'TestTable'
    SELECT CASE OBJECTPROPERTY(OBJECT_ID(@MyTableName), 'TableHasIdentity') 
    WHEN 1 THEN 'has identity' 
    ELSE 'no identity columns' 
    END as HasIdentity
    
    0 讨论(0)
  • 2020-12-09 01:15

    Very good question. I Have same issue. May be you can try to reset IDENTITY_INSERT using TRY/CATCH? For example, you make the job but not sure if the job is finished and IDENTITY_INSERT is set to OFF.

    Why you don't try:

    BEGIN TRY 
    ...
    END TRY 
    BEGIN CATCH
    SET IDENTITY_INSERT table OFF;
    END CATCH;
    

    Also I am not sure that this is working correctly but I see that adding only SET IDENTITY_INSERT ... OFF did not return error. So you can set just in case in the end SET IDENTITY_INSERT ... OFF.

    0 讨论(0)
  • 2020-12-09 01:19

    If you're attempting to turn off IDENTITY_INSERT for some other table to avoid getting an error when you want to set IDENTITY_INSERT on, the following may also work for you. As other have said on this thread IDENTITY_INSERT is a session setting with no direct visibility. However I made the interesting discovery that SET IDENTITY_INSERT OFF doesn't error out for any table that has an identity whether or not IDENTITY_INSERT is ON for that table. So it occurred to me that I could just call SET IDENTITY_INSERT ... OFF for every table with an identity in the database. It feels a bit like a brute force solution, but I found that the following dynamic SQL block did the trick very nicely.

    ---- make sure IDENTITY_INSERT is OFF ----
    DECLARE @cmd NVARCHAR(MAX)
    SET @cmd = CAST((SELECT 'SET IDENTITY_INSERT ' + 
                 QUOTENAME(OBJECT_SCHEMA_NAME(t.object_id)) + '.' + 
                 QUOTENAME(t.name) + ' OFF' + CHAR(10)
                 FROM sys.columns c 
                 JOIN sys.tables t ON t.object_id = c.object_id
                 WHERE c.is_identity = 1 
                 ORDER BY 1 FOR XML PATH('')) AS NVARCHAR(MAX))
    EXEC sp_executesql @cmd
    
    0 讨论(0)
  • 2020-12-09 01:23

    In summary:

    • Nathan's solution is the fastest:

      SELECT OBJECTPROPERTY(OBJECT_ID('MyTable'), 'TableHasIdentity');
      
      • when using an API wrapper, one can reduce the entire check to just checking for rows. For instance when using C#'s SqlDataReaders property HasRows and a query construct like:

        SELECT CASE OBJECTPROPERTY(OBJECT_ID('MyTable'), 'TableHasIdentity')
               WHEN 1 THEN '1' ELSE NULL END
        
    • Ricardo's solution allows more flexibility but requires the Column's identity name

      SELECT * FROM sys.columns WHERE object_id = OBJECT_ID('MyTable', 'U') 
                              AND name = 'MyTableIdentityColumnName';
      
    • Bogdan Bodanov solution, using try/catch would work as well, but additional checking should confine exception handling to cases of IDENTITY_INSERT is already ON for table 'MyTable'. Cannot perform SET operation for table 'MyTable';

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