2013-08-22 43 views
86

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

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

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 biến trên máy chủ SQL có thể lưu loại dữ liệu này để kiểm tra.

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 số nguyên, vậy làm cách nào để chạy truy vấn báo cáo trên SQL Server với cùng giá trị như báo cáo?

+0

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

+0

câu hỏi hay. +1 – RayLoveless

Trả lời

5

Bạn nói đúng, không có kiểu dữ liệu 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ị số nguyên riêng biệt và đặt chúng vào 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 một truy vấn năng độ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); 
+0

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

+0

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 @listOfID là tham số chuỗi do người dùng cung cấp. Tùy thuộc vào kiến ​​trúc của ứng dụng của bạn, điều này có thể hoặc không thể là một vấn đề. – Rogala

+0

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

1

Bạn không thể làm điều đó như thế này, nhưng bạn có thể thực hiện truy vấn toàn bộ 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) 
+0

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 @listOfID là một tham số do người dùng cung cấp. – Rogala

120

Table variable

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

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 điều đó nhưng một lần nữa nó đòi hỏi tôi phải viết lại cách biến được đọc trong truy vấn. Tôi phải giữ nó như cũ. – user1413844

+2

Nếu bạn không biết ID là gì và xuất phát từ truy vấn thì sao? Ví dụ: 'SET @AddressIDs = (CHỌN ID TỪ địa chỉ WHERE Account = 1234)' Truy vấn này sẽ trả về nhiều ID và tôi gặp lỗi khi truy vấn con trả về nhiều kết quả và không được phép. Có anyway để tạo ra một biến sẽ lưu trữ một mảng nếu ID từ một truy vấn phụ? –

+0

Tôi đã thử tùy chọn thứ hai và nó hoạt động với số lượng bản ghi ít hơn. Khi tôi tăng số lượng Id, tôi nhận được lỗi TimeOut. Có thể diễn viên đang cản trở hiệu suất. –

2

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

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

+5

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

+0

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?). Những gì tôi không hiểu là truy vấn của bạn đã sử dụng chúng như thế nào mà không sử dụng một trong những phương thức đã cho trong câu trả lời. Cung cấp thêm một số ngữ cảnh và chúng tôi có thể giúp bạn thêm. –

14

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

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

Và Stored Procedure được sử dụng nó trong hình thức này:

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

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

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 cung cấp IntList mình

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

Đối với SQL Server 2016+ và Cơ sở dữ liệu SQL Azure, hàm STRING_SPLIT đã được thêm vào sẽ là 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/functions/string-split-transact-sql

Dưới đâ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

Các vấn đề liên quan