Thủ tục lưu trữ T-SQL chấp nhận nhiều giá trị Id


145

Có cách nào duyên dáng để xử lý việc chuyển danh sách id làm tham số cho thủ tục được lưu trữ không?

Chẳng hạn, tôi muốn các bộ phận 1, 2, 5, 7, 20 được trả về bởi thủ tục lưu trữ của tôi. Trước đây, tôi đã thông qua một danh sách id được phân tách bằng dấu phẩy, như đoạn mã dưới đây, nhưng cảm thấy thực sự bẩn khi làm điều đó.

Tôi nghĩ rằng SQL Server 2005 là giới hạn áp dụng duy nhất của tôi.

create procedure getDepartments
  @DepartmentIds varchar(max)
as
  declare @Sql varchar(max)     
  select @Sql = 'select [Name] from Department where DepartmentId in (' + @DepartmentIds + ')'
  exec(@Sql)

Đây là một biến thể của phương thức XML mà tôi vừa tìm thấy.
JasonS

5
Nếu bạn đang sử dụng SQL Server 2008, bạn có thể sử dụng Tham số có giá trị bảng. http://www.sqlteam.com/article/sql-server-2008-table-valued-parameter
Ian Nelson

Câu trả lời:


237

Erland Sommarskog đã duy trì câu trả lời có thẩm quyền cho câu hỏi này trong 16 năm qua: Mảng và Danh sách trong SQL Server .

Có ít nhất một tá cách để truyền một mảng hoặc danh sách vào một truy vấn; mỗi cái đều có ưu và nhược điểm riêng.

Tôi thực sự không thể đề nghị đủ để đọc bài viết để tìm hiểu về sự đánh đổi trong số tất cả các tùy chọn này.


11

Vâng, giải pháp hiện tại của bạn dễ bị tấn công SQL SQL.

Giải pháp tốt nhất mà tôi đã tìm thấy là sử dụng chức năng chia văn bản thành các từ (có một vài bài được đăng ở đây hoặc bạn có thể sử dụng chức năng này từ blog của tôi ) và sau đó nối nó vào bảng của bạn. Cái gì đó như:

SELECT d.[Name]
FROM Department d
    JOIN dbo.SplitWords(@DepartmentIds) w ON w.Value = d.DepartmentId

14
Tôi không chắc chắn rằng nó "dễ bị tấn công SQL SQL" trừ khi Proc được lưu trữ có thể gọi trực tiếp từ các máy khách không đáng tin cậy, trong trường hợp đó bạn gặp vấn đề lớn hơn. Mã lớp dịch vụ sẽ tạo chuỗi @DepidorIds từ dữ liệu được nhập mạnh (ví dụ: int [] DivisionIds), trong trường hợp đó bạn sẽ ổn.
Anthony

Giải pháp tuyệt vời, @Matt Hamilton. Không biết điều này có giúp được ai không, nhưng tôi đã nhận được kết quả chính xác hơn trên SQL Server 2008r khi tôi đang tìm kiếm các trường văn bản bằng cách sử dụng "tham gia dbo.Splitemme (@MyParameterArray) p ON CHARINDEX (p.value, d.MyFieldToSearch)> 0"
Darkloki

3

Một phương pháp bạn có thể muốn xem xét nếu bạn sẽ làm việc với các giá trị rất nhiều là trước tiên hãy viết chúng vào một bảng tạm thời. Sau đó, bạn chỉ cần tham gia vào nó như bình thường.

Bằng cách này, bạn chỉ phân tích cú pháp một lần.

Dễ dàng nhất để sử dụng một trong các UDF 'Tách', nhưng rất nhiều người đã đăng các ví dụ về những điều đó, tôi nghĩ rằng tôi sẽ đi một con đường khác;)

