it-swarm.asia

Bagaimana cara menyalin tabel dengan SELECT INTO tetapi mengabaikan properti IDENTITY?

Saya memiliki tabel dengan kolom identitas yang mengatakan:

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

Sudah terkenal, bahwa ini

select * into copy_from_with_id_1 from with_id;

menghasilkan copy_from_with_id_1 dengan identitas pada id juga.

Berikut ini pertanyaan stack overflow menyebutkan daftar semua kolom secara eksplisit.

Mari mencoba

select id, val into copy_from_with_id_2 from with_id;

Ups, bahkan dalam hal ini id adalah kolom identitas.

Yang saya inginkan adalah tabel seperti

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

Dari Buku Daring

Format new_table ditentukan dengan mengevaluasi ekspresi dalam daftar pilih. Kolom di new_table dibuat dalam urutan yang ditentukan oleh daftar pilih. Setiap kolom di new_table memiliki nama, tipe data, nullability, dan nilai yang sama dengan ekspresi yang sesuai dalam daftar pilih. Properti IDENTITY dari suatu kolom ditransfer kecuali dalam kondisi yang ditentukan dalam "Bekerja dengan Kolom Identitas" di bagian Keterangan.

Bawah halaman:

Ketika kolom identitas yang ada dipilih ke tabel baru, kolom baru mewarisi properti IDENTITY, kecuali salah satu dari kondisi berikut ini benar:

  • Pernyataan SELECT berisi fungsi bergabung, klausa GROUP BY, atau agregat.
  • Beberapa pernyataan SELECT digabungkan dengan menggunakan UNION.
  • Kolom identitas terdaftar lebih dari satu kali dalam daftar pilih.
  • Kolom identitas adalah bagian dari ekspresi.
  • Kolom identitas berasal dari sumber data jarak jauh.

Jika salah satu dari kondisi ini benar, kolom dibuat BUKAN NULL alih-alih mewarisi properti IDENTITY. Jika kolom identitas diperlukan dalam tabel baru tetapi kolom seperti itu tidak tersedia, atau Anda ingin seed atau nilai kenaikan yang berbeda dari kolom identitas sumber, tentukan kolom dalam daftar pilih menggunakan fungsi IDENTITAS. Lihat "Membuat kolom identitas menggunakan fungsi IDENTITY" di bagian Contoh di bawah ini.

Jadi ... secara teori Anda bisa lolos dengan:

select id, val 
into copy_from_with_id_2 
from with_id

union all

select 0, 'test_row' 
where 1 = 0;

Penting untuk mengomentari kode ini untuk menjelaskannya, jangan sampai dihapus saat orang lain melihatnya.

Terinspirasi oleh jawaban Erics, saya menemukan solusi berikut yang hanya bergantung pada nama tabel dan tidak menggunakan nama kolom tertentu:

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

Bahkan mungkin untuk meningkatkan ini

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

Anda dapat menggunakan gabungan untuk membuat dan mengisi tabel baru dalam sekali jalan:

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

Karena 1 = 0 kondisi, sisi kanan tidak akan memiliki kecocokan dan dengan demikian mencegah duplikasi baris sisi kiri, dan karena ini adalah gabungan luar, baris sisi kiri tidak akan dihilangkan juga. Akhirnya, karena ini adalah gabungan, properti IDENTITY dihilangkan.

Memilih hanya kolom sisi kiri, oleh karena itu, akan menghasilkan salinan persis dbo.TableWithIdentity hanya data-bijaksana, yaitu dengan properti IDENTITY dilucuti.

Semua yang dikatakan, Max Vernon telah mengangkat poin yang valid dalam komentar yang patut diingat. Jika Anda melihat rencana eksekusi permintaan di atas:

Execution plan

anda akan melihat bahwa tabel sumber disebutkan dalam rencana eksekusi sekali saja. Contoh lain telah dihilangkan oleh optimizer.

