2012-09-12 19 views
15

Quiero usar un procedimiento almacenado para copiar una tabla de mi base de datos de prueba a un servidor vinculado con la misma ID/Identidad pero no puedo hacer que funcione ... He configurado el IDENTITY_INSERT al ON pero todavía se queja de la columna de ID.La inserción de identidad en el servidor vinculado falla

Aquí está mi procedimiento:

CREATE PROCEDURE [dbo].[TEST2PROD_CopyUIDataSServer] 
AS Begin 
declare @sql nvarchar(max) 
-- First truncate target table 
set @sql = 'EXEC [LINKEDSERVER].tempdb.sys.sp_sqlexec' + char(39)+ 'TRUNCATE Table [ProductManager].dbo.[UIData]' + char(39)+ ';' 
---- SET IDENTITY_INSERT ON 
set @sql = @sql + 'EXEC [LINKEDSERVER].tempdb.sys.sp_sqlexec' + char(39)+ 'SET IDENTITY_INSERT [ProductManager].[dbo].[UIData] ON' + char(39)+ ';' 
---- INSERT UIDATA records from DB1 into linked server DB2 
set @sql = @sql + 'WITH TestData as (SELECT * from ProductManager.dbo.UIData UID)' + NCHAR(13)+ 'INSERT INTO [LINKEDSERVER].[ProductManager].[dbo].[UIData]' + NCHAR(13) + 'select * from TestData;' 
print @sql 
exec (@sql) 
end 

Pero cuando ejecuto el SP que me da el siguiente error:

The OLE DB provider "SQLNCLI10" for linked server .... could not INSERT INTO table "[LINKEDSERVER].[ProductManager].[dbo].[UIData]" because of column "Id". The user did not have permission to write to the column.

Vinculados propiedades de servidor de RPC y RPC a cabo se establece en true. Espero que alguien me ayude aquí.

ACTUALIZACIÓN: decidí a tirar las cosas aparte, primero copiar los datos desde el servidor local al servidor vinculado en un TEMP_TABLE donde no tengo que lidiar con IDENTITY cuestiones.

Luego escribí un procedimiento almacenado en el servidor vinculado/remoto, ya que no estoy usando SELECT *, pero especifico la lista de columnas. Lo más probable es que esto funcionará desde el servidor local en un SP también, pero no tienen el tiempo o interés para comprobar que funciona todavía ..

USE [ProductManager] 
GO 
SET ANSI_NULLS ON 
GO 
SET QUOTED_IDENTIFIER ON 
GO 
CREATE PROCEDURE [dbo].[TEST2PROD_CopyBaseTables] 
AS BEGIN 
DECLARE @DestTable VARCHAR(50) 
DECLARE @DestPath VARCHAR(50) 
DECLARE @SrceTable VARCHAR(255) 
declare @sql nvarchar(max) 
DECLARE @columnList varchar(max) 
DECLARE @err int 

Begin TRY 

declare @comma_delimited_list varchar(4000) 
--- FIRST TRY WITH ONE TABLE, EXTENDABLE... 
set @comma_delimited_list = 'UIData' 

declare @cursor cursor 
set @cursor = cursor static for 
    select * from dbo.Split(@comma_delimited_list,',') a 

declare @naam varchar(50) 
open @cursor 
while 1=1 begin 
    fetch next from @cursor into @DestTable 
    if @@fetch_status <> 0 break 

    --Create tablenames 
    SET @SrceTable = '[ProductManager].[dbo].TEMP_' + @DestTable 
    SET @DestPath = '[ProductManager].[dbo].'+ @DestTable 
    print @srceTable; 
    print @DestTable; 

    --Truncate target table 
    set @sql ='TRUNCATE TABLE '+ @DestPath + ';' 

    --Insert statement needs column names 
    set @columnList ='' 
    SELECT @columnList = coalesce(@columnList + '[' + name + '],','') FROM sys.columns Where OBJECT_NAME(OBJECT_ID) = @DestTable 
    if RIGHT(RTRIM(@columnList),1) = ',' 
    begin 
     SET @columnList = LEFT(@columnList, LEN(@columnList) - 1) 
    end 

    --Transfer data from source table 2 destination 
    set @sql = @sql + ' SET IDENTITY_INSERT ' + @DestPath + ' ON;' + ' INSERT INTO ' + @DestPath + '(' + @columnList + ') SELECT ' + @columnList + ' FROM ' + @SrceTable 

    print @sql; 

    exec (@sql) 
end 
-- not strictly necessary w/ cursor variables since the will go out of scope like a normal var 
close @cursor 
deallocate @cursor 

End Try 
Begin Catch 
declare @ErrorMsg nvarchar(MAX); 
select @ErrorMsg = ERROR_MESSAGE(); 

SELECT @err = @@error IF @err <> 0 Return @err 
end Catch 
END 

Respuesta

17

IDENTITY_INSERT no funciona con servidores vinculados yo sepa, a menos que se ejecuta SQL dinámico que incluye el SET IDENTITY_INSERT en el lote o tiene algún código (por ejemplo, Stored Proc) en el servidor remoto que hace eso por usted.

El IDENTITY_INSERT es por sesión (ver MSDN) y cuando se utiliza el servidor remoto éste será probablemente en una sesión diferente de su sentencia ejecutada a través de [LINKEDSERVER].tempdb.sys.sp_sqlexec, lo que provoca que falle como se ve que esto ocurra.

+0

Gracias por su respuesta Lucero pero si creo un SP para activar la identidad de en el servidor remoto y lo llaman: set = ATsql ATsql + 'EXEC [37.230.100.75] .ProductManager.dbo.TEST2PROD_IdentityOn;' ¿Me sale el mismo error? Saludos, Mike –

