2008-09-30 22 views
15

Estoy tratando de crear una tabla de asignación para asociar los ID de las filas nuevas en una tabla con aquellas de las que se copiaron. La cláusula OUTPUT INTO parece perfecta para eso, pero no parece comportarse de acuerdo con la documentación.¿Qué columnas se pueden usar en la cláusula OUTPUT INTO?

Mi código:

DECLARE @Missing TABLE (SrcContentID INT PRIMARY KEY) 
INSERT INTO @Missing 
    (SrcContentID) 
SELECT cshadow.ContentID 
    FROM Private.Content AS cshadow 
    LEFT JOIN Private.Content AS cglobal ON cshadow.Tag = cglobal.Tag 
    WHERE cglobal.ContentID IS NULL 

PRINT 'Adding new content headers' 
DECLARE @Inserted TABLE (SrcContentID INT PRIMARY KEY, TgtContentID INT) 
INSERT INTO Private.Content 
    (Tag, Description, ContentDate, DateActivate, DateDeactivate, SortOrder, CreatedOn, IsDeleted, ContentClassCode, ContentGroupID, OrgUnitID) 
    OUTPUT cglobal.ContentID, INSERTED.ContentID INTO @Inserted (SrcContentID, TgtContentID) 
SELECT Tag, Description, ContentDate, DateActivate, DateDeactivate, SortOrder, CreatedOn, IsDeleted, ContentClassCode, ContentGroupID, NULL 
    FROM Private.Content AS cglobal 
    INNER JOIN @Missing AS m ON cglobal.ContentID = m.SrcContentID 

Resultados en el mensaje de error:

Msg 207, Level 16, State 1, Line 34 
Invalid column name 'SrcContentID'. 

(línea 34 siendo el uno con el OUTPUT INTO)

experimentación sugiere que sólo las filas que son en realidad presente en el objetivo de INSERT se puede seleccionar en OUTPUT INTO. Pero esto contradice los documentos en los libros en línea. El artículo sobre la cláusulasalida tiene ejemplo E, que describe un uso similar:

el resultado en rendimientos cláusula values ​​ de la tabla que se actualiza (WorkOrder) y también de la tabla Producto . La tabla Producto se usa en la cláusula FROM para especificar las filas a la actualización .

¿Alguien ha trabajado con esta función?

(Mientras tanto, me he reescrito el código para hacer el trabajo usando un bucle de cursor, pero eso es feo y todavía estoy curioso)

Respuesta

13

He verificado que el problema es que sólo se puede utilizar INSERTED columnas. La documentación parece indicar que se puede utilizar from_table_name, pero me parece que no puede conseguir que funcione (el identificador de varias partes "m.ContentID" no podía ser obligado.):

TRUNCATE TABLE main 

SELECT * 
FROM incoming 

SELECT * 
FROM main 

DECLARE @Missing TABLE (ContentID INT PRIMARY KEY) 
INSERT INTO @Missing(ContentID) 
SELECT incoming.ContentID 
FROM incoming 
LEFT JOIN main 
    ON main.ContentID = incoming.ContentID 
WHERE main.ContentID IS NULL 

SELECT * 
FROM @Missing 

DECLARE @Inserted TABLE (ContentID INT PRIMARY KEY, [Content] varchar(50)) 
INSERT INTO main(ContentID, [Content]) 
OUTPUT INSERTED.ContentID /* incoming doesn't work, m doesn't work */, INSERTED.[Content] INTO @Inserted (ContentID, [Content]) 
SELECT incoming.ContentID, incoming.[Content] 
FROM incoming 
INNER JOIN @Missing AS m 
    ON m.ContentID = incoming.ContentID 

SELECT * 
FROM @Inserted 

SELECT * 
FROM incoming 

SELECT * 
FROM main 

Al parecer, el from_table_name prefijo sólo se permite en DELETE o UPDATE (o MERGE en 2008) - no estoy seguro de por qué:

  • from_table_name

es una colu mn prefix que especifica una tabla incluida en la cláusula FROM de una declaración DELETE o UPDATE que se utiliza para especificar las filas para actualizar o eliminar.

Si la tabla que se está modificando también se especifica en la cláusula FROM, cualquier referencia a las columnas en esa tabla debe calificarse con el prefijo INSERTED o DELETED.

6

