CHỌN VÀO một biến bảng trong T-SQL


372

Có một truy vấn CHỌN phức tạp, từ đó tôi muốn chèn tất cả các hàng vào một biến bảng, nhưng T-SQL không cho phép nó.

Dọc theo cùng một dòng, bạn không thể sử dụng biến bảng với các truy vấn SELECT INTO hoặc INSERT EXEC. http://odetocode.com/Articles/365.aspx

Ví dụ ngắn:

declare @userData TABLE(
                        name varchar(30) NOT NULL,
                        oldlocation varchar(30) NOT NULL
                       )

SELECT name, location
INTO @userData
FROM myTable
    INNER JOIN otherTable ON ...
WHERE age > 30

Dữ liệu trong biến bảng sau đó sẽ được sử dụng để chèn / cập nhật lại vào các bảng khác nhau (chủ yếu là sao chép cùng một dữ liệu với các cập nhật nhỏ). Mục tiêu của việc này chỉ đơn giản là làm cho tập lệnh dễ đọc hơn một chút và dễ dàng tùy chỉnh hơn là thực hiện SELECT INTOtrực tiếp vào các bảng bên phải. Hiệu suất không phải là một vấn đề, vì rowcountnó khá nhỏ và nó chỉ chạy thủ công khi cần thiết.
... Hoặc chỉ cho tôi biết nếu tôi làm sai tất cả.

Câu trả lời:


601

Hãy thử một cái gì đó như thế này:

DECLARE @userData TABLE(
    name varchar(30) NOT NULL,
    oldlocation varchar(30) NOT NULL
);

INSERT INTO @userData (name, oldlocation)
SELECT name, location FROM myTable
INNER JOIN otherTable ON ...
WHERE age > 30;

2
Nếu bạn "CHỌN tên, vị trí TỪ myTable" làm các giá trị bạn sẽ chèn vào bảng UserData thì không thành vấn đề nếu tên của các biến trong lựa chọn khớp với tên trong định nghĩa bảng. Bạn đang chọn 'name' để đi vào biến 'tên' của UserData nhưng bạn đang chọn 'location' và bằng cách nào đó gán nó cho biến UserData 'oldlocation'. SQL sẽ tự động ánh xạ những thứ này hay nó sẽ đưa ra một loại ngoại lệ nào đó?
Aran Mulholland

Nó không quan trọng tên, chỉ có loại cột.
CristiC

5
Wow điều đó có ý nghĩa nhưng đồng thời trình phân tích cú pháp trong tôi cảm thấy bị xúc phạm :)
Aran Mulholland

Tôi dường như không thể sử dụng điều này trong (các) câu lệnh CẬP NHẬT: liên kết chính
Paul-Sebastian Manole

1
Trong một câu lệnh chèn, nếu bạn không khai báo các cột một cách rõ ràng, thì chúng được ánh xạ theo thứ tự được khai báo trong câu lệnh tạo bảng gốc, giống như select *. Vì vậy, vị trí trong câu lệnh select được ánh xạ tới vị trí cũ trong bảng @userData vì vị trí nằm ở vị trí 2 trong tập kết quả của lựa chọn và vị trí cũ là cột 2 trong định nghĩa bảng. Điều đó nói rằng, đừng bao giờ làm điều này. Thứ tự cơ sở dữ liệu của các cột hoặc hàng không được dựa vào. Luôn luôn rõ ràng về điều này.
thợ rèn

94

Mục đích của SELECT INTOlà (theo các tài liệu, sự nhấn mạnh của tôi)

Để tạo một bảng mới từ các giá trị trong một bảng khác

Nhưng bạn đã một bảng mục tiêu! Vì vậy, những gì bạn muốn là

Câu INSERTlệnh thêm một hoặc nhiều hàng mới vào bảng

Bạn có thể chỉ định các giá trị dữ liệu theo các cách sau:

...

