2009-10-06 33 views
14

Tôi muốn viết một thủ tục lưu sẵn SQL Server 2005 sẽ chọn và trả về bản ghi người dùng từ bảng người dùng cho một số người dùng được chuyển đến thủ tục được lưu trữ làm tham số.SQL: trong mệnh đề trong thủ tục lưu sẵn: cách vượt qua các giá trị

Cách thực hiện việc này?

Tôi có thể chuyển id người dùng dưới dạng chuỗi được phân tách bằng dấu phẩy. Để tôi có thể sử dụng số điện thoại

select * 
from users 
where userid in (userids) 

Ví dụ: : Tôi muốn chọn các bản ghi cho id của 5,6,7,8,9

Làm thế nào để viết thủ tục được lưu trữ?

+0

Đây là một câu hỏi phổ biến. Tôi có một tùy chọn ở đây: http://stackoverflow.com/questions/977021/can-a-stored-procedure-have-dynamic-parameters-to-be-used-in-an-in-clause/977114#977114 – ScottE

Trả lời

17

Đối với SQL Server 2005, hãy kiểm tra Arrays and Lists in SQL Server 2005 bài báo xuất sắc Erland Sommarskog của đó cho thấy đối với một số kỹ thuật như thế nào đối phó với danh sách và mảng trong SQL Server 2005 (ông cũng có một bài viết cho SQL Server 2000).

Nếu bạn có thể nâng cấp lên SQL Server 2008, bạn có thể sử dụng tính năng mới được gọi là "bảng giá trị tham số":

Đầu tiên, tạo một bảng kiểu người dùng định nghĩa

CREATE TYPE dbo.MyUserIDs AS TABLE (UserID INT NOT NULL) 

Thứ hai, sử dụng mà loại bảng trong thủ tục được lưu trữ của bạn dưới dạng thông số:

CREATE PROC proc_GetUsers @UserIDTable MyUserIDs READONLY 
AS 
SELECT * FROM dbo.Users 
    WHERE userid IN (SELECT UserID FROM @UserIDTable) 

Xem chi tiết here.

Marc

7

bạn có thể sử dụng sql động. Vượt qua trong bản Tuyên Bố đến một Sql SP qua một biến và nối nó vào một truy vấn trong SQL và thực hiện sử dụng sp_execute sql

create procedure myproc(@clause varchar(100)) as 
begin 
    exec sp_executesql 'select * from users where userid in (' + @clause +')' 
end 
+8

hoạt động, nhưng SQL động có chia sẻ các vấn đề (an ninh, hiệu suất, v.v.) –

3

Giả sử T-SQL, bạn có thể sử dụng hàm đẹp này (trả về bảng).

DROP FUNCTION sp_ConvertStringToTable 
GO 
CREATE FUNCTION sp_ConvertStringToTable(@list ntext) 
     RETURNS @tbl TABLE (Position INT IDENTITY(1, 1) NOT NULL, 
          Value INT NOT NULL) AS 
    BEGIN 
     DECLARE @pos  int, 
       @textpos int, 
       @chunklen smallint, 
       @str  nvarchar(4000), 
       @tmpstr nvarchar(4000), 
       @leftover nvarchar(4000) 

     SET @textpos = 1 
     SET @leftover = '' 
     WHILE @textpos <= datalength(@list)/2 
     BEGIN 
     SET @chunklen = 4000 - datalength(@leftover)/2 
     SET @tmpstr = ltrim(@leftover + substring(@list, @textpos, @chunklen)) 
     SET @textpos = @textpos + @chunklen 

     SET @pos = charindex(' ', @tmpstr) 
     WHILE @pos > 0 
     BEGIN 
      SET @str = substring(@tmpstr, 1, @pos - 1) 
      INSERT @tbl (Value) VALUES(convert(int, @str)) 
      SET @tmpstr = ltrim(substring(@tmpstr, @pos + 1, len(@tmpstr))) 
      SET @pos = charindex(' ', @tmpstr) 
     END 

     SET @leftover = @tmpstr 
     END 

     IF ltrim(rtrim(@leftover)) <> '' 
     INSERT @tbl (Value) VALUES(convert(int, @leftover)) 

     RETURN 
    END 
GO 

Bằng cách này:

SELECT * FROM Users 
WHERE userid IN 
(SELECT Value FROM sp_ConvertStringToTable('1 2 3')) 

Bạn có thể thay đổi các chức năng lưu trữ để làm việc với các dấu phẩy tách chuỗi thay vì không gian tách biệt những người thân.

Nếu bạn không muốn/không thể sử dụng chức năng được lưu trữ, bạn có thể bao gồm mã của nó bên trong quy trình được lưu trữ khi cần.

CHỈNH SỬA: điều này cực kỳ hiệu quả hơn chuỗi nối.

+0

Bạn có số liệu thống kê về điều đó không? Tôi muốn được quan tâm đến việc đọc chúng –

