ฉันจะคัดลอกตารางด้วย SELECT INTO ได้อย่างไร แต่ไม่สนใจคุณสมบัติตัวตน


41

ฉันมีตารางที่มีคอลัมน์ระบุตัวตน:

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

มันเป็นที่รู้จักกันดีว่าสิ่งนี้

select * into copy_from_with_id_1 from with_id;

ผลลัพธ์ใน copy_from_with_id_1 พร้อมรหัสประจำตัวด้วย

คำถามล้นสแต็คต่อไปนี้กล่าวถึงรายการคอลัมน์ทั้งหมดอย่างชัดเจน

มาลองกัน

select id, val into copy_from_with_id_2 from with_id;

อ๊ะแม้ในกรณีนี้ id จะเป็นคอลัมน์ข้อมูลประจำตัว

สิ่งที่ฉันต้องการคือโต๊ะ

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

คำตอบ:


52

จากหนังสือออนไลน์

รูปแบบของ new_table ถูกกำหนดโดยการประเมินผลนิพจน์ในรายการที่เลือก คอลัมน์ใน new_table ถูกสร้างขึ้นตามลำดับที่ระบุโดยรายการที่เลือก แต่ละคอลัมน์ใน new_table มีชื่อประเภทข้อมูลความสามารถในการลบล้างและค่าเหมือนกันในนิพจน์ที่สอดคล้องกันในรายการที่เลือก คุณสมบัติ IDENTITY ของคอลัมน์จะถูกถ่ายโอนยกเว้นภายใต้เงื่อนไขที่กำหนดไว้ใน "การทำงานกับคอลัมน์ข้อมูลประจำตัว" ในส่วนข้อสังเกต

ลงหน้า:

เมื่อคอลัมน์ข้อมูลประจำตัวที่มีอยู่ถูกเลือกลงในตารางใหม่คอลัมน์ใหม่จะสืบทอดคุณสมบัติตัวตนยกเว้นว่าเงื่อนไขข้อใดข้อหนึ่งต่อไปนี้เป็นจริง:

  • คำสั่ง SELECT ประกอบด้วยการเข้าร่วม GROUP BY clause หรือฟังก์ชั่นรวม
  • คำสั่ง SELECT หลายคำเข้าร่วมโดยใช้ UNION
  • คอลัมน์ข้อมูลประจำตัวมีการระบุไว้มากกว่าหนึ่งครั้งในรายการที่เลือก
  • คอลัมน์ข้อมูลประจำตัวเป็นส่วนหนึ่งของการแสดงออก
  • คอลัมน์ข้อมูลประจำตัวมาจากแหล่งข้อมูลระยะไกล

หากเงื่อนไขข้อใดข้อหนึ่งเหล่านี้เป็นจริงคอลัมน์จะถูกสร้างไม่เป็นโมฆะแทนที่จะสืบทอดคุณสมบัติตัวตน หากจำเป็นต้องใช้คอลัมน์ข้อมูลประจำตัวในตารางใหม่ แต่คอลัมน์ดังกล่าวไม่สามารถใช้งานได้หรือคุณต้องการค่าเมล็ดหรือค่าส่วนเพิ่มที่แตกต่างจากคอลัมน์ข้อมูลประจำตัวแหล่งกำเนิดให้กำหนดคอลัมน์ในรายการเลือกโดยใช้ฟังก์ชัน IDENTITY ดู "การสร้างคอลัมน์ข้อมูลประจำตัวโดยใช้ฟังก์ชั่นตัวตน" ในส่วนตัวอย่างด้านล่าง

ดังนั้น ... ในทางทฤษฎีคุณสามารถหลีกเลี่ยง:

select id, val 
into copy_from_with_id_2 
from with_id

union all

select 0, 'test_row' 
where 1 = 0;

มันเป็นสิ่งสำคัญที่จะต้องแสดงความคิดเห็นรหัสนี้เพื่ออธิบายว่ามันจะถูกลบออกในครั้งต่อไปที่มีคนดู


29

ได้รับแรงบันดาลใจจากคำตอบของ DDP ฉันพบวิธีแก้ไขปัญหาต่อไปนี้ซึ่งขึ้นอยู่กับชื่อตารางเท่านั้นและไม่ได้ใช้ชื่อคอลัมน์เฉพาะใด ๆ :

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;

แก้ไข

