it-swarm.it

Come copio una tabella con SELECT INTO ma ignoro la proprietà IDENTITY?

Ho una tabella con colonna identità dire:

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

È noto, questo

select * into copy_from_with_id_1 from with_id;

genera anche copy_from_with_id_1 con identità su ID.

La seguente domanda di overflow dello stack menziona l'elenco di tutte le colonne in modo esplicito.

Proviamo

select id, val into copy_from_with_id_2 from with_id;

Oops, anche in questo caso id è una colonna di identità.

Quello che voglio è un tavolo simile

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

Da Libri online

Il formato di new_table viene determinato valutando le espressioni nell'elenco di selezione. Le colonne in new_table vengono create nell'ordine specificato dall'elenco di selezione. Ogni colonna in new_table ha lo stesso nome, tipo di dati, nullability e valore dell'espressione corrispondente nell'elenco di selezione. La proprietà IDENTITY di una colonna viene trasferita tranne nelle condizioni definite in "Utilizzo delle colonne di identità" nella sezione Note.

In fondo alla pagina:

Quando una colonna di identità esistente viene selezionata in una nuova tabella, la nuova colonna eredita la proprietà IDENTITY, a meno che una delle seguenti condizioni sia vera:

  • L'istruzione SELECT contiene un join, una clausola GROUP BY o una funzione aggregata.
  • Più istruzioni SELECT vengono unite utilizzando UNION.
  • La colonna identità è elencata più di una volta nell'elenco di selezione.
  • La colonna identità fa parte di un'espressione.
  • La colonna identità proviene da un'origine dati remota.

Se una di queste condizioni è vera, la colonna viene creata NOT NULL invece di ereditare la proprietà IDENTITY. Se una nuova colonna di identità è richiesta nella nuova tabella ma tale colonna non è disponibile o si desidera un seme o un valore di incremento diverso dalla colonna di identità di origine, definire la colonna nell'elenco di selezione utilizzando la funzione IDENTITÀ. Vedere "Creazione di una colonna identità mediante la funzione IDENTITÀ" nella sezione Esempi di seguito.

Quindi ... potresti teoricamente cavartela con:

select id, val 
into copy_from_with_id_2 
from with_id

union all

select 0, 'test_row' 
where 1 = 0;

Sarebbe importante commentare questo codice per spiegarlo, per evitare che venga rimosso la prossima volta che qualcuno lo guarda.

Ispirato dalla risposta di Erics, ho trovato la seguente soluzione che dipende solo dai nomi delle tabelle e non utilizza alcun nome di colonna specifico:

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;

Edit

È anche possibile migliorare questo a

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

Puoi utilizzare un join per creare e popolare la nuova tabella in una sola volta:

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

A causa del 1 = 0 condizione, il lato destro non avrà corrispondenze e quindi impedirà la duplicazione delle file del lato sinistro e, poiché si tratta di un join esterno, le file del lato sinistro non verranno eliminate. Infine, poiché si tratta di un join, la proprietà IDENTITY viene eliminata.

Selezionando solo le colonne sul lato sinistro, quindi, verrà prodotta una copia esatta di dbo.TableWithIdentity solo per quanto riguarda i dati, ovvero con la proprietà IDENTITY rimossa.

Detto questo, Max Vernon ha sollevato un punto valido in un commento che vale la pena ricordare. Se guardi il piano di esecuzione della query sopra:

Execution plan

noterai che la tabella di origine è menzionata nel piano di esecuzione solo una volta. L'altra istanza è stata eliminata dall'ottimizzatore.

Quindi, se l'ottimizzatore può stabilire correttamente che il lato destro del join non è necessario nel piano, dovrebbe essere ragionevole aspettarsi che in una versione futura di SQL Server potrebbe essere in grado di capire che la proprietà IDENTITY non deve essere rimosso anche, poiché non esiste più un'altra colonna IDENTITY nella riga di origine impostata in base al piano di query. Ciò significa che la query sopra potrebbe smettere di funzionare come previsto ad un certo punto.

Ma, come correttamente osservato da ypercubeᵀᴹ , finora il manuale ha esplicitamente dichiarato che se esiste un join, la proprietà IDENTITY non viene mantenuta:

Quando una colonna di identità esistente viene selezionata in una nuova tabella, la nuova colonna eredita la proprietà IDENTITY, a meno che [...] [t] l'istruzione SELECT contenga un join.

Quindi, fintanto che il manuale continuerà a menzionarlo, possiamo essere certi che il comportamento rimarrà lo stesso.

Complimenti a Shaneis e ypercubeᵀᴹ per aver sollevato un argomento correlato nella chat.

13
Andriy M

Prova questo codice ..

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

La chiamata ISNULL assicura che la nuova colonna venga creata con NOT NULL nullability.

6
Saurav Ghosh

Solo per mostrare un modo diverso:

È possibile utilizzare un server collegato.

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

È possibile creare temporaneamente un server collegato al server locale usando questo:

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;

A quel punto, avresti eseguito il select * into codice, facendo riferimento al localserver server collegato in quattro parti nome:

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

Al termine, ripulisci il server collegato localserver con questo:

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

In alternativa, è possibile utilizzare la sintassi OPENQUERY

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

La proprietà identity non viene trasferita se l'istruzione select contiene un join e così

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

fornirà anche il comportamento desiderato (della colonna id copiata per non mantenere la proprietà IDENTITY. Tuttavia, avrà l'effetto collaterale di non copiare alcuna riga! (come con alcuni altri metodi), quindi dovrai fare:

insert into without_id select * from with_id;

(grazie AakashM!)

1
anon-99

Il modo più semplice è rendere la colonna parte di un'espressione.

Esempio:
Se la tabella dbo.Employee ha un'identità nella colonna ID, nell'esempio sotto la tabella temporanea #t avrà anche un'IDENTITÀ sulla colonna ID.

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

Modificalo per applicare un'espressione all'ID e #t non avrà più un IDENTITÀ nella colonna ID. In questo caso applichiamo una semplice aggiunta alla colonna ID.

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

Altri esempi di espressioni per altri tipi di dati potrebbero includere: convert (), concatenazione di stringhe o Isnull ()

1
FistOfFury

A volte, vuoi inserire da una tabella in cui non sai (o ti interessa) se la colonna è stata creata usando IDENTITY o meno. Potrebbe non essere nemmeno una colonna intera con cui stai lavorando. In questo caso, funzionerà quanto segue:

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

ISNULL eliminerà l'attributo IDENTITY dalla colonna ma lo inserirà con lo stesso nome e tipo della colonna originale e lo renderà non annullabile. TOP (0) creerà una tabella vuota che sarà quindi possibile utilizzare per inserire le righe selezionate. È anche possibile rendere compressa la tabella prima di inserire i dati, se necessario.

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

rimuoverà l'identità.

Il rovescio della medaglia è che id diventa nullable ma potresti aggiungere quel vincolo.

0
john hunter