如何检查SQL Server中IDENTITY_INSERT是否设置为ON或OFF?


75

我已经搜索过此内容,但是出现该内容的主题往往会得到不了解该问题的人的解答。

采用以下语法:

SET IDENTITY_INSERT Table1 ON

您如何做更多这样的事情:

GET IDENTITY_INSERT Table1

我不想对数据库中的数据或设置做任何事情来获取此信息。谢谢!


4
有趣的是,您提到人们误解了这个问题,而这里的绝大多数答案正是这样做的。
Fishcake

3
对于您和其他人来说,这才是真正的问题: 会话。您可能只想检查/恢复IDENTITY_INSERT的值,该会话的时间既长又复杂,足以允许并稍后阻止标识插入。(连续运行的作业步骤吗?)因为如果开始新的会话,IDENTITY_INSERT将关闭!如果您不清楚某个会话是否保持相同的会话,Google会话(不完全是=连接),请查看sys.dm_exec_sessionssys.dm_exec_connections,或下载sp_WhoIsActiveEXEC sp_WhoIsActive @show_sleeping_spids = 2, @show_own_spid = 1
Doug_Ivison

Answers:


37

由于SET IDENTITY_INSERT是对会话敏感的,因此可以在缓冲区级别对其进行管理,而无需存储在任何地方。这意味着我们不需要检查IDENTITY_INSERT状态,因为我们在当前会话中从未使用过此关键字。

抱歉,对此没有帮助。

虽然是个好问题:)

资料来源:这里

更新 也许有一些方法可以做到这一点,在我链接的网站IMO中也可以看到,这太费力了。

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

谢谢。我刚刚得知它存在于昨天,所以我不确定如何应用您所说的内容。基本上,我们在两个不同的服务器上有两个数据库,关闭其中一个数据库并重新启动它将是一个问题。如果数据库服务无限期地运行,那么您引用的会话是否持续了几分钟以上?基本上,我在其中一个数据库上设置了几次该值,而无需事先做笔记,并且我需要确保这样的任何更改都可以回滚。
Panzercrisis

1
请注意,这不是我自己的答案,而是从我的源代码中的social.msdn.microsoft.com/Forums/en/transactsql/thread/…(正式的Microsoft论坛,有成千上万的书签)中获得。这些服务器上的性能。显然,如果服务器运行缓慢/有关SQL Server的服务正在关闭,则此代码(此处为伪代码)将无法正常工作。为什么您确实需要知道表的身份状态?如果服务无限期运行,则此会话不应持续很长时间(取决于服务器规格和表大小)...
2012年

我只是想将数据库重置为开始弄乱IDENTITY_INSERT值之前的状态。
Panzercrisis

1
msdn.microsoft.com/zh-cn/library/aa259221(v=sql.80).aspx很好地解释了什么是Identity_Insert。告诉我,当服务器重新启动时,您的数据库是否损坏或发生了什么?如果是,为什么不尝试从备份中还原它?我祈祷你有备份:O
Eon 2012年

看起来一切都很好。我只是不熟悉SQL的更多技术方面,所以这就是我遇到的一些困难。谢谢。
Panzercrisis'5

22

综上所述:

  • Nathan的解决方案是最快的:

    SELECT OBJECTPROPERTY(OBJECT_ID('MyTable'), 'TableHasIdentity');
    
    • 使用API​​包装器时,可以将整个检查减少为仅检查行。例如,当使用C#的SqlDataReaders 属性HasRows和类似以下的查询构造时:

      SELECT CASE OBJECTPROPERTY(OBJECT_ID('MyTable'), 'TableHasIdentity')
             WHEN 1 THEN '1' ELSE NULL END
      
  • 里卡多的解决方案具有更大的灵活性,但需要列的标识名称

    SELECT * FROM sys.columns WHERE object_id = OBJECT_ID('MyTable', 'U') 
                            AND name = 'MyTableIdentityColumnName';
    
  • 使用try/的Bogdan Bodanov解决方案catch也可以正常工作,但其他检查应将异常处理限制在以下情况下: IDENTITY_INSERT is already ON for table 'MyTable'. Cannot perform SET operation for table 'MyTable';


7

您可以使用下面的代码发现是否启用了identity_insert,以及是否对哪个表启用了。

declare @tableWithIdentity varchar(max) = '';
SET IDENTITY_INSERT ExampleTable ON

begin try
  create table #identityCheck (id int identity(1,1))
  SET IDENTITY_INSERT #identityCheck ON
  drop table #identityCheck