มันเป็นไปได้ที่จะปรับปรุงสิ่งนี้เป็น

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

13

คุณสามารถใช้การเข้าร่วมเพื่อสร้างและเติมตารางใหม่ในครั้งเดียว:

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

เนื่องจาก1 = 0เงื่อนไขด้านขวาจะไม่มีการจับคู่จึงป้องกันการซ้ำซ้อนของแถวด้านซ้ายและเนื่องจากเป็นการรวมภายนอกด้านนอกแถวด้านซ้ายจะไม่ถูกตัดออกเช่นกัน ในที่สุดเนื่องจากเป็นการเข้าร่วมคุณสมบัติ IDENTITY จะถูกกำจัด

ดังนั้นการเลือกคอลัมน์ด้านซ้ายจึงจะสร้างสำเนาที่แน่นอนของdbo.TableWithIdentity data-wise เท่านั้นที่มีคุณสมบัติตัวตนถูกถอดออก

ทั้งหมดที่กล่าวมาMax Vernonได้ยกประเด็นที่ถูกต้องในความคิดเห็นที่ควรค่าแก่การพิจารณา หากคุณดูแผนการดำเนินการของแบบสอบถามด้านบน:

แผนการดำเนินการ

คุณจะสังเกตเห็นว่าตารางต้นฉบับถูกกล่าวถึงในแผนปฏิบัติการเพียงครั้งเดียว อินสแตนซ์อื่นถูกกำจัดโดยเครื่องมือเพิ่มประสิทธิภาพ

ดังนั้นถ้าเครื่องมือเพิ่มประสิทธิภาพสามารถสร้างได้อย่างถูกต้องว่าไม่จำเป็นต้องใช้ด้านขวาของการเข้าร่วมในแผนควรมีเหตุผลที่คาดหวังว่าใน SQL Server รุ่นในอนาคตอาจจะสามารถคิดได้ว่าคุณสมบัติตัวตนไม่จำเป็น ลบออกอย่างใดอย่างหนึ่งเนื่องจากไม่มีคอลัมน์ตัวตนอีกต่อไปในแถวแหล่งที่ตั้งตามแผนแบบสอบถาม นั่นหมายความว่าแบบสอบถามด้านบนอาจหยุดทำงานตามที่คาดไว้ในบางจุด

แต่ตามที่ระบุไว้อย่างถูกต้องโดยypercubeᵀᴹจนถึงขณะนี้คู่มือดังกล่าวได้ระบุไว้อย่างชัดเจนว่าหากมีการเข้าร่วมคุณสมบัติตัวตนจะไม่ถูกเก็บรักษาไว้:

เมื่อคอลัมน์ข้อมูลประจำตัวที่มีอยู่ถูกเลือกลงในตารางใหม่คอลัมน์ใหม่จะสืบทอดคุณสมบัติตัวตนยกเว้นว่า [... ] [t] คำสั่ง SELECT จะมีการเข้าร่วม

ดังนั้นตราบใดที่คู่มือยังคงกล่าวถึงเราอาจมั่นใจได้ว่าพฤติกรรมจะยังคงเหมือนเดิม

รุ่งโรจน์ถึงShaneisและypercubeᵀᴹสำหรับแสดงหัวข้อที่เกี่ยวข้องในการแชท


จะใช้JOIN (SELECT 1) AS dummy ON 1 = 1งานได้เช่นกัน?
ypercubeᵀᴹ

CROSS JOIN (SELECT 1), INNER JOIN (SELECT 1) ON 1=1, LEFT JOIN (SELECT 1) ON 1=0หรือON 1=1- ไม่มีแถบรหัสประจำตัว ดูเหมือนว่ามันจะต้องมีการประกาศหรือสร้างวัตถุ
Andriy M

5

ลองรหัสนี้ ..

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

การISNULLโทรทำให้มั่นใจได้ว่าคอลัมน์ใหม่จะถูกสร้างขึ้นด้วยความNOT NULLสามารถ


1
มันเป็นISNULL()หรือ+0ว่ามันทำหรือไม่ หรือจำเป็นทั้งคู่?
ypercubeᵀᴹ

3

เพียงเพื่อแสดงวิธีอื่น:

คุณสามารถใช้เซิร์ฟเวอร์ที่เชื่อมโยง

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