Bằng cách sử dụng SELECTtruy vấn con để chỉ định các giá trị dữ liệu cho một hoặc nhiều hàng, chẳng hạn như:

  INSERT INTO MyTable 
 (PriKey, Description)
        SELECT ForeignKey, Description
        FROM SomeView

Và trong cú pháp này , nó được phép MyTabletrở thành một biến bảng.


1
Thực sự muốn câu trả lời được chấp nhận bao gồm thông tin này!
Davie Brown

Tôi nhận MyTable là "Tên đối tượng không hợp lệ" đang làm điều này, vì vậy có một cái gì đó bị thiếu trong câu trả lời này.
Mike Flynn

@MikeFlynn MyTableở đây là một trình giữ chỗ cho tên của bảng thực tế của bạn . Tôi không nghĩ có bất kỳ cơ sở dữ liệu thực sự nào có bảng có tên ...MyTable
AakashM

Và nếu tôi muốn tạo / khai báo một biến bảng với CHỌN VÀO ...? Ví dụ: để xác định các cột của biến bảng là t1.somecolumn, t1.othercolumn, t2. *
Armando

27

Bạn cũng có thể sử dụng các biểu thức bảng chung để lưu trữ các bộ dữ liệu tạm thời. Họ thanh lịch và thân thiện hơn:

WITH userData (name, oldlocation)
AS
(
  SELECT name, location 
  FROM   myTable    INNER JOIN 
         otherTable ON ...
  WHERE  age>30
)
SELECT * 
FROM   userData -- you can also reuse the recordset in subqueries and joins

Thích cái này! Cảm ơn bạn.
Fourpastmidnight 2/218

Tôi không nghĩ rằng điều này tạo ra một bản sao, nếu bạn xóa hoặc cập nhật từ userData thì nó sẽ không xóa và cập nhật các bản ghi trong bảng gốc của bạn chứ?
atreeon

Có, XÓA và CẬP NHẬT trên CTE sẽ sửa đổi bảng nguồn miễn là CTE không tham chiếu nhiều bảng bằng cách sử dụng các phép nối, liên hiệp, v.v.
nanestev

2
Nhược điểm của việc này là bạn chỉ có thể sử dụng bảng CTE trong các lệnh ngay sau đây. Nếu bạn cần thực hiện nhiều hơn một lần thông qua tập kết quả vì bất kỳ lý do gì CTE sẽ không hoạt động. OP dường như ngụ ý rằng nhiều sửa đổi sẽ được thực hiện, trong trường hợp này sẽ không hoạt động - "Dữ liệu trong biến bảng sau đó sẽ được sử dụng để chèn / cập nhật lại vào các bảng khác nhau (chủ yếu là sao chép cùng một dữ liệu với dữ liệu phụ cập nhật). "
Tony

16

Bạn có thể thử sử dụng các bảng tạm thời ... nếu bạn không làm điều đó từ một ứng dụng. (Có thể ổn khi chạy thủ công)

SELECT name, location INTO #userData FROM myTable
INNER JOIN otherTable ON ...
WHERE age>30

Bạn bỏ qua nỗ lực khai báo bảng theo cách đó ... Giúp cho các truy vấn adhoc ... Điều này tạo ra một bảng tạm thời cục bộ sẽ không hiển thị cho các phiên khác trừ khi bạn ở trong cùng một phiên. Có thể một vấn đề nếu bạn đang chạy truy vấn từ một ứng dụng.

nếu bạn yêu cầu nó chạy trên một ứng dụng, hãy sử dụng các biến được khai báo theo cách này:

DECLARE @userData TABLE(
    name varchar(30) NOT NULL,
    oldlocation varchar(30) NOT NULL
);

INSERT INTO @userData
SELECT name, location FROM myTable
INNER JOIN otherTable ON ...
WHERE age > 30;

Chỉnh sửa: như nhiều bạn đã đề cập đến khả năng hiển thị được cập nhật cho phiên từ kết nối. Tạo bảng tạm thời không phải là một tùy chọn cho các ứng dụng web, vì các phiên có thể được sử dụng lại, bám vào các biến tạm thời trong các trường hợp đó


