是否有任何替代方法可以为标识列插入显式值,而不是设置IDENTITY_INSERT ON

时间:2016-11-10 07:04:13

标签: sql-server

我有两张桌子。两者都有相同的列名。为了扩展目的,我需要将数据从一个表迁移到另一个表。我写了以下查询:

INSERT INTO Table1(primaryKey,column1, column2)
SELECT * 
FROM Table2
WHERE (<condition>)

导致错误:

  

当IDENTITY_INSERT设置为OFF时,无法在表'Table1'中为identity列插入显式值

当我谷歌时,所有人都提到使用

SET IDENTITY_INSERT [spider3].[Table1] ON 

但是有一些缺点。即使此功能仅对连接有效,我们也无法将IDENTITY_INSERT内容作为ON用于多个表。所以我在寻找替代品。

有人可以提出替代解决方案吗?我可以在查询中使用'as'关键字来解决此问题吗?如果是这样的话?

1 个答案:

答案 0 :(得分:0)

这些是一些插入语句,用于计算身份插入的可能性。这些可以批量运行,并将动态执行IDENTITY_INSERT。我通常使用脚本来生成这些语句。

IF (OBJECTPROPERTY(OBJECT_ID('[Production].[ScrapReason]'), 'TableHasIdentity') = 1) SET IDENTITY_INSERT [Production].[ScrapReason] ON; INSERT INTO [AdventureWorks2014].[Production].[ScrapReason] ([ScrapReasonID], [Name], [ModifiedDate]) SELECT * FROM [AdventureWorks2014].[Production].[ScrapReason]; IF (OBJECTPROPERTY(OBJECT_ID('[Production].[ScrapReason]'), 'TableHasIdentity') = 1) SET IDENTITY_INSERT [Production].[ScrapReason] OFF; 
IF (OBJECTPROPERTY(OBJECT_ID('[HumanResources].[Shift]'), 'TableHasIdentity') = 1) SET IDENTITY_INSERT [HumanResources].[Shift] ON; INSERT INTO [AdventureWorks2014].[HumanResources].[Shift] ([ShiftID], [Name], [StartTime], [EndTime], [ModifiedDate]) SELECT * FROM [AdventureWorks2014].[HumanResources].[Shift]; IF (OBJECTPROPERTY(OBJECT_ID('[HumanResources].[Shift]'), 'TableHasIdentity') = 1) SET IDENTITY_INSERT [HumanResources].[Shift] OFF; 
IF (OBJECTPROPERTY(OBJECT_ID('[Production].[ProductCategory]'), 'TableHasIdentity') = 1) SET IDENTITY_INSERT [Production].[ProductCategory] ON; INSERT INTO [AdventureWorks2014].[Production].[ProductCategory] ([ProductCategoryID], [Name], [rowguid], [ModifiedDate]) SELECT * FROM [AdventureWorks2014].[Production].[ProductCategory]; IF (OBJECTPROPERTY(OBJECT_ID('[Production].[ProductCategory]'), 'TableHasIdentity') = 1) SET IDENTITY_INSERT [Production].[ProductCategory] OFF; 

在这种情况下,select和insert都发生在[AdventureWorks2014]中,显然通常不是这种情况,但我目前只有一个DB可用来演示。如果您对如何生成这些内容感兴趣,请参阅下文。

DECLARE @v_SourceDatabase      NVARCHAR(250); 
DECLARE @v_DestinationDatabase NVARCHAR(250);
DECLARE @t_TablesToProcess     TABLE (FullTableName NVARCHAR(250),
                                      TableName     NVARCHAR(250),
                                      TableSchema   NVARCHAR(250),
                                      ColumnList    NVARCHAR(MAX));
DECLARE @v_ColumnList         NVARCHAR(MAX);
DECLARE @v_TableName          NVARCHAR(250);
DECLARE @v_TableSchema        NVARCHAR(250);

-- -------------------------------------- --
-- Set Source & destination database here --
-- -------------------------------------- --

SET @v_SourceDatabase      = N'AdventureWorks2014';
SET @v_DestinationDatabase = N'AdventureWorks2014';

-- Generate tables to process
INSERT INTO @t_TablesToProcess
SELECT CONCAT(QUOTENAME(syssch.name), '.', QUOTENAME(systab.name)) FullTableName
     , systab.name
     , syssch.name
     , NULL
FROM   sys.tables systab
INNER JOIN sys.schemas syssch
  ON syssch.schema_id = systab.schema_id
WHERE type = 'U';

DECLARE TableList CURSOR FAST_FORWARD FOR
  SELECT TableName, TableSchema
  FROM @t_TablesToProcess;

OPEN TableList;
FETCH NEXT FROM TableList INTO @v_TableName, @v_TableSchema;

WHILE @@FETCH_STATUS = 0
BEGIN
  SELECT @v_ColumnList = CONCAT(@v_ColumnList, QUOTENAME(column_name), ', ')
  FROM INFORMATION_SCHEMA.COLUMNS 
  WHERE TABLE_NAME = @v_TableName
    AND TABLE_SCHEMA = @v_TableSchema

  SET @v_ColumnList = LEFT(@v_ColumnList, LEN(@v_ColumnList) - 1);

  UPDATE @t_TablesToProcess
  SET ColumnList = @v_ColumnList
  WHERE TableName = @v_TableName
    AND TableSchema = @v_TableSchema;

  SET @v_ColumnList = '';

    FETCH NEXT FROM TableList INTO @v_TableName, @v_TableSchema;
END 

CLOSE TableList;
DEALLOCATE TableList;

-- Generate insert statements
SELECT CONCAT(N'IF (OBJECTPROPERTY(OBJECT_ID(''', FullTableName, '''), ''TableHasIdentity'') = 1) SET IDENTITY_INSERT ', FullTableName, N' ON; '
            , N'INSERT INTO ', QUOTENAME(@v_DestinationDatabase), '.', FullTableName
            , N' (', ColumnList, N')'
            , N' SELECT * FROM ', QUOTENAME(@v_SourceDatabase), N'.', FullTableName, N';'
            , N' IF (OBJECTPROPERTY(OBJECT_ID(''', FullTableName, '''), ''TableHasIdentity'') = 1) SET IDENTITY_INSERT ', FullTableName, N' OFF; ')
FROM @t_TablesToProcess;

小注释:编写此脚本时不会通过任何实际测试。有些事情可以明显改善。我处理列名称构建的方式不太理想。使用XML PATH&amp; STUFF是更好的选择:

SELECT Chars = STUFF((
    SELECT ', ' + [Char]
    FROM dbo.Chars
FOR XML PATH(''), TYPE).value('.', 'VARCHAR(MAX)'), 1, 2, '') 

这是一个可能的方向推动。如果我找到时间,我可能会将其重新编写为更加傻瓜/通用的解决方案。

关于在不使用IDENTITY_INSERT的情况下进行身份插入的另一种方法,我怀疑是否存在。