ตัวแปร SQL เพื่อเก็บรายการของจำนวนเต็ม


175

ฉันพยายามที่จะดีบักรายงาน SQL ของคนอื่นและวางคิวรีรายงานพื้นฐานลงในหน้าต่างแบบสอบถามของ SQL 2012

หนึ่งในพารามิเตอร์ที่รายงานถามคือรายการจำนวนเต็ม สิ่งนี้สามารถทำได้ในรายงานผ่านกล่องดร็อปดาวน์แบบเลือกได้หลายแบบ แบบสอบถามพื้นฐานของรายงานใช้รายการจำนวนเต็มนี้ในwhereข้อเช่น

select *
from TabA
where TabA.ID in (@listOfIDs)

ฉันไม่ต้องการแก้ไขแบบสอบถามที่ฉันกำลังดีบั๊ก แต่ฉันไม่สามารถหาวิธีสร้างตัวแปรบน SQL Server ที่สามารถเก็บข้อมูลประเภทนี้เพื่อทดสอบได้

เช่น

declare @listOfIDs int
set listOfIDs  = 1,2,3,4

ไม่มีประเภทข้อมูลที่สามารถเก็บรายการจำนวนเต็มดังนั้นฉันจะเรียกใช้แบบสอบถามรายงานใน SQL Server ของฉันด้วยค่าเดียวกันกับรายงานได้อย่างไร


1
ฉันรู้ว่าฉันใช้ TVP Table Value Parmeter ในการแทรกข้อมูล แต่ตอนนี้แน่ใจว่าสามารถใช้ได้ในที่ใด ผลสืบเนื่อง?
paparazzo

2
คำถามที่พูดดี +1
RayLoveless

คำตอบ:


224

ตัวแปรตาราง

declare @listOfIDs table (id int);
insert @listOfIDs(id) values(1),(2),(3);    

select *
from TabA
where TabA.ID in (select id from @listOfIDs)

หรือ

declare @listOfIDs varchar(1000);
SET @listOfIDs = ',1,2,3,'; --in this solution need put coma on begin and end

select *
from TabA
where charindex(',' + CAST(TabA.ID as nvarchar(20)) + ',', @listOfIDs) > 0

2
ขอบคุณสำหรับสิ่งนั้น แต่อีกครั้งฉันต้องเขียนวิธีการอ่านตัวแปรในแบบสอบถามอีกครั้ง ฉันต้องเก็บไว้เหมือนกัน
ErickTreetops

3
ถ้าคุณไม่ทราบว่า ID คืออะไรและมาจากการสืบค้น ตัวอย่าง: SET @AddressIDs = (SELECT ID FROM address WHERE Account = 1234)แบบสอบถามนี้จะส่งคืน ID หลายรายการและฉันได้รับข้อผิดพลาดว่าแบบสอบถามย่อยส่งคืนผลลัพธ์มากกว่าหนึ่งรายการและไม่ได้รับอนุญาต อย่างไรก็ตามมีการสร้างตัวแปรที่จะเก็บอาร์เรย์ถ้ารหัสจากแบบสอบถามย่อยหรือไม่
Rafael Moreira

1
ฉันลองตัวเลือกที่สองและทำงานกับจำนวนระเบียนน้อยลง เมื่อฉันเพิ่มจำนวนรหัสฉันได้รับข้อผิดพลาด TimeOut บางทีนักแสดงอาจขัดขวางการแสดง
ผู้ใช้ M

ไม่ใช่คำตอบของคำถามเดิม แต่เป็นคำตอบที่ฉันไม่ได้ถามดังนั้นฉันจึงดี ฉันผ่านในรายการ <int> เป็นพารามิเตอร์ แต่ต้องการสร้างตัวแปรท้องถิ่นสำหรับการทดสอบใน SSMS มันเป็นนักฆ่า
ลุย Hatler

คำตอบที่ดีช่วยให้ฉันประหยัดเวลาได้มาก
Alfredo A.

