Biến SQL để giữ danh sách các số nguyên


174

Tôi đang cố gắng gỡ lỗi các báo cáo SQL của người khác và đã đặt truy vấn báo cáo cơ bản vào một cửa sổ truy vấn của SQL 2012.

Một trong những tham số mà báo cáo yêu cầu là danh sách các số nguyên. Điều này đạt được trên báo cáo thông qua hộp thả xuống đa lựa chọn. Truy vấn cơ bản của báo cáo sử dụng danh sách số nguyên này trong wheremệnh đề, vd

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

Tôi không muốn sửa đổi truy vấn tôi đang gỡ lỗi nhưng tôi không thể tìm ra cách tạo một biến trên Máy chủ SQL có thể chứa loại dữ liệu này để kiểm tra nó.

ví dụ

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

Không có kiểu dữ liệu nào có thể chứa danh sách các số nguyên, vậy làm cách nào tôi có thể chạy truy vấn báo cáo trên Máy chủ SQL của mình với cùng các giá trị như báo cáo?


1
Tôi biết tôi đã sử dụng Parmeter Bảng giá trị bảng TVP để chèn dữ liệu nhưng bây giờ chắc chắn nếu nó có thể được sử dụng ở một nơi. Phần tiếp theo?
paparazzo

2
câu hỏi tốt. +1
RayLovless

Câu trả lời:


224

Bảng biến

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

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

hoặc là

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
Cảm ơn vì điều đó nhưng một lần nữa nó yêu cầu tôi viết lại cách đọc biến trong truy vấn. Tôi phải giữ nó như cũ.
ErickTreetops

3
Điều gì xảy ra nếu bạn không biết ID là gì và xuất phát từ truy vấn? Ví dụ: SET @AddressIDs = (SELECT ID FROM address WHERE Account = 1234)Truy vấn này sẽ trả về nhiều ID và tôi gặp lỗi khi nói truy vấn con trả về nhiều hơn một kết quả và điều đó không được phép. Có cách nào để tạo một biến sẽ lưu trữ một mảng nếu ID từ truy vấn con không?
Rafael Moreira

1
Tôi đã thử tùy chọn thứ hai và nó hoạt động với số lượng hồ sơ ít hơn. Khi tôi tăng số lượng Id, tôi gặp lỗi TimeOut. Có lẽ dàn diễn viên đang cản trở màn trình diễn.
Người dùng M

Không phải câu trả lời cho câu hỏi ban đầu mà là câu trả lời cho câu hỏi tôi không hỏi, vì vậy tôi rất tốt. Tôi chuyển vào Danh sách <int> dưới dạng tham số, nhưng muốn tạo biến cục bộ để kiểm tra trong SSMS. Đó là kẻ giết người.
Wade Hatler

Câu trả lời tuyệt vời, đã tiết kiệm cho tôi rất nhiều thời gian
Alfredo A.

35

Giả sử biến là một cái gì đó giống như:

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

Và Thủ tục lưu trữ đang sử dụng nó trong mẫu này:

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

Bạn có thể tạo IntList và gọi thủ tục như vậy:

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

Hoặc nếu bạn đang tự cung cấp IntList

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

17

Bạn đã đúng, không có kiểu dữ liệu nào trong SQL-Server có thể chứa danh sách các số nguyên. Nhưng những gì bạn có thể làm là lưu trữ một danh sách các số nguyên dưới dạng một chuỗi.

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

Sau đó, bạn có thể chia chuỗi thành các giá trị nguyên riêng biệt và đặt chúng vào một bảng. Thủ tục của bạn có thể đã làm điều này.

Bạn cũng có thể sử dụng truy vấn động để đạt được kết quả tương tự:

DECLARE @SQL nvarchar(8000);

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

Cảm ơn, nhưng một lần nữa sẽ cần phải sửa đổi một truy vấn mà tôi không được phép.
ErickTreetops