Jadi, jika pengoptimal dapat menetapkan dengan benar bahwa sisi kanan dari gabungan tidak diperlukan dalam rencana, harus masuk akal untuk mengharapkan bahwa dalam versi SQL Server yang akan datang mungkin dapat mengetahui bahwa properti IDENTITY tidak perlu dihapus juga, karena tidak ada lagi kolom IDENTITAS di baris sumber yang diatur sesuai dengan rencana kueri. Itu berarti bahwa permintaan di atas mungkin berhenti berfungsi seperti yang diharapkan di beberapa titik.

Tetapi, sebagaimana dicatat dengan benar oleh ypercubeᵀᴹ , sejauh ini manual secara eksplisit menyatakan bahwa jika ada gabungan, properti IDENTITY tidak dipertahankan:

Ketika kolom identitas yang ada dipilih ke tabel baru, kolom baru mewarisi properti IDENTITY, kecuali [...] [t] ia SELECT pernyataan berisi bergabung.

Jadi, selama manual terus menyebutkannya, kita mungkin dapat yakin bahwa perilakunya akan tetap sama.

Kudos to Shaneis dan ypercubeᵀᴹ untuk memunculkan topik terkait dalam obrolan.

13
Andriy M

Coba kode ini ..

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

Panggilan ISNULL memastikan bahwa kolom baru dibuat dengan NOT NULL nullability.

6
Saurav Ghosh

Hanya untuk menunjukkan cara yang berbeda:

Anda dapat menggunakan server tertaut.

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

Anda dapat membuat sementara server yang tertaut ke server lokal menggunakan ini:

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;

Di titik mana, Anda akan menjalankan select * into kode, mereferensikan localserver server yang terhubung dengan empat bagian nama:

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

Setelah itu selesai, bersihkan server yang terhubung localserver dengan ini:

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

Atau, Anda bisa menggunakan sintaks OPENQUERY

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

Properti identitas tidak ditransfer jika pernyataan pilih berisi gabungan, dan sebagainya

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

juga akan memberikan perilaku yang diinginkan (dari kolom id yang disalin untuk tidak menjaga properti IDENTITY. Namun, itu akan memiliki efek samping karena tidak menyalin baris apa pun sama sekali! (seperti beberapa lainnya metode) sehingga Anda perlu melakukan:

insert into without_id select * from with_id;

(terima kasih AakashM!)

1
anon-99

Cara mudah adalah membuat bagian kolom dari ekspresi.

Contoh:
Jika tabel dbo.Employee memiliki identitas pada kolom ID maka dalam contoh di bawah ini tabel temp #t akan memiliki IDENTITAS pada kolom ID juga.

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

Ubah ini untuk menerapkan ekspresi pada ID dan Anda tidak akan lagi memiliki IDENTITAS pada kolom ID. Dalam hal ini kami menerapkan tambahan sederhana ke kolom ID.

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

Contoh lain dari ekspresi untuk tipe data lain dapat meliputi: convert (), penggabungan string, atau Isnull ()

1
FistOfFury

Terkadang, Anda ingin menyisipkan dari tabel di mana Anda tidak tahu (atau peduli) apakah kolom itu dibuat menggunakan IDENTITAS atau tidak. Bahkan mungkin bukan kolom bilangan bulat yang sedang Anda kerjakan. Dalam hal ini, berikut ini akan berfungsi:

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

ISNULL akan menjatuhkan atribut IDENTITY dari kolom tetapi menyisipkannya dengan nama dan jenis yang sama dengan kolom asli dan juga membuatnya tidak nullable. TOP (0) akan membuat tabel kosong yang bisa Anda gunakan untuk menyisipkan baris yang dipilih. Anda juga dapat membuat tabel dikompresi sebelum Anda memasukkan data jika diperlukan.

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

akan menghapus identitas.

Kelemahannya adalah id menjadi nol tetapi Anda dapat menambahkan batasan itu.

0
john hunter