คุณสามารถสร้างเซิร์ฟเวอร์ที่ลิงก์ไปยังเซิร์ฟเวอร์ภายในโดยใช้สิ่งนี้ชั่วคราว:

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;

ณ จุดนี้คุณจะต้องเรียกใช้select * intoโค้ดโดยอ้างอิงlocalserverเซิร์ฟเวอร์สี่ลิงก์ชื่อ:

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

หลังจากนั้นให้ทำความสะอาดlocalserverเซิร์ฟเวอร์ที่เชื่อมโยงด้วยสิ่งนี้:

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

หรือคุณสามารถใช้OPENQUERYไวยากรณ์

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

1

คุณสมบัติตัวตนจะไม่ถูกโอนหากคำสั่ง select มีการเข้าร่วมและอื่น ๆ

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

จะให้ลักษณะการทำงานที่ต้องการ (ของidคอลัมน์ที่คัดลอกเพื่อไม่เก็บIDENTITYคุณสมบัติอย่างไรก็ตามจะมีผลข้างเคียงของการไม่คัดลอกแถวเลย! (เช่นเดียวกับวิธีอื่น ๆ ) ดังนั้นคุณจะต้องทำดังนี้:

insert into without_id select * from with_id;

(ขอบคุณ AakashM!)


1

วิธีง่ายๆคือการทำให้ส่วนคอลัมน์ของนิพจน์

ตัวอย่าง:
ถ้าตาราง dbo.Employee มีข้อมูลประจำตัวในคอลัมน์ ID ในตัวอย่างด้านล่าง temp table #t จะมี IDENTITY ในคอลัมน์ ID ด้วย

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

เปลี่ยนค่านี้เพื่อใช้นิพจน์กับ ID และคุณ #t จะไม่มี IDENTITY ในคอลัมน์ ID อีกต่อไป ในกรณีนี้เราใช้การเพิ่มแบบง่ายกับคอลัมน์ ID

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

ตัวอย่างอื่นของนิพจน์สำหรับชนิดข้อมูลอื่นอาจรวมถึง: แปลง (), การต่อสตริงหรือIsnull ()


1
จากdocs.microsoft.com/en-us/sql/t-sql/queries/ … :“ เมื่อมีการเลือกคอลัมน์ข้อมูลประจำตัวที่มีอยู่ลงในตารางใหม่คอลัมน์ใหม่จะรับช่วงคุณสมบัติ IDENTITY เว้นแต่ว่าเงื่อนไขใด ๆ ต่อไปนี้เป็นจริง …คอลัมน์ข้อมูลประจำตัวเป็นส่วนหนึ่งของนิพจน์…คอลัมน์ถูกสร้างไม่ใช่ค่า NULL แทนที่จะสืบทอดคุณสมบัติตัวตน”
Manngo

1

บางครั้งคุณต้องการแทรกจากตารางที่คุณไม่รู้ (หรือสนใจ) ว่าคอลัมน์ถูกสร้างขึ้นโดยใช้ตัวตนหรือไม่ มันอาจไม่ได้เป็นคอลัมน์จำนวนเต็มที่คุณทำงานด้วย ในกรณีนี้สิ่งต่อไปนี้จะได้ผล:

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

ISNULL จะวางแอตทริบิวต์ IDENTITY จากคอลัมน์ แต่แทรกด้วยชื่อและประเภทเดียวกันกับคอลัมน์เดิมและทำให้ไม่เป็นโมฆะ TOP (0) จะสร้างตารางว่างที่คุณสามารถใช้เพื่อแทรกแถวที่เลือก คุณสามารถทำให้ตารางถูกบีบอัดก่อนที่คุณจะแทรกข้อมูลได้ถ้าต้องการ


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

จะลบตัวตน

ข้อเสียคือidสามารถเปลี่ยนได้ แต่คุณสามารถเพิ่มข้อ จำกัด นั้นได้


1
คุณสามารถใช้ ISNULL เพื่อหลีกเลี่ยงปัญหานั้น
Erik Darling

-2

คุณทำไม่ได้ select * intoรักษาเอกลักษณ์


2
*มีความต้องการในคำถามที่จะใช้งานไม่ได้
Martin Smith

2
และidentityคุณสมบัติไม่ได้ถูกสงวนไว้เสมอเหมือนคำตอบอื่น ๆ ที่ชี้ไป
ypercubeᵀᴹ
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.