¿Cómo copio una tabla con SELECT INTO pero ignoro la propiedad IDENTITY?


43

Tengo una tabla con una columna de identidad que dice:

create table with_id (
 id int identity(1,1),
 val varchar(30)
);

Es bien sabido que esto

select * into copy_from_with_id_1 from with_id;

da como resultado copy_from_with_id_1 con identidad en la identificación también.

La siguiente pregunta de desbordamiento de pila menciona una lista explícita de todas las columnas.

Intentemos

select id, val into copy_from_with_id_2 from with_id;

Vaya, incluso en este caso id es una columna de identidad.

Lo que quiero es una mesa como

create table without_id (
 id int,
 val varchar(30)
);

Respuestas:


55

De libros en línea

El formato de new_table se determina evaluando las expresiones en la lista de selección. Las columnas en new_table se crean en el orden especificado por la lista de selección. Cada columna en new_table tiene el mismo nombre, tipo de datos, nulabilidad y valor que la expresión correspondiente en la lista de selección. La propiedad IDENTITY de una columna se transfiere, excepto en las condiciones definidas en "Trabajar con columnas de identidad" en la sección Comentarios.

Abajo de la página:

Cuando se selecciona una columna de identidad existente en una nueva tabla, la nueva columna hereda la propiedad IDENTITY, a menos que se cumpla una de las siguientes condiciones:

  • La instrucción SELECT contiene una unión, cláusula GROUP BY o función agregada.
  • Se unen varias instrucciones SELECT mediante UNION.
  • La columna de identidad aparece más de una vez en la lista de selección.
  • La columna de identidad es parte de una expresión.
  • La columna de identidad es de una fuente de datos remota.

Si alguna de estas condiciones es verdadera, la columna se crea NOT NULL en lugar de heredar la propiedad IDENTITY. Si se requiere una columna de identidad en la nueva tabla pero dicha columna no está disponible, o si desea un valor inicial o de incremento que sea diferente a la columna de identidad de origen, defina la columna en la lista de selección utilizando la función IDENTIDAD. Consulte "Creación de una columna de identidad utilizando la función IDENTIDAD" en la sección de Ejemplos a continuación.

Entonces ... teóricamente podrías salirte con la tuya:

select id, val 
into copy_from_with_id_2 
from with_id

union all

select 0, 'test_row' 
where 1 = 0;

Sería importante comentar este código para explicarlo, para que no se elimine la próxima vez que alguien lo vea.


30

Inspirado por la respuesta de Erics, encontré la siguiente solución que solo depende de los nombres de las tablas y no utiliza ningún nombre de columna específico:

select * into without_id from with_id where 1 = 0
union all
select * from with_id where 1 = 0
;
insert into without_id select * from with_id;

Editar

Incluso es posible mejorar esto para

select * into without_id from with_id
union all
select * from with_id where 1 = 0
;

13

Puede usar una combinación para crear y completar la nueva tabla de una vez:

SELECT
  t.*
INTO
  dbo.NewTable
FROM
  dbo.TableWithIdentity AS t
  LEFT JOIN dbo.TableWithIdentity ON 1 = 0
;

Debido a la 1 = 0condición, el lado derecho no tendrá coincidencias y, por lo tanto, evitará la duplicación de las filas del lado izquierdo, y como se trata de una unión externa, las filas del lado izquierdo tampoco se eliminarán. Finalmente, como se trata de una unión, se elimina la propiedad IDENTITY.

Seleccionar solo las columnas del lado izquierdo, por lo tanto, producirá una copia exacta de dbo.TableWithIdentity solo en términos de datos, es decir, con la propiedad IDENTITY despojada.

Dicho todo esto, Max Vernon ha planteado un punto válido en un comentario que vale la pena tener en cuenta. Si observa el plan de ejecución de la consulta anterior:

Plan de ejecución

notará que la tabla fuente se menciona en el plan de ejecución solo una vez. La otra instancia ha sido eliminada por el optimizador.

Por lo tanto, si el optimizador puede establecer correctamente que el lado derecho de la combinación no es necesario en el plan, debería ser razonable esperar que en una versión futura de SQL Server pueda darse cuenta de que la propiedad IDENTITY no necesita ser eliminado tampoco, ya que ya no hay otra columna IDENTIDAD en el conjunto de filas de origen de acuerdo con el plan de consulta. Eso significa que la consulta anterior podría dejar de funcionar como se esperaba en algún momento.

Pero, como lo señaló correctamente ypercubeᵀᴹ , hasta ahora el manual ha declarado explícitamente que si hay una unión, la propiedad IDENTITY no se conserva:

Cuando se selecciona una columna de identidad existente en una nueva tabla, la nueva columna hereda la propiedad IDENTITY, a menos que [...] la instrucción SELECT contenga una unión.

Por lo tanto, mientras el manual lo siga mencionando, probablemente podamos estar seguros de que el comportamiento seguirá siendo el mismo.