end try
begin catch
  declare @msg varchar(max) = error_message()
  set @tableWithIdentity= @msg;
  set @tableWithIdentity = 
  SUBSTRING(@tableWithIdentity,charindex('''',@tableWithIdentity,1)+1, 10000)

  set @tableWithIdentity = SUBSTRING(@tableWithIdentity,1, charindex('''',@tableWithIdentity,1)-1)

  print @msg;
  drop table #identityCheck
end catch

if @tableWithIdentity<>''
begin
  print ('Name of table with Identity_Insert set to ON: ' + @tableWithIdentity)
end
else
begin
  print 'No table currently has Identity Insert Set to ON'
end 

1
@Graeme:谢谢,我认为问题的实际答案可能有用,这是我知道的唯一方法。
jmoreno '18

6

如果您想关闭其他表的IDENTITY_INSERT,以避免在想要将IDENTITY_INSERT设置为on时收到错误,则以下内容也可能对您有用。正如其他人在该线程上所说的那样,IDENTITY_INSERT是一个没有直接可见性的会话设置。但是,我做了一个有趣的发现,对于具有身份的任何表,无论该表的IDENTITY_INSERT是否为ON,SET IDENTITY_INSERT OFF都不会出错。因此,我想到可以对数据库中具有身份的每个表都调用SET IDENTITY_INSERT ... OFF。感觉有点像蛮力解决方案,但是我发现下面的动态SQL块很好地解决了问题。

---- 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

蛮力的解决方案。我很谦虚地看不到在哪些情况下既必要又安全。如果IDENTITY_INSERT在调用脚本中(或之前)调用了set on,则肯定是有原因的。:p假定呼叫者此后不再继续依赖该信息是错误的,并且可能有危险。在设计上明智的做法是,最好的恕我直言,只要尝试将其设置为任意状态,并在必要时让整个设备崩溃并燃烧即可。至少开发人员将了解发生了什么问题-假设错误记录在某处。
克罗诺(Crono)

话虽如此,我不会投反对票,因为我仍然认为您的答案可能对某个人在应用程序中解决数据库相关问题很有帮助。但这确实是我能想到的唯一方案。当然,请随时告诉我我是否缺少某些东西。:)
克罗诺(Crono)

2

如果您想了解会话变量...是个好问题,但是我看不到该信息在哪里有用。在正常执行中检查对插入的正常表响应时,这应该起作用!

-如果只想知道给定表上是否存在标识插入:

select is_identity
from sys.columns
where object_id = OBJECT_ID('MyTable', 'U') and name = 'column_Name'

-或...如果要根据结果执行某些操作,请使用此命令:

if exists (select *
from sys.columns
where object_id = OBJECT_ID('MyTable', 'U') and is_identity = 1)
... your code considering identity insert
else
... code that should not run with identity insert

玩得开心!


8
这遗漏了一点,问题是关于IDENTITY_INSERT表是否具有身份列是否在上
Fishcake

2
is_identity在列sys.columns表确定该列是否是标识列或否,变量不是否IDENTITY_INSERTONOFF
javiniar.leonard

1

很好的问题。我有同样的问题。也许您可以尝试使用TRY / CATCH重设IDENTITY_INSERT?例如,您进行作业,但不确定该作业是否已完成并且IDENTITY_INSERT设置为OFF。

为什么不尝试:

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

另外,我不确定这是否正常工作,但是我发现仅添加操作SET IDENTITY_INSERT ... OFF不会返回错误。因此,您可以设置以防万一SET IDENTITY_INSERT ... OFF


0

您还可以使用ObjectProperty方法确定表是否具有标识:

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

朋友,谢谢。那就是我一直在寻找的地方
chenio '19

1
抱歉,这个问题无法解决。这将检查TableHasIdentity表中定义的值的状态,而不是IDENTITY_INSERT操作正在寻找的实际会话存储值。自己尝试:有一个查询来检查属性,然后更改IDENTITY_INSERT表的值,然后再次获取对象属性。其值仍将与以前相同。
克罗诺(Crono)

0

这是我的解决方案。它与@jmoreno的答案非常相似。

你会这样称呼它

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

这将返回具有列名IDENTITY_INSERT的1行记录集,该记录集可以为ON,OFF或NO_IDENTITY(如果给定的表没有标识列)。它还设置输出参数@IdentityInsert。因此,您可以将代码调整为您喜欢的任何一种方法。

最好将它放入用户定义的函数中,但是不幸的是,我找不到避免TRY..CATCH块的方法,而您不能在用户定义的函数中使用它。

-- ================================================================================
-- 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
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.