กำหนดตัวแปรที่จะใช้กับตัวดำเนินการ IN (T-SQL)


138

ฉันมีคิวรี Transact-SQL ที่ใช้ตัวดำเนินการ IN บางสิ่งเช่นนี้

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

มีวิธีกำหนดตัวแปรเพื่อเก็บรายการทั้งหมด "(1,2,3,4)" หรือไม่? ฉันควรกำหนดมันอย่างไร

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

7
คำถามนี้ไม่เหมือนคำถาม "Parameterize an SQL IN clause" คำถามนี้หมายถึง Native T-SQL คำถามอื่นอ้างอิงถึง C #
Slogmeister Extraordinaire

คำตอบ:


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 พูดว่า[Err] 42000 - [SQL Server]Must declare the scalar variable "@mylist".
Cees Timmerman

1
แก้ไขให้คุณ @Paul
Stefan Z Camilleri

5
คุณสามารถใช้(VALUES (1),(2),(3),(4),(5))โดยตรงได้ไหม
toddmo

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

12

มีสองวิธีในการจัดการรายการ csv แบบไดนามิกสำหรับการสืบค้น TSQL:

1) การใช้ตัวเลือกภายใน

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

2) การใช้ TSQL แบบแบ่งส่วนแบบไดนามิก

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) ตัวเลือกที่สามที่เป็นไปได้คือตัวแปรตาราง หากคุณมี SQl Server 2005 คุณสามารถใช้ตัวแปรตารางได้ หากใน Sql Server 2008 ของคุณคุณสามารถส่งผ่านตัวแปรทั้งตารางเป็นพารามิเตอร์ไปยังโพรซีเดอร์ที่เก็บไว้และใช้ในการเข้าร่วมหรือเป็นการเลือกย่อยในข้อ 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 - การวางนัยทั่วไปและการสรุปทั้งหมดผิด :) ฉันได้เสนอทางเลือกอื่น ๆ อีกมากมาย
hollystyles

1
@Vilx - คุณหมายถึงการตั้งค่าตัวแปร @list หรือไม่ ถ้าตั้งเป็นอย่างดี แต่ตั้งเพียงหนึ่งตัวแปรด้วยการเลือกคุณสามารถเติมหลายตัวแปรในหนึ่งคำสั่ง เนื่องจากมีไม่มากระหว่างพวกเขาฉันจึงมักจะเลือกใช้ SELECT
hollystyles

1
จริง ... ธรรมดามาก ทางเลือกของคุณดีกว่า ฉันหมายถึงการสร้าง SQL จากภายในสคริปต์ SQL โดยปกติแล้วจะทำให้เกิดโค้ดที่ไม่สามารถรักษาได้, ความเสี่ยงในการถูกโจมตีจากการฉีดและความน่ารังเกียจอื่น ๆ
badbod99

9

ใช้ฟังก์ชั่นเช่นนี้:

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;

แทนการใช้ like คุณสร้างการรวมภายในกับตารางที่ส่งคืนโดยฟังก์ชัน:

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

กลายเป็น

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

ในตารางบันทึก 1M ที่ไม่มีการทำดัชนีรุ่นที่สองใช้เวลาประมาณครึ่ง ...

ไชโย


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)

โปรดทราบว่าสำหรับรายการที่มีขนาดยาวหรือระบบการผลิตไม่แนะนำให้ใช้วิธีนี้เนื่องจากอาจช้ากว่าINตัวดำเนินการธรรมดาอย่างมากsomeColumnName in (1,2,3,4)(ทดสอบโดยใช้รายการมากกว่า 8000 รายการ)


4

ไม่ไม่มีประเภทดังกล่าว แต่มีตัวเลือกบางอย่าง:

  • ข้อความค้นหาที่สร้างขึ้นแบบไดนามิก (sp_executesql)
  • ตารางชั่วคราว
  • ตัวแปรชนิดตาราง (สิ่งที่ใกล้เคียงที่สุดที่มีในรายการ)
  • สร้างสตริง XML จากนั้นแปลงเป็นตารางด้วยฟังก์ชั่น XML (น่าอึดอัดใจจริงๆและวงเวียนยกเว้นว่าคุณมี XML ที่จะเริ่มต้นด้วย)

ไม่มีของเหล่านี้มีความสง่างามจริงๆ แต่นั่นเป็นสิ่งที่ดีที่สุด


4

การปรับปรุงเล็กน้อยใน @LukeH ไม่จำเป็นต้องทำซ้ำ "INSERT INTO": และคำตอบของ @ realPT - ไม่จำเป็นต้องเลือก:

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

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

4

ฉันรู้ว่ามันเก่าแล้ว แต่ TSQL => 2016 คุณสามารถใช้ STRING_SPLIT:

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

เริ่มต้นด้วย SQL2017 คุณสามารถใช้STRING_SPLITและทำสิ่งนี้:

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

2

หากคุณต้องการทำสิ่งนี้โดยไม่ใช้ตารางที่สองคุณสามารถทำการเปรียบเทียบ LIKE กับ CAST:

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

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

หากช่องที่คุณกำลังเปรียบเทียบเป็นสตริงอยู่แล้วคุณไม่จำเป็นต้องใช้ CAST

ล้อมรอบทั้งการจับคู่คอลัมน์และค่าที่ไม่ซ้ำกันแต่ละรายการในเครื่องหมายจุลภาคจะช่วยให้มั่นใจได้ถึงการจับคู่ที่แน่นอน มิฉะนั้นจะพบค่า 1 ในรายการที่มี ', 4,2,15,'


1

อย่างที่ไม่มีใครพูดถึงมาก่อนเริ่มจาก Sql Server 2016 คุณยังสามารถใช้อาร์เรย์ json และ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)

คุณสามารถทดสอบได้ sql fiddle demo

คุณสามารถครอบคลุมกรณีที่ซับซ้อนมากขึ้นด้วย json ง่ายขึ้น - ดูรายการค้นหาค่าและช่วงใน SQL โดยใช้ WHERE IN clause กับตัวแปร SQL?


1

อันนี้ใช้ PATINDEX เพื่อจับคู่รหัสจากตารางกับรายการจำนวนเต็มแบบไม่มีตัวคั่น

-- 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

หมายเหตุ:

  • เมื่อแคสต์เป็น varchar และไม่ได้ระบุขนาดไบต์ในวงเล็บความยาวเริ่มต้นคือ 30
  • % (อักขระตัวแทน) จะจับคู่กับสตริงของอักขระศูนย์หรือมากกว่านั้น
  • ^ (ไวด์การ์ด) เพื่อไม่ให้ตรงกัน
  • [^ 0-9] จะตรงกับอักขระที่ไม่ใช่ตัวเลขใด ๆ
  • PATINDEX เป็นฟังก์ชันมาตรฐาน SQL ที่ส่งคืนตำแหน่งของรูปแบบในสตริง

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
มันจะเป็นคำตอบที่ดีกว่าถ้าคุณอธิบายรหัสของคุณที่นั่น!
Deep Kakkar

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