35

สมมติว่าตัวแปรเป็นสิ่งที่คล้ายกับ:

CREATE TYPE [dbo].[IntList] AS TABLE(
[Value] [int] NOT NULL
)

และกระบวนงานที่เก็บไว้ใช้ในรูปแบบนี้:

ALTER Procedure [dbo].[GetFooByIds]
    @Ids [IntList] ReadOnly
As 

คุณสามารถสร้าง IntList และเรียกขั้นตอนดังนี้:

Declare @IDs IntList;
Insert Into @IDs Select Id From dbo.{TableThatHasIds}
Where Id In (111, 222, 333, 444)
Exec [dbo].[GetFooByIds] @IDs

หรือถ้าคุณให้บริการ IntList ด้วยตัวคุณเอง

DECLARE @listOfIDs dbo.IntList
INSERT INTO @listofIDs VALUES (1),(35),(118);

17

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

DECLARE @listOfIDs varchar(8000);
SET @listOfIDs = '1,2,3,4';

จากนั้นคุณสามารถแยกสตริงออกเป็นค่าจำนวนเต็มแยกและวางลงในตาราง ขั้นตอนของคุณอาจทำไปแล้ว

คุณยังสามารถใช้แบบสอบถามแบบไดนามิกเพื่อให้ได้ผลลัพธ์เดียวกัน:

DECLARE @SQL nvarchar(8000);

SET @SQL = 'SELECT * FROM TabA WHERE TabA.ID IN (' + @listOfIDs + ')';
EXECUTE (@SQL);

ขอบคุณ แต่ต้องแก้ไขแบบสอบถามอีกครั้งซึ่งฉันไม่ได้รับอนุญาต
ErickTreetops

2
หากใครก็ตามที่ใช้สิ่งนี้โปรดทราบว่านี่อาจมีความเสี่ยงสูงต่อการฉีด SQL หาก @listOfIDs เป็นพารามิเตอร์สตริงที่จัดทำโดยผู้ใช้ ขึ้นอยู่กับสถาปัตยกรรมของแอปของคุณสิ่งนี้อาจเป็นไปได้หรือไม่มีปัญหา
Rogala

@Rogala ตกลงผู้ใช้จะต้องทำสุขาภิบาลของตัวเองตามที่ต้องการ
Möoz

1
@ Möozฉันขอแนะนำให้เพิ่มบันทึกลงในคำตอบของคุณเพื่อสะท้อนถึงสิ่งนี้ ไม่ใช่ทุกคนที่รู้และพวกเขาเพียงแค่คัดลอกและวางโซลูชันโดยไม่ต้องนึกถึงผลที่ตามมา Dynamic SQL นั้นอันตรายมากและฉันก็หลีกเลี่ยงมันเหมือนกับโรคระบาด
Rogala

@ Möozอีกทั้งฉันไม่ได้ลงคะแนนโหวตคำตอบของคุณ แต่สิ่งที่คุณโปรดใช้เวลาสักครู่และตรวจสอบคำตอบของฉัน? ฟังก์ชั่น STRING_SPLIT ค่อนข้างหวานและฉันคิดว่าคุณจะอยู่เหนือทุกอย่างเลย !!
Rogala

6

สำหรับ SQL Server 2016+ และฐานข้อมูล Azure SQL ฟังก์ชัน STRING_SPLIT ถูกเพิ่มเข้ามาซึ่งจะเป็นโซลูชันที่สมบูรณ์แบบสำหรับปัญหานี้ นี่คือเอกสาร: https://docs.microsoft.com/en-us/sql/t-sql/functions/string-split-transact-sql

นี่คือตัวอย่าง:

/*List of ids in a comma delimited string
  Note: the ') WAITFOR DELAY ''00:00:02''' is a way to verify that your script 
        doesn't allow for SQL injection*/
DECLARE @listOfIds VARCHAR(MAX) = '1,3,a,10.1,) WAITFOR DELAY ''00:00:02''';