2
Xin lỗi, quên đề cập đến việc tôi không có quyền TẠO BẢNG.
Indrek

6
Tạo một temp có thêm một chút chi phí.
paparazzo

2
sử dụng bảng tạm thời không phải lúc nào cũng an toàn. Ví dụ, các dịch vụ web. Với các dịch vụ web có một kết nối duy nhất để giới hạn kết nối tối đa trên máy chủ VÀ bảo vệ SQL thêm một chút, bảng tạm thời sẽ tồn tại cho MỌI truy vấn đi qua và có thể ghi đè lên ai đó hiện đang sử dụng nó.
Franck

12
@Franck - nếu bạn sử dụng bảng tạm thời toàn cầu (hai tiền tố băm), bạn đã đúng. Tuy nhiên, một bảng tạm thời cục bộ (một tiền tố băm) sẽ được tách ra thành một phiên duy nhất (còn gọi là kết nối đơn), do đó sẽ không có vấn đề tương tranh mà bạn đang ám chỉ trừ khi bạn sử dụng một kết nối duy nhất cho tất cả các yêu cầu (không phải khuyên). Ý nghĩa hiệu suất có thể vẫn còn, mặc dù.
maf748

@GazB Chắc chắn, bất kỳ tuyên bố nào có tác dụng phụ đều bị loại trừ khỏi việc sử dụng trong a function. Theo kinh nghiệm của tôi, trong hầu hết các trường hợp ai đó nghĩ rằng họ cần những tuyên bố như vậy, điều này thực sự có nghĩa là họ nên suy nghĩ lại về họ function- hoặc ít nhất là tái cấu trúc thành a procedure. Nói cho bản thân tôi, ít nhất. :-)
gạch dưới

8

Cố gắng sử dụng INSERTthay vì SELECT INTO:

INSERT @UserData   
SELECT name, location etc.

5

Đầu tiên tạo một bảng tạm thời:

Bước 1:

create table #tblOm_Temp (

    Name varchar(100),
    Age Int ,
    RollNumber bigint
)

** Bước 2: ** Chèn một số giá trị vào bảng Temp.

insert into #tblom_temp values('Om Pandey',102,1347)

Bước 3: Khai báo một bảng Biến để giữ dữ liệu bảng tạm thời.

declare   @tblOm_Variable table(

    Name Varchar(100),
    Age int,
    RollNumber bigint
)

Bước 4: chọn giá trị từ bảng tạm thời và chèn vào biến bảng.

insert into @tblOm_Variable select * from #tblom_temp

Cuối cùng giá trị được chèn từ một bảng tạm thời vào biến Bảng

Bước 5: Có thể Kiểm tra giá trị chèn trong biến bảng.

select * from @tblOm_Variable

1

OK, bây giờ với đủ nỗ lực tôi có thể chèn vào @table bằng cách sử dụng bên dưới:

INSERT @TempWithheldTable CHỌN
a.SuspendedReason, a.SuspendedNotes, a.SuspendedBy, a.ReasonCode TỪ OPENROWSET (BULK 'C: \ Databases \ WithHeld.csv', FORMATFILE = N'C: \ Databases \ Format.txt',
ERRORFILE = N'C: \ Temp \ MovieLensRatings.txt ') NHƯ a;

Điều chính ở đây là chọn cột để chèn.


Tôi nhận được thông báo lỗi biên dịch 'Phải khai báo biến bảng "@TempWithainedTable"
atreeon

-5

Một lý do để sử dụng CHỌN VÀO là nó cho phép bạn sử dụng IDENTITY:

SELECT IDENTITY(INT,1,1) AS Id, name
INTO #MyTable 
FROM (SELECT name FROM AnotherTable) AS t

Điều này sẽ không hoạt động với một biến bảng, quá tệ ...


6
Bạn có thể khai báo một biến bảng với một IDENTITYcột mặc dù.
Martin Smith
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.