Me encuentro con EXACTAMENTE el mismo problema que usted, siento su dolor ... Por lo que he podido encontrar, no hay forma de usar el prefijo from_table_name con una instrucción INSERT. Estoy seguro de que hay una razón técnica viable para esto, y me gustaría saber exactamente de qué se trata.

Ok, lo encontraron, aquí hay un mensaje en el foro sobre por qué no funciona: MSDN forums

+0

Y proporciona la clave para la solución. ¡Fusiona la declaración! Entonces @Simon logró proporcionar sintaxis. ¡Bonito! – lambacck

0

creo que he encontrado una solución a este problema, que tristemente involucra una tabla temporal, pero al menos voy evitar la creación de un cursor temido :) Lo que necesita hacer es agregar una columna extra a la tabla desde la que está duplicando registros y darle un tipo 'uniqueidentifer'.

luego declarar una tabla temporal:

DECLARE @tmptable TABLE (uniqueid uniqueidentifier, original_id int, new_id int) 

insertar los datos en su tabla temporal como esto:

insert into @tmptable 
(uniqueid,original_id,new_id) 
select NewId(),id,0 from OriginalTable 

el seguir adelante y hacer la inserción real de la tabla original:

insert into OriginalTable 
(uniqueid) 
select uniqueid from @tmptable 

Ahora, para agregar los valores de identidad recién creados a su tabla temporal:

update @tmptable 
set new_id = o.id 
from OriginalTable o inner join @tmptable tmp on tmp.uniqueid = o.uniqueid 

Ahora usted tiene una tabla de consulta que contiene la nueva identificación y la identificación original en un registro, para su uso de placer :)

espero que esto ayude a alguien ...

+1

Gracias, pero esta es solo una solución viable si la clave que necesito seguir es un identificador único. Todo el problema que trato de resolver es rastrear el valor de IDENTIDAD que se está asignando a una nueva fila. –

0

(MS) Si la tabla que se está modificando también se especifica en la cláusula FROM, cualquier referencia a las columnas en esa tabla debe calificarse con el prefijo INSERTED o BORRADO.

En su ejemplo, no se puede utilizar la tabla cglobal en el resultado a menos que sea INSERTED.column_name o DELETED.column_name:

INSERT INTO Private.Content 
    (Tag) 
    OUTPUT cglobal.ContentID, INSERTED.ContentID 
    INTO @Inserted (SrcContentID, TgtContentID) 
SELECT Tag 
    FROM Private.Content AS cglobal 
    INNER JOIN @Missing AS m ON cglobal.ContentID = m.SrcContentID 

Lo que funcionó para mí fue una sencilla tabla de alias, como esto:

INSERT INTO con1 
    (Tag) 
    OUTPUT **con2**.ContentID, INSERTED.ContentID 
    INTO @Inserted (SrcContentID, TgtContentID) 
SELECT Tag 
    FROM Private.Content con1 
    **INNER JOIN Private.Content con2 ON con1.id=con2.id** 
    INNER JOIN @Missing AS m ON con1.ContentID = m.SrcContentID 
+0

No pude conseguir que este enfoque funcionara :-( –

21

Usted puede hacer esto con una mezcla en SQL Server por debajo de 2008. Ejemplo de código:

--drop table A 
create table A (a int primary key identity(1, 1)) 
insert into A default values 
insert into A default values 

delete from A where a>=3 

-- insert two values into A and get the new primary keys 
MERGE a USING (SELECT a FROM A) AS B(a) 
ON (1 = 0) -- ignore the values, NOT MATCHED will always be true 
WHEN NOT MATCHED THEN INSERT DEFAULT VALUES -- always insert here for this example 
OUTPUT $action, inserted.*, deleted.*, B.a; -- show the new primary key and source data 

R esultado es

INSERT, 3, NULL, 1 
INSERT, 4, NULL, 2 

es decir, para cada fila de la nueva clave principal (3, 4) y el viejo (1, 2). Crear una tabla llamada p. #OUTPUT y agregando "INTO #OUTPUT;" al final de la cláusula OUTPUT se guardarían los registros.

+1

La instrucción MERGE es mi nueva mejor amiga :) – lambacck

+5

Buena explicación y ejemplo aquí: http://sqlblog.com/blogs/jamie_thomson/archive/2010/01/06/ merge-and-output-the-swiss-army-knife-of-t-sql.aspx –

Cuestiones relacionadas