+1

@MikeDole, todo el código (configuración identidad-inserción, inserción de datos, configuración de inserción de identidad desactivada) tendría que estar en ese SP. P.ej. puede alimentar elementos individuales para insertarlos en el SP, o si desea realizar operaciones de configuración, debe pasar un TVP o hacer que el servidor vinculado use el servidor local como servidor vinculado). – Lucero

+0

FYI. No funciona mágicamente envolviéndolo en sql dinámico. Aún obtiene este error: el nombre del objeto 'LINKED_SERVER.DestinationDatabase.dbo.DestinationTable' contiene más que el número máximo de prefijos. El máximo es 2. –

4

Puede insertar un valor de identidad en una tabla con una columna de identidad en un servidor vinculado con el "interruptor en" truco.

Si no ha utilizado el truco "CAMBIAR A" para agregar y eliminar identidad en una columna, ¡es muy rápido, incluso en tablas grandes!

Conceptualmente sólo tiene que crear un nuevo esquema exactamente igual que la tabla que está deseando INSERT a sin la identidad definida. Luego cambie la tabla a ese ESQUEMA y haga su INSERT. A continuación, vuelva a cambiar al SCHEMA con la identidad definida.
El ejemplo siguiente se ha probado en un servidor vinculado en AZURE. Todas las advertencias de la utilización de "interruptor en" aplicar (índices deben ser los mismos, eliminar y reconstruir las claves externas, etc.)

Para probar, puede ejecutar la secuencia de comandos completa a continuación en una base de datos vinculada Azure SQL Server. Tendrá que buscar/reemplazar con [LINKED_SERVER_NAME] y [DATABASE_NAME], reemplazando con sus valores. En una base de datos que no sea Azure, es posible que deba agregar "EN PRIMARIO" a las creaciones de la tabla.

--Let's setup the example by creating a table with an IDENTITY column on the Linked Server 
EXEC(' 
CREATE TABLE [DATABASE_NAME].[dbo].[Example_Table](
    [ID] [int] IDENTITY(1,1) NOT NULL, 
    [Name] [nchar](10) NULL 
) 
' 
) AT [LINKED_SERVER_NAME] 

--INSERT some data into the table 
INSERT INTO [LINKED_SERVER_NAME].[DATABASE_NAME].[dbo].[Example_Table] ([Name]) VALUES ('Travis') 
INSERT INTO [LINKED_SERVER_NAME].[DATABASE_NAME].[dbo].[Example_Table] ([Name]) VALUES ('Mike') 

-- Looks good 
SELECT * FROM [LINKED_SERVER_NAME].[DATABASE_NAME].[dbo].[Example_Table] 

GO 


-- Create a TABLE with an identical schema, without the identity defined 
EXEC(' 
CREATE TABLE [DATABASE_NAME].[dbo].[Example_Table_temp](
    [ID] [int] NOT NULL, 
    [Name] [nchar](10) NULL 
) 
' 
) AT [LINKED_SERVER_NAME] 

--Now Use the "SWITCH TO" to move the data to the new table 
EXEC(' 
ALTER TABLE [DATABASE_NAME].[dbo].[Example_Table] SWITCH TO [DATABASE_NAME].[dbo].[Example_Table_temp] 
    ' 
) AT [LINKED_SERVER_NAME] 


--Drop the old table (It should now be empty, but you may want to verify that if you are unsure here) 
EXEC(' 
DROP TABLE [DATABASE_NAME].[dbo].[Example_Table] 
' 
) AT [LINKED_SERVER_NAME] 

--Rename the new table back to the old table name 
-- NOTE the lack of database and owner identifiers in the new name 
-- NOTE the use of double single qoutes (ESCAPED single quotes) 
EXEC(' 
    EXEC sp_rename ''[DATABASE_NAME].[dbo].Example_Table_temp'',''Example_Table'' 
    ' 
) AT [LINKED_SERVER_NAME] 


    -- Now do your IDENTITY INSERTs !!!! 

INSERT INTO [LINKED_SERVER_NAME].[DATABASE_NAME].[dbo].[Example_Table] (ID,[Name]) VALUES (888,'Travis') 
INSERT INTO [LINKED_SERVER_NAME].[DATABASE_NAME].[dbo].[Example_Table] (ID,[Name]) VALUES (999,'Mike') 

--Verify they got put in 
SELECT * FROM [LINKED_SERVER_NAME].[DATABASE_NAME].[dbo].[Example_Table] 


    --Now let's switch it back to our SCHEMA with an IDENTITY 

    EXEC(' 
CREATE TABLE [DATABASE_NAME].[dbo].[Example_Table_temp](
    [ID] [int] IDENTITY(1,1) NOT NULL, 
    [Name] [nchar](10) NULL 
) 

ALTER TABLE [DATABASE_NAME].[dbo].[Example_Table] SWITCH TO [DATABASE_NAME].[dbo].[Example_Table_temp] 

DROP TABLE [DATABASE_NAME].[dbo].[Example_Table] 

    EXEC sp_rename ''[DATABASE_NAME].[dbo].Example_Table_temp'',''Example_Table'' 
    ' 
) AT [LINKED_SERVER_NAME] 

--Data is still there 
SELECT * FROM [LINKED_SERVER_NAME].[DATABASE_NAME].[dbo].[Example_Table] 
GO 
-- And note you can no longer INSERT the IDENTITY 
INSERT INTO [LINKED_SERVER_NAME].[DATABASE_NAME].[dbo].[Example_Table] (ID,[Name]) VALUES (45,'Travis') 
GO 
Cuestiones relacionadas