+0

điều này là chậm, kiểm tra này ra: http://stackoverflow.com/questions/1456192/comparing-a-column-to-a-list-of-values-in-t-sql/1456404 # 1456404 –

+0

Woah! Cảm ơn bạn KM, làm một vài thử nghiệm và thay thế nó để dự án ngay bây giờ! Dù sao thì, "điều này nhanh hơn" có nghĩa là tham chiếu đến sql động. –

4

thấy my previous answer to this

đây là nguồn tốt nhất:

http://www.sommarskog.se/arrays-in-sql.html

tạo một hàm tách, và sử dụng nó như:

SELECT 
    * 
    FROM YourTable y 
    INNER JOIN dbo.splitFunction(@Parameter) s ON y.ID=s.Value 

I prefer the number table approach

Đối metho này d để làm việc, bạn cần phải làm một thời gian bảng thiết lập này:

SELECT TOP 10000 IDENTITY(int,1,1) AS Number 
    INTO Numbers 
    FROM sys.objects s1 
    CROSS JOIN sys.objects s2 
ALTER TABLE Numbers ADD CONSTRAINT PK_Numbers PRIMARY KEY CLUSTERED (Number) 

Khi những con số bảng được thiết lập, tạo ra chức năng này:

CREATE FUNCTION [dbo].[FN_ListToTable] 
(
    @SplitOn char(1)  --REQUIRED, the character to split the @List string on 
    ,@List  varchar(8000)--REQUIRED, the list to split apart 
) 
RETURNS TABLE 
AS 
RETURN 
(

    ---------------- 
    --SINGLE QUERY-- --this will not return empty rows 
    ---------------- 
    SELECT 
     ListValue 
     FROM (SELECT 
        LTRIM(RTRIM(SUBSTRING(List2, number+1, CHARINDEX(@SplitOn, List2, number+1)-number - 1))) AS ListValue 
        FROM (
          SELECT @SplitOn + @List + @SplitOn AS List2 
         ) AS dt 
         INNER JOIN Numbers n ON n.Number < LEN(dt.List2) 
        WHERE SUBSTRING(List2, number, 1) = @SplitOn 
      ) dt2 
     WHERE ListValue IS NOT NULL AND ListValue!='' 

); 
GO 

Bạn có thể dễ dàng chia một chuỗi CSV vào một bảng và tham gia vào nó:

select * from dbo.FN_ListToTable(',','1,2,3,,,4,5,6777,,,') 

OUTPUT:

ListValue 
----------------------- 
1 
2 
3 
4 
5 
6777 

(6 row(s) affected) 

của bạn có thể vượt qua trong một chuỗi CSV vào một thủ tục và quy trình chỉ hàng cho các ID đưa ra:

SELECT 
    y.* 
    FROM YourTable y 
     INNER JOIN dbo.FN_ListToTable(',',@GivenCSV) s ON y.ID=s.ListValue 
+0

Tôi thừa nhận tôi đã sử dụng điều này trong nhiều năm nay, nhưng nó đã trở lại để cắn tôi trong ass. Các phiên bản đa tuyên bố chunking này (ở đây: http://www.sommarskog.se/arrays-in-sql-2005.html#tblnum) thực sự là hai lần nhanh hơn. Vấn đề chỉ đáng chú ý khi bảng có liên quan chứa gần một triệu hàng. Vì vậy, hãy xem xét kỹ trước khi bạn sử dụng giải pháp @ KM. – pkExec

8

Chỉ cần sử dụng nó như thế này sẽ làm việc

Create procedure sp_DoctorList 
@userid varchar(100) 
as 
begin 
exec ('select * from doctor where userid in ('+ @userid +')') 
end 
+0

Cảm ơn bạn đã tư vấn! Chỉ cần ** exec ** phần làm việc cho tôi trong thủ tục của tôi 'chèn vào #temp_hourly exec ('chọn ZONE_ID từ ZONE nơi ZONE_ID trong (' + @ZoneId + ') nhóm bởi ZONE_ID')' –

-1

Bạn cũng có thể sử dụng Find_IN_SET thay vì TRÊN . Xem các truy vấn dưới đây

create procedure myproc(IN in_user_ids varchar(100)) 
begin 
    select * from users where FIND_IN_SET(userid, in_user_ids); 
end 
+0

'FIND_IN_SET' không phải là một SQL chức năng tiêu chuẩn, và nó không ** có sẵn trong Microsoft ** SQL Server ** (mà OP đã hỏi về) –

0

thử này hoạt động này đối với tôi

DECLARE @InClause NVARCHAR(100) 
SET @InClause = 'tom,dick,harry' 
DECLARE @SafeInClause NVARCHAR(100) 
SET @SafeInClause = ',' + @InClause + ',' 
SELECT * FROM myTable WHERE PATINDEX(',' + myColumn + ',', @SafeInClause) > 0 
Các vấn đề liên quan