Xác định biến để sử dụng với toán tử IN (T-SQL)


138

Tôi có một truy vấn Transact-SQL sử dụng toán tử IN. Một cái gì đó như thế này:

select * from myTable where myColumn in (1,2,3,4)

Có cách nào để xác định một biến để giữ toàn bộ danh sách "(1,2,3,4)" không? Tôi nên định nghĩa nó như thế nào?

declare @myList {data type}
set @myList = (1,2,3,4)
select * from myTable where myColumn in @myList

7
Câu hỏi này không giống với câu hỏi "Tham số hóa mệnh đề SQL IN". Câu hỏi này đề cập đến T-SQL bản địa, câu hỏi khác đề cập đến C #.
Slogmeister Extraordinaire

Câu trả lời:


113
DECLARE @MyList TABLE (Value INT)
INSERT INTO @MyList VALUES (1)
INSERT INTO @MyList VALUES (2)
INSERT INTO @MyList VALUES (3)
INSERT INTO @MyList VALUES (4)

SELECT *
FROM MyTable
WHERE MyColumn IN (SELECT Value FROM @MyList)

47
DECLARE @mylist TABLE (Id int)
INSERT INTO @mylist
SELECT id FROM (VALUES (1),(2),(3),(4),(5)) AS tbl(id)

SELECT * FROM Mytable WHERE theColumn IN (select id from @mylist)

T-SQL nói[Err] 42000 - [SQL Server]Must declare the scalar variable "@mylist".
Cees Timmerman

1
Đã sửa lỗi cho bạn @Paul
Stefan Z Camilleri

5
Bạn chỉ có thể sử dụng (VALUES (1),(2),(3),(4),(5))trực tiếp?
chập chững

Đây là giải pháp tốt nhất cho nhu cầu của tôi. Tôi cần một biến dưới dạng danh sách Id mà tôi nhận được từ Chọn để các giá trị không được xác định trước. Điều này hoàn thành chính xác những gì tôi cần. Cảm ơn!
Lexi847942

12

Có hai cách để xử lý danh sách csv động cho các truy vấn TSQL:

1) Sử dụng lựa chọn bên trong

SELECT * FROM myTable WHERE myColumn in (SELECT id FROM myIdTable WHERE id > 10)

2) Sử dụng TSQL kết hợp động

DECLARE @sql varchar(max)  
declare @list varchar(256)  
select @list = '1,2,3'  
SELECT @sql = 'SELECT * FROM myTable WHERE myColumn in (' + @list + ')'

exec sp_executeSQL @sql

3) Một tùy chọn thứ ba có thể là các biến bảng. Nếu bạn có SQl Server 2005, bạn có thể sử dụng biến bảng. Nếu trên Sql Server 2008 của bạn, bạn thậm chí có thể chuyển toàn bộ các biến của bảng thành tham số cho các thủ tục được lưu trữ và sử dụng nó trong một phép nối hoặc làm mục phụ trong mệnh đề IN.

DECLARE @list TABLE (Id INT)

INSERT INTO @list(Id)
SELECT 1 UNION ALL SELECT 2 UNION ALL SELECT 3 UNION ALL SELECT 4


SELECT
    * 
FROM 
    myTable
    JOIN @list l ON myTable.myColumn = l.Id

SELECT
    * 
FROM 
    myTable
WHERE
    myColumn IN (SELECT Id FROM @list)

5
@ badbod99 - Đó là một sự khái quát hóa và tất cả các khái quát đều sai :) Tôi đã đưa ra các lựa chọn thay thế
hollyststyle

1
@Vilx - bạn có nghĩa là để thiết lập biến @list? nếu vậy set là tốt nhưng chỉ đặt một biến, với select bạn có thể điền một vài biến trong một câu lệnh. Vì không có nhiều giữa họ nên tôi có thói quen luôn sử dụng CHỌN.
hollyststyle

1
Đúng ... rất chung chung. Thay thế của bạn là tốt hơn. Tôi thực sự có nghĩa là tạo SQL từ bên trong một tập lệnh SQL thường gây ra mã không thể nhầm lẫn, có nguy cơ bị tấn công tiêm chích và một loạt các sự khó chịu khác.
badbod99

9

Sử dụng một chức năng như thế này:

CREATE function [dbo].[list_to_table] (@list varchar(4000))
returns @tab table (item varchar(100))
begin

if CHARINDEX(',',@list) = 0 or CHARINDEX(',',@list) is null
begin
    insert into @tab (item) values (@list);
    return;
end


declare @c_pos int;
declare @n_pos int;
declare @l_pos int;

set @c_pos = 0;
set @n_pos = CHARINDEX(',',@list,@c_pos);

while @n_pos > 0
begin
    insert into @tab (item) values (SUBSTRING(@list,@c_pos+1,@n_pos - @c_pos-1));
    set @c_pos = @n_pos;
    set @l_pos = @n_pos;
    set @n_pos = CHARINDEX(',',@list,@c_pos+1);
end;

insert into @tab (item) values (SUBSTRING(@list,@l_pos+1,4000));

return;
end;

Thay vì sử dụng like, bạn tạo một phép nối bên trong với bảng được trả về bởi hàm:

select * from table_1 where id in ('a','b','c')

trở thành

select * from table_1 a inner join [dbo].[list_to_table] ('a,b,c') b on (a.id = b.item)

Trong bảng ghi 1M chưa được lập trình, phiên bản thứ hai mất khoảng một nửa thời gian ...

chúc mừng


