2011-01-26 15 views
27

Estoy tratando de insertar de una tabla a otra usandoSQL Server 2005 ROW_NUMBER() sin ORDER BY

DECLARE @IDOffset int; 
SELECT @IDOffset = MAX(ISNULL(ID,0)) FROM TargetTable 

INSERT INTO TargetTable(ID, FIELD) 
SELECT [Increment] + @IDOffset ,FeildValue 
FROM SourceTable 
WHERE [somecondition] 

TargetTable.ID no es una columna de identidad, por lo que tengo que encontrar una manera de auto -incrementarlo yo mismo.

Sé que puedo usar un cursor, o crear una variable de tabla con una columna de identidad y un campo FieldValue, poblar eso y luego usarlo en mi insert into...select, pero eso no es muy eficiente. Intenté usar la función ROW_NUMBER para incrementar, pero realmente no tengo un campo ORDER BY legítimo en la SourceTable que pueda usar, y me gustaría mantener el orden original de la SourceTable (si es posible).

¿Alguien puede sugerir algo?

+2

¿Cuál es el índice agrupado en la tabla de origen? Supongo que de eso es de lo que está hablando cuando dice "orden original de la tabla de origen". Si es un montón, no hay un orden en particular. –

Respuesta

70

Puede evitar especificar un orden explícita de la siguiente manera:

INSERT dbo.TargetTable (ID, FIELD) 
SELECT 
    Row_Number() OVER (ORDER BY (SELECT 1)) 
     + Coalesce(
     (SELECT Max(ID) FROM dbo.TargetTable WITH (TABLOCKX, HOLDLOCK)), 
     0 
    ), 
    FieldValue 
FROM dbo.SourceTable 
WHERE {somecondition}; 

Sin embargo, tenga en cuenta que no es más que una manera de evitar especificar un ordenamiento y NO garantiza que se conservará cualquier ordenación de datos original. Existen otros factores que pueden ocasionar que se ordene el resultado, como ORDER BY en la consulta externa. Para entender completamente esto, uno debe darse cuenta de que el concepto "no ordenado (de una manera particular)" no es lo mismo que "retener el orden original" (¡lo cual se ordena de una manera particular!). Creo que desde una perspectiva de base de datos relacional pura, el último concepto no existe, por definición (aunque puede haber implementaciones de bases de datos que violen esto, SQL Server no es uno de ellos).

El motivo de las sugerencias de bloqueo es evitar que otras inserciones de proceso utilicen el valor que planea usar, entre las partes de la consulta que se está ejecutando.

Nota: Mucha gente usa (SELECT NULL) para sortear la restricción de "no constantes permitidas en la cláusula ORDER BY de una función de ventana". Por alguna razón, prefiero 1 sobre NULL.

También: creo que una columna de identidad es muy superior y debería usarse en su lugar. No es bueno para la concurrencia bloquear exclusivamente tablas enteras. Atenuación.

+0

@Emtucifor: El primero funciona bien. En cuanto al segundo, el hecho es que devuelve un resultado, una fila, en cualquier caso. Si la tabla está vacía, devuelve NULL, por lo que no es necesario repetirla, simplemente use ISNULL() en MAX(), pero no dentro de ella. De alguna manera había logrado descartarlo primero, de hecho, es posible que no lo haya pensado mucho en ese momento. Una mera coincidencia me permite saber lo contrario cuando leo la publicación de alguien aquí en SO (no recuerdo ahora cuál). Lo revisé antes de publicar mi corrección. ¿Por qué es mejor? Bueno, es simplemente más simple, y más fácil de leer, creo. –

+0

Esto no es necesariamente correcto, si la consulta externa tiene una cláusula 'ORDER BY', o en otros casos extremos como se puede ver aquí: http://stackoverflow.com/q/18961789/521799 –

+0

@LukasEder Mi respuesta fue correcta , pero tal vez no fue tan completo como podría haber sido.Si alguien buscaba "preservar el orden original" y no estaba consciente de que este es un concepto sin sentido en SQL Server, podrían intentar usar este código para obtenerlo, pero fallarían. Ahora he actualizado mi respuesta para abordar este aspecto. – ErikE

2

puede ignorar el pedido mediante el uso de order by (select null) así:

declare @IDOffset int; 
select @IDOffset = max(isnull(ID, 0)) from TargetTable 

insert into TargetTable(ID, FIELD) 
select row_number() over (order by (select null)) + @IDOffset, FeildValue 
    from SourceTable 
where [somecondition] 
+1

En comparación con la respuesta que ya ha estado aquí durante cinco años, ¿qué significa esto? No proporciona ningún nuevo enfoque. No proporciona ninguna nueva explicación. No proporciona nada nuevo. – hvd

Cuestiones relacionadas