Ví dụ này sẽ tạo một bảng tạm thời để bạn tham gia (#tmpDept) và điền nó với id của bộ phận mà bạn đã truyền vào. Tôi giả sử bạn đang tách chúng bằng dấu phẩy, nhưng dĩ nhiên - bạn có thể thay đổi nó cho bất cứ điều gì bạn muốn.

IF OBJECT_ID('tempdb..#tmpDept', 'U') IS NOT NULL
BEGIN
    DROP TABLE #tmpDept
END

SET @DepartmentIDs=REPLACE(@DepartmentIDs,' ','')

CREATE TABLE #tmpDept (DeptID INT)
DECLARE @DeptID INT
IF IsNumeric(@DepartmentIDs)=1
BEGIN
    SET @DeptID=@DepartmentIDs
    INSERT INTO #tmpDept (DeptID) SELECT @DeptID
END
ELSE
BEGIN
        WHILE CHARINDEX(',',@DepartmentIDs)>0
        BEGIN
            SET @DeptID=LEFT(@DepartmentIDs,CHARINDEX(',',@DepartmentIDs)-1)
            SET @DepartmentIDs=RIGHT(@DepartmentIDs,LEN(@DepartmentIDs)-CHARINDEX(',',@DepartmentIDs))
            INSERT INTO #tmpDept (DeptID) SELECT @DeptID
        END
END

Điều này sẽ cho phép bạn vượt qua trong một id bộ phận, nhiều id với dấu phẩy ở giữa chúng hoặc thậm chí nhiều id với dấu phẩy và khoảng trắng giữa chúng.

Vì vậy, nếu bạn đã làm một cái gì đó như:

SELECT Dept.Name 
FROM Departments 
JOIN #tmpDept ON Departments.DepartmentID=#tmpDept.DeptID
ORDER BY Dept.Name

Bạn sẽ thấy tên của tất cả các ID bộ phận mà bạn đã chuyển qua ...

Một lần nữa, điều này có thể được đơn giản hóa bằng cách sử dụng một hàm để điền vào bảng tạm thời ... Tôi chủ yếu làm điều đó mà không có ai chỉ để giết một chút nhàm chán :-P

- Kevin Fairchild


3

Bạn có thể sử dụng XML.

Ví dụ

declare @xmlstring as  varchar(100) 
set @xmlstring = '<args><arg value="42" /><arg2>-1</arg2></args>' 

declare @docid int 

exec sp_xml_preparedocument @docid output, @xmlstring

select  [id],parentid,nodetype,localname,[text]
from    openxml(@docid, '/args', 1) 

Lệnh sp_xml_preparedocument được tích hợp sẵn.

Điều này sẽ tạo ra đầu ra:

id  parentid    nodetype    localname   text
0   NULL        1           args        NULL
2   0           1           arg         NULL
3   2           2           value       NULL
5   3           3           #text       42
4   0           1           arg2        NULL
6   4           3           #text       -1

trong đó có tất cả (nhiều hơn?) những gì bạn cần.


2

Phương thức XML cực nhanh, nếu bạn muốn sử dụng một thủ tục được lưu trữ và vượt qua danh sách ID Sở được phân tách bằng dấu phẩy:

Declare @XMLList xml
SET @XMLList=cast('<i>'+replace(@DepartmentIDs,',','</i><i>')+'</i>' as xml)
SELECT x.i.value('.','varchar(5)') from @XMLList.nodes('i') x(i))

Tất cả tín dụng được chuyển đến Blog của Brad Brad Schulz


-3

Hãy thử cái này:

@list_of_params varchar(20) -- value 1, 2, 5, 7, 20 

SELECT d.[Name]
FROM Department d
where @list_of_params like ('%'+ CONVERT(VARCHAR(10),d.Id)  +'%')

rất đơn giản.


1
rất đơn giản - và rất sai. Nhưng ngay cả khi bạn sẽ khắc phục vấn đề trong mã của mình thì nó cũng sẽ rất chậm. Xem liên kết "Phương pháp thực sự chậm" trong câu trả lời được chấp nhận để biết chi tiết.
Sebastian Meine
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.