Làm cách nào để sao chép bảng có CHỌN VÀO nhưng bỏ qua thuộc tính IDENTITY?


41

Tôi có một bảng với cột nhận dạng nói:

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

Nó được biết đến, rằng điều này

select * into copy_from_with_id_1 from with_id;

kết quả là copy_from_with_id_1 với danh tính trên id.

Câu hỏi tràn ngăn xếp sau đây đề cập đến việc liệt kê tất cả các cột một cách rõ ràng.

Hãy thử

select id, val into copy_from_with_id_2 from with_id;

Rất tiếc, ngay cả trong trường hợp này id là một cột danh tính.

Những gì tôi muốn là một bảng như

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

Câu trả lời:


52

Từ sách trực tuyến

Định dạng của new_table được xác định bằng cách đánh giá các biểu thức trong danh sách chọn. Các cột trong new_table được tạo theo thứ tự được chỉ định bởi danh sách chọn. Mỗi cột trong new_table có cùng tên, kiểu dữ liệu, tính không hợp lệ và giá trị là biểu thức tương ứng trong danh sách chọn. Thuộc tính IDENTITY của một cột được chuyển ngoại trừ theo các điều kiện được xác định trong "Làm việc với các cột định danh" trong phần Ghi chú.

Xuống trang:

Khi một cột danh tính hiện có được chọn vào một bảng mới, cột mới sẽ kế thừa thuộc tính IDENTITY, trừ khi một trong các điều kiện sau là đúng:

  • Câu lệnh SELECT chứa một mệnh đề nối, mệnh đề BY BY hoặc hàm tổng hợp.
  • Nhiều câu lệnh CHỌN được nối bằng cách sử dụng UNION.
  • Cột danh tính được liệt kê nhiều lần trong danh sách chọn.
  • Cột danh tính là một phần của biểu thức.
  • Cột nhận dạng là từ một nguồn dữ liệu từ xa.

Nếu bất kỳ một trong những điều kiện này là đúng, thì cột được tạo KHÔNG NULL thay vì kế thừa thuộc tính IDENTITY. Nếu một cột định danh được yêu cầu trong bảng mới nhưng cột đó không có sẵn hoặc bạn muốn giá trị hạt giống hoặc giá trị khác với cột nhận dạng nguồn, hãy xác định cột trong danh sách chọn bằng hàm IDENTITY. Xem "Tạo cột nhận dạng bằng chức năng IDENTITY" trong phần Ví dụ bên dưới.

Vì vậy, ... về mặt lý thuyết bạn có thể thoát khỏi:

select id, val 
into copy_from_with_id_2 
from with_id

union all

select 0, 'test_row' 
where 1 = 0;

Điều quan trọng là phải bình luận mã này để giải thích nó, vì sợ rằng nó sẽ bị xóa vào lần tới khi ai đó nhìn vào nó.


29

Lấy cảm hứng từ câu trả lời của Erics, tôi đã tìm thấy giải pháp sau chỉ phụ thuộc vào tên bảng và không sử dụng bất kỳ tên cột cụ thể nào:

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;

Chỉnh sửa

Thậm chí có thể cải thiện điều này để

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

13

Bạn có thể sử dụng phép nối để tạo và điền vào bảng mới trong một lần:

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

Do 1 = 0điều kiện, phía bên phải sẽ không có kết quả trùng khớp và do đó ngăn ngừa trùng lặp các hàng bên trái và vì đây là liên kết ngoài, nên các hàng bên trái cũng sẽ không bị loại bỏ. Cuối cùng, vì đây là một tham gia, thuộc tính IDENTITY bị loại bỏ.

Do đó, chỉ chọn các cột bên trái sẽ tạo ra một bản sao chính xác của dbo.TableWithIdentity data-khôn ngoan, tức là với thuộc tính IDENTITY bị loại bỏ.

Tất cả những gì đang được nói, Max Vernon đã đưa ra một điểm hợp lệ trong một nhận xét đáng để ghi nhớ. Nếu bạn nhìn vào kế hoạch thực hiện của truy vấn trên:

Kế hoạch thực hiện

bạn sẽ nhận thấy rằng bảng nguồn được đề cập trong kế hoạch thực hiện chỉ một lần. Ví dụ khác đã được loại bỏ bởi trình tối ưu hóa.

Vì vậy, nếu trình tối ưu hóa có thể thiết lập chính xác rằng bên phải của phép nối không cần thiết trong kế hoạch, thì sẽ hợp lý khi hy vọng rằng trong phiên bản tương lai của SQL Server, có thể không thể hiểu rằng thuộc tính IDENTITY không cần phải có đã bị xóa, vì không còn cột IDENTITY khác trong hàng nguồn được đặt theo kế hoạch truy vấn. Điều đó có nghĩa là truy vấn trên có thể ngừng hoạt động như mong đợi tại một số điểm.