2
Nếu bất cứ ai sử dụng điều này, xin lưu ý rằng điều này có thể rất dễ bị tấn công SQL nếu @listOfIDs là một tham số chuỗi được cung cấp bởi người dùng. Tùy thuộc vào kiến ​​trúc ứng dụng của bạn, điều này có thể hoặc không thể là một vấn đề.
Rogala

@Rogala Đồng ý, người dùng sẽ cần tự vệ sinh theo yêu cầu.
Möoz

1
@ Möoz Tôi khuyên bạn nên thêm một ghi chú vào câu trả lời của bạn để phản ánh điều này. Không phải ai cũng biết, và họ chỉ đơn giản là sao chép và dán các giải pháp mà không nghĩ đến hậu quả. SQL động rất nguy hiểm và tôi tránh nó như bệnh dịch đó.
Rogala

@ Möoz Ngoài ra, tôi đã không bỏ phiếu cho câu trả lời của bạn, nhưng những gì bạn vui lòng dành vài phút và kiểm tra câu trả lời của tôi? Chức năng STRINGinksLIT khá ngọt ngào và tôi nghĩ bạn sẽ TẤT CẢ về nó !!
Rogala

6

Đối với SQL Server 2016+ và Azure SQL Database, chức năng STRINGinksLIT đã được thêm vào sẽ là một giải pháp hoàn hảo cho vấn đề này. Dưới đây là tài liệu: https://docs.microsoft.com/en-us/sql/t-sql/fifts/opes-split-transact-sql

Đây là một ví dụ:

/*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;

Kết quả của truy vấn là 1,3

~ Chúc mừng


4

Cuối cùng, tôi đi đến kết luận rằng không sửa đổi cách thức truy vấn hoạt động, tôi không thể lưu trữ các giá trị trong các biến. Tôi đã sử dụng SQL profiler để bắt các giá trị và sau đó mã hóa chúng vào truy vấn để xem nó hoạt động như thế nào. Có 18 mảng số nguyên này và một số có hơn 30 phần tử trong đó.

Tôi nghĩ rằng cần có MS / SQL để giới thiệu một số kiểu dữ liệu quảng cáo vào ngôn ngữ. Mảng khá phổ biến và tôi không hiểu tại sao bạn không thể sử dụng chúng trong một kho lưu trữ.


6
SQL Server không cần mảng, khi nó có các tham số và biến có giá trị bảng.
John Saunders

Vì vậy, những gì chúng ta biết là truy vấn sử dụng một danh sách các số nguyên (được truyền cho nó bởi một mảng?). Điều tôi không hiểu là làm thế nào truy vấn của bạn đã sử dụng chúng mà không sử dụng một trong các phương thức đã cho trong các câu trả lời. Cung cấp thêm một số bối cảnh và chúng tôi có thể giúp bạn hơn nữa.
Möoz

2

Bạn không thể làm như thế này, nhưng bạn có thể thực hiện toàn bộ truy vấn lưu trữ nó trong một biến.

Ví dụ:

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

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

Exec (@query)

2
Như đã nêu trong một nhận xét trước đó, tùy thuộc vào cách bạn triển khai loại giải pháp này, hãy lưu ý rằng điều này có thể dễ bị tấn công SQL nếu @listOfIDs là một tham số được cung cấp bởi người dùng.
Rogala

2

Có một hàm mới trong SQL được gọi string_splitnếu bạn đang sử dụng danh sách chuỗi. Liên kết giới thiệu STRINGinksLIT (Transact-SQL)

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

bạn có thể vượt qua truy vấn này innhư sau:

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

1
Có vẻ hợp lý nhưng chỉ có SQL Server 2016 trở đi không may.
AnotherFineMess

0

Tôi sử dụng cái này:

1-Khai báo biến bảng tạm thời trong tập lệnh mà tòa nhà của bạn:

DECLARE @ShiftPeriodList TABLE(id INT NOT NULL);

2-Phân bổ vào bảng tạm thời:

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-Tham khảo bảng khi bạn cần trong câu lệnh WHERE:

INSERT INTO SomeTable WHERE ShiftPeriod IN (SELECT * FROM @ShiftPeriodList)
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.