--Make sure the temp table was dropped before trying to create it
IF OBJECT_ID('tempdb..#MyTable') IS NOT NULL DROP TABLE #MyTable;

--Create example reference table
CREATE TABLE #MyTable
([Id] INT NOT NULL);

--Populate the reference table
DECLARE @i INT = 1;
WHILE(@i <= 10)
BEGIN
    INSERT INTO #MyTable
    SELECT @i;

    SET @i = @i + 1;
END

/*Find all the values
  Note: I silently ignore the values that are not integers*/
SELECT t.[Id]
FROM #MyTable as t
    INNER JOIN 
        (SELECT value as [Id] 
        FROM STRING_SPLIT(@listOfIds, ',')
        WHERE ISNUMERIC(value) = 1 /*Make sure it is numeric*/
            AND ROUND(value,0) = value /*Make sure it is an integer*/) as ids
    ON t.[Id] = ids.[Id];

--Clean-up
DROP TABLE #MyTable;

ผลลัพธ์ของแบบสอบถามคือ 1,3

~ ไชโย


4

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

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


6
SQL Server ไม่จำเป็นต้องใช้อาร์เรย์เมื่อมันมีพารามิเตอร์และตัวแปรที่มีค่าเป็นตาราง
จอห์นแซนเดอร์

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

2

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

ตัวอย่างเช่น:

DECLARE @listOfIDs NVARCHAR(MAX) = 
    '1,2,3'

DECLARE @query NVARCHAR(MAX) = 
    'Select *
     From TabA
     Where TabA.ID in (' + @listOfIDs + ')'

Exec (@query)

2
ตามที่ระบุไว้ในความคิดเห็นก่อนหน้านี้ขึ้นอยู่กับวิธีที่คุณใช้วิธีการแก้ปัญหาประเภทนี้โปรดทราบว่าสิ่งนี้อาจมีความเสี่ยงต่อการฉีด SQL หาก @listOfIDs เป็นพารามิเตอร์ที่ผู้ใช้กำหนด
Rogala

2

มีฟังก์ชั่นใหม่ใน SQL ที่เรียกว่าstring_splitถ้าคุณใช้รายการสตริง ลิงก์อ้างอิงSTRING_SPLIT (Transact-SQL)

DECLARE @tags NVARCHAR(400) = 'clothing,road,,touring,bike'
SELECT value
FROM STRING_SPLIT(@tags, ',')
WHERE RTRIM(value) <> '';

คุณสามารถส่งแบบสอบถามนี้ด้วยinดังนี้:

SELECT *
  FROM [dbo].[yourTable]
  WHERE (strval IN (SELECT value FROM STRING_SPLIT(@tags, ',') WHERE RTRIM(value) <> ''))

1
ดูน่าเชื่อถือ แต่มี SQL Server 2016 ขึ้นไปเท่านั้น
AnotherFineMess

0

ฉันใช้สิ่งนี้:

1- ประกาศตัวแปรตารางชั่วคราวในสคริปต์สิ่งปลูกสร้างของคุณ:

DECLARE @ShiftPeriodList TABLE(id INT NOT NULL);

2-Allocate to temp table:

IF (SOME CONDITION) 
BEGIN 
        INSERT INTO @ShiftPeriodList SELECT ShiftId FROM [hr].[tbl_WorkShift]
END
IF (SOME CONDITION2)
BEGIN
    INSERT INTO @ShiftPeriodList
        SELECT  ws.ShiftId
        FROM [hr].[tbl_WorkShift] ws
        WHERE ws.WorkShift = 'Weekend(VSD)' OR ws.WorkShift = 'Weekend(SDL)'

END

อ้างอิงตาราง 3 เมื่อคุณต้องการในคำสั่ง WHERE:

INSERT INTO SomeTable WHERE ShiftPeriod IN (SELECT * FROM @ShiftPeriodList)
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.