Felicitaciones a Shaneis y ypercubeᵀᴹ por mencionar un tema relacionado en el chat.


¿ JOIN (SELECT 1) AS dummy ON 1 = 1Funcionaría también?
ypercubeᵀᴹ


6

Prueba este código ...

SELECT isnull(Tablename_old.IDENTITYCOL + 0, -1) AS 'New Identity Column'
INTO   dbo.TableName_new
FROM   dbo.TableName_old 

La ISNULLllamada asegura que la nueva columna se cree con NOT NULLnulabilidad.


1
¿Es el ISNULL()o el +0que lo hace? ¿O se necesitan ambos?
ypercubeᵀᴹ

Solo agrego 0 trabajos. Esta es la solución más simple siempre que enumere columnas explícitamente y no use select *.
Ian Horwill

3

Solo para mostrar una forma diferente:

Puede usar un servidor vinculado .

SELECT * 
INTO without_id 
FROM [linked_server].[source_db].dbo.[with_id];

Puede crear temporalmente un servidor vinculado al servidor local utilizando esto:

DECLARE @LocalServer SYSNAME 
SET @LocalServer = @@SERVERNAME;
EXEC master.dbo.sp_addlinkedserver @server = N'localserver'
    , @srvproduct = ''
    , @provider = 'SQLNCLI'
    , @datasrc = @LocalServer;
EXEC master.dbo.sp_addlinkedsrvlogin @rmtsrvname=N'localserver'
    , @useself = N'True'
    , @locallogin = NULL
    , @rmtuser = NULL
    , @rmtpassword = NULL;

En ese momento, ejecutará el select * intocódigo, haciendo referencia al localserverservidor vinculado de cuatro partes:

SELECT * 
INTO without_id 
FROM [localserver].[source_db].dbo.[with_id];

Una vez que se complete, limpie el localserverservidor vinculado con esto:

EXEC sp_dropserver @server = 'localserver'
    , @droplogins = 'droplogins';

O podría usar la OPENQUERYsintaxis

SELECT * 
INTO without_id 
FROM OPENQUERY([linked_server], 'SELECT * FROM [source_db].dbo.[with_id]');

1

La propiedad de identidad no se transfiere si la instrucción select contiene una unión, y así

select a.* into without_id from with_id a inner join with_id b on 1 = 0;

también dará el comportamiento deseado (de la idcolumna copiada para no mantener la IDENTITYpropiedad. Sin embargo, tendrá el efecto secundario de no copiar ninguna fila en absoluto (como con algunos otros métodos), por lo que deberá hacer lo siguiente:

insert into without_id select * from with_id;

(gracias AakashM!)


1

La manera fácil es hacer que la columna sea parte de una expresión.

Ejemplo:
si la tabla dbo.Employee tiene una identidad en la columna ID, entonces, en el siguiente ejemplo, la tabla temporal #t también tendrá una IDENTIDAD en la columna ID.

--temp table has IDENTITY
select ID, Name 
into #t
from dbo.Employee

Cambie esto para aplicar una expresión a ID y ya no tendrá una IDENTIDAD en la columna ID. En este caso, aplicamos una adición simple a la columna ID.

--no IDENTITY
select ID = ID + 0, Name 
into #t
from dbo.Employee

Otros ejemplos de expresiones para otros tipos de datos podrían incluir: convert (), concatenación de cadenas o Isnull ()


1
De docs.microsoft.com/en-us/sql/t-sql/queries/… : “Cuando se selecciona una columna de identidad existente en una nueva tabla, la nueva columna hereda la propiedad IDENTITY, a menos que una de las siguientes condiciones sea verdadera ... La columna de identidad es parte de una expresión ... la columna se crea NO NULL en lugar de heredar la propiedad IDENTITY ".
Manngo

1

A veces, desea insertar desde una tabla donde no sabe (o no le importa) si la columna se creó con IDENTITY o no. Puede que ni siquiera sea una columna entera con la que esté trabajando. En este caso, lo siguiente funcionará:

SELECT TOP(0) ISNULL([col],NULL) AS [col], ... INTO [table2] FROM [table1]
ALTER TABLE [table2] REBUILD WITH (DATA_COMPRESSION=page)
INSERT INTO [table2] ...

ISNULL eliminará el atributo IDENTITY de la columna pero lo insertará con el mismo nombre y tipo que la columna original y también hará que no sea anulable. TOP (0) creará una tabla vacía que luego puede usar para insertar filas seleccionadas. También puede hacer que la tabla se comprima antes de insertar datos si es necesario.


0
select convert(int, id) as id, val 
into copy_from_with_id_without_id 
from with_id;

eliminará la identidad.

La desventaja es que se idconvierte en anulable, pero podría agregar esa restricción.


1
Puede usar ISNULL para evitar eso.
Erik Darling

Al usar nuestro sitio, usted reconoce que ha leído y comprende nuestra Política de Cookies y Política de Privacidad.
Licensed under cc by-sa 3.0 with attribution required.