5
DECLARE @myList TABLE (Id BIGINT) INSERT INTO @myList(Id) VALUES (1),(2),(3),(4);
select * from myTable where myColumn in(select Id from @myList)

Xin lưu ý rằng đối với danh sách dài hoặc hệ thống sản xuất, không nên sử dụng cách này vì nó có thể chậm hơn nhiều so với INtoán tử đơn giản như someColumnName in (1,2,3,4)(được thử nghiệm bằng cách sử dụng danh sách hơn 8000 mặt hàng)


4

Không, không có loại đó. Nhưng có một số lựa chọn:

  • Các truy vấn được tạo động (sp_executesql)
  • Bảng tạm thời
  • Biến kiểu bảng (thứ gần nhất có trong danh sách)
  • Tạo một chuỗi XML và sau đó chuyển đổi nó thành một bảng có các hàm XML (thực sự khó xử và làm tròn, trừ khi bạn có một XML để bắt đầu)

Không ai trong số này thực sự thanh lịch, nhưng đó là điều tốt nhất.


4

cải thiện một chút về @LukeH, không cần lặp lại câu "XÁC NHẬN VÀO": và câu trả lời của @ realPT - không cần phải có CHỌN:

DECLARE @MyList TABLE (Value INT) 
INSERT INTO @MyList VALUES (1),(2),(3),(4)

SELECT * FROM MyTable
WHERE MyColumn IN (SELECT Value FROM @MyList)

4

Tôi biết điều này đã cũ nhưng TSQL => 2016, bạn có thể sử dụng STRINGinksLIT:

DECLARE @InList varchar(255) = 'This;Is;My;List';

WITH InList (Item) AS (
    SELECT value FROM STRING_SPLIT(@InList, ';')
)

SELECT * 
FROM [Table]
WHERE [Item] IN (SELECT Tag FROM InList)

4

Bắt đầu với SQL2017, bạn có thể sử dụng STRINGinksLIT và thực hiện việc này:

declare @myList nvarchar(MAX)
set @myList = '1,2,3,4'
select * from myTable where myColumn in (select value from STRING_SPLIT(@myList,','))

2

Nếu bạn muốn làm điều này mà không cần sử dụng bảng thứ hai, bạn có thể thực hiện so sánh THÍCH với CAST:

DECLARE @myList varchar(15)
SET @myList = ',1,2,3,4,'

SELECT *
FROM myTable
WHERE @myList LIKE '%,' + CAST(myColumn AS varchar(15)) + ',%'

Nếu trường bạn đang so sánh đã là một chuỗi thì bạn sẽ không cần phải CAST.

Xung quanh cả khớp cột và từng giá trị duy nhất trong dấu phẩy sẽ đảm bảo khớp chính xác. Nếu không, giá trị 1 sẽ được tìm thấy trong danh sách chứa ', 4.2,15,'


1

Như chưa có ai đề cập trước đó, bắt đầu từ Sql Server 2016, bạn cũng có thể sử dụng mảng json và OPENJSON (Transact-SQL):

declare @filter nvarchar(max) = '[1,2]'

select *
from dbo.Test as t
where
    exists (select * from openjson(@filter) as tt where tt.[value] = t.id)

Bạn có thể kiểm tra nó trong sql fiddle demo

Bạn cũng có thể bao gồm các trường hợp phức tạp hơn với json dễ dàng hơn - xem danh sách Tìm kiếm các giá trị và phạm vi trong SQL bằng mệnh đề WHERE IN với biến SQL?


1

Cái này sử dụng PATINDEX để khớp id từ bảng với danh sách số nguyên được phân tách không có chữ số.

-- Given a string @myList containing character delimited integers 
-- (supports any non digit delimiter)
DECLARE @myList VARCHAR(MAX) = '1,2,3,4,42'

SELECT * FROM [MyTable]
    WHERE 
        -- When the Id is at the leftmost position 
        -- (nothing to its left and anything to its right after a non digit char) 
        PATINDEX(CAST([Id] AS VARCHAR)+'[^0-9]%', @myList)>0 
        OR
        -- When the Id is at the rightmost position
        -- (anything to its left before a non digit char and nothing to its right) 
        PATINDEX('%[^0-9]'+CAST([Id] AS VARCHAR), @myList)>0
        OR
        -- When the Id is between two delimiters 
        -- (anything to its left and right after two non digit chars)
        PATINDEX('%[^0-9]'+CAST([Id] AS VARCHAR)+'[^0-9]%', @myList)>0
        OR
        -- When the Id is equal to the list
        -- (if there is only one Id in the list)
        CAST([Id] AS VARCHAR)=@myList

Ghi chú:

  • khi truyền dưới dạng varchar và không chỉ định kích thước byte trong ngoặc đơn, độ dài mặc định là 30
  • % (ký tự đại diện) sẽ khớp với bất kỳ chuỗi nào có 0 hoặc nhiều ký tự
  • ^ (ký tự đại diện) không khớp
  • [^ 0-9] sẽ khớp với bất kỳ ký tự không có chữ số nào
  • PATINDEX là một hàm tiêu chuẩn SQL trả về vị trí của một mẫu trong chuỗi

0
DECLARE @StatusList varchar(MAX);
SET @StatusList='1,2,3,4';
DECLARE @Status SYS_INTEGERS;
INSERT INTO  @Status 
SELECT Value 
FROM dbo.SYS_SPLITTOINTEGERS_FN(@StatusList, ',');
SELECT Value From @Status;

5
nó sẽ là một câu trả lời tốt hơn nếu bạn mô tả mã của bạn ở đó!
Sâu Kakkar

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.