Nhưng, như được lưu ý chính xác bởi ypercubeᵀᴹ , cho đến nay hướng dẫn đã tuyên bố rõ ràng rằng nếu có một tham gia, thuộc tính IDENTITY không được bảo tồn:

Khi một cột danh tính hiện có được chọn vào một bảng mới, cột mới sẽ kế thừa thuộc tính IDENTITY, trừ khi [...] [t] câu lệnh CHỌN của nó có chứa một phép nối.

Vì vậy, miễn là hướng dẫn sử dụng liên tục đề cập đến nó, có lẽ chúng ta có thể yên tâm rằng hành vi sẽ giữ nguyên.

Kudos đến Shaneisypercubeᵀᴹ vì đã đưa ra một chủ đề liên quan trong trò chuyện.


Cũng sẽ JOIN (SELECT 1) AS dummy ON 1 = 1làm việc chứ?
ypercubeᵀᴹ


5

Hãy thử mã này ..

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

Cuộc ISNULLgọi đảm bảo rằng cột mới được tạo với vô hiệu NOT NULL.


1
Nó là ISNULL()hay cái +0đó không? Hay cả hai đều cần thiết?
ypercubeᵀᴹ

3

Chỉ để thể hiện một cách khác nhau:

Bạn có thể sử dụng một máy chủ được liên kết .

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

Bạn có thể tạm thời tạo một máy chủ được liên kết đến máy chủ cục bộ bằng cách này:

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;

Tại thời điểm đó, bạn sẽ chạy select * intomã, tham chiếu tên localserverbốn phần của máy chủ được liên kết:

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

Sau khi hoàn thành, dọn sạch localservermáy chủ được liên kết với điều này:

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

Hoặc, bạn có thể sử dụng OPENQUERYcú pháp

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

1

Thuộc tính danh tính không được chuyển nếu câu lệnh chọn chứa tham gia, và vì vậy

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

cũng sẽ đưa ra hành vi mong muốn (của idcột được sao chép để không giữ thuộc IDENTITYtính. Tuy nhiên, nó sẽ có tác dụng phụ là không sao chép bất kỳ hàng nào cả! (như với một số phương pháp khác), do đó bạn sẽ cần phải làm:

insert into without_id select * from with_id;

(cảm ơn AakashM!)


1

Cách dễ dàng là tạo phần cột của biểu thức.

Ví dụ:
Nếu bảng dbo.Employee có danh tính trên cột ID thì trong ví dụ bên dưới bảng tạm thời #t cũng sẽ có IDENTITY trên cột ID.

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

Thay đổi điều này để áp dụng biểu thức cho ID và bạn #t sẽ không còn IDENTITY trên cột ID. Trong trường hợp này, chúng tôi áp dụng một bổ sung đơn giản cho cột ID.

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

Các ví dụ khác về biểu thức cho các loại dữ liệu khác có thể bao gồm: convert (), nối chuỗi hoặc Isnull ()


1
Từ docs.microsoft.com/en-us/sql/t-sql/queries/... : “Khi một cột sắc hiện có được chọn vào một bảng mới, cột mới được thừa hưởng tài sản SẮC, trừ khi một trong các điều kiện sau đây là đúng Cột nhận dạng là một phần của biểu thức, cột được tạo KHÔNG phải là NULL thay vì kế thừa thuộc tính IDENTITY.
Nhẫn

1

Đôi khi, bạn muốn chèn từ một bảng mà bạn không biết (hoặc quan tâm) nếu cột được tạo bằng IDENTITY hay không. Nó thậm chí có thể không phải là một cột số nguyên mà bạn đang làm việc. Trong trường hợp này, sau đây sẽ hoạt động:

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

ISNULL sẽ bỏ thuộc tính IDENTITY khỏi cột nhưng chèn nó với cùng tên và loại với cột ban đầu và cũng làm cho nó không thể rỗng. TOP (0) sẽ tạo một bảng trống mà sau đó bạn có thể sử dụng để chèn các hàng đã chọn vào. Bạn cũng có thể làm cho bảng được nén trước khi bạn chèn dữ liệu nếu cần.


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

sẽ xóa danh tính.

Nhược điểm là idtrở nên vô giá trị nhưng bạn có thể thêm ràng buộc đó.


1
Bạn có thể sử dụng ISNULL để có được xung quanh đó.
Erik Darling

-2

Bạn không. select * intogiữ gìn bản sắc.


2
Không có yêu cầu trong câu hỏi để sử dụng *.
Martin Smith

2
identitytài sản không phải luôn luôn được bảo tồn, như các câu trả lời khác chỉ ra.
ypercubeᵀᴹ
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.