SQL: в предложении в хранимой процедуре: как передать значения - PullRequest
17 голосов
/ 06 октября 2009

Я хочу написать хранимую процедуру SQL Server 2005, которая будет выбирать и возвращать пользовательские записи из таблицы пользователей для некоторых идентификаторов пользователей, которые передаются в хранимую процедуру в качестве параметра.

Как это сделать?

Я могу передать идентификаторы пользователя в виде строки, разделенной запятой. Так что я могу использовать

select * 
from users 
where userid in (userids)

например. : Я хочу выбрать записи для идентификаторов 5,6,7,8,9

Как написать хранимую процедуру?

Ответы [ 7 ]

25 голосов
/ 06 октября 2009

Для SQL Server 2005 ознакомьтесь с превосходной статьей Erland Sommarskog Массивы и списки в SQL Server 2005 , в которой показаны некоторые методы работы со списками и массивами в SQL Server 2005 (у него также есть другая статья для SQL Server 2000).

Если бы вы могли перейти на SQL Server 2008, вы можете использовать новую функцию под названием «табличный параметр»:

Сначала создайте пользовательский тип таблицы

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

Во-вторых, используйте этот тип таблицы в вашей хранимой процедуре в качестве параметра:

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

Подробнее здесь .

Марк

8 голосов
/ 15 января 2013

Просто используйте, как это будет работать

Create procedure sp_DoctorList 
@userid varchar(100)
as 
begin
exec ('select * from doctor where userid in ( '+ @userid +' )')
end
6 голосов
/ 06 октября 2009

вы можете использовать динамический sql. Передайте оператор in в Sql SP через переменную и объедините его в запрос в SQL и выполните с помощью sp_execute sql

create procedure myproc(@clause varchar(100)) as 
begin
  exec sp_executesql 'select * from users where userid in ( ' + @clause +' )'
end
4 голосов
/ 07 октября 2009

см. мой предыдущий ответ на этот вопрос

это лучший источник:

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

создайте функцию разделения и используйте ее следующим образом:

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

Я предпочитаю подход с таблицей чисел

Чтобы этот метод работал, вам нужно выполнить настройку единовременного расписания:

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)

После настройки таблицы чисел создайте эту функцию:

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 

Теперь вы можете легко разбить строку CSV на таблицу и присоединиться к ней:

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

ВЫВОД:

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

(6 row(s) affected)

Вы можете передать строку CSV в процедуру и обработать только строки с заданными идентификаторами:

SELECT
    y.*
    FROM YourTable y
        INNER JOIN dbo.FN_ListToTable(',',@GivenCSV) s ON y.ID=s.ListValue
3 голосов
/ 06 октября 2009

Предполагая, что T-SQL, вы можете использовать эту красивую функцию (которая возвращает таблицу).

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

Таким образом:

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

Вы можете изменить сохраненную функцию для работы со строками, разделенными запятыми, вместо строк, разделенных пробелами.

Если вы не хотите / не можете использовать сохраненную функцию, вы можете включить ее код в хранимую процедуру, где это необходимо.

РЕДАКТИРОВАТЬ: это невероятно более эффективно, чем конкатенация строк.

2 голосов
/ 31 августа 2015

попробуйте это работает для меня

DECLARE @InClause NVARCHAR(100)
SET @InClause = 'tom,dick,harry'
DECLARE @SafeInClause NVARCHAR(100)
SET @SafeInClause = ',' + @InClause + ',' 
SELECT * FROM myTable WHERE PATINDEX(',' + myColumn + ',', @SafeInClause) > 0
0 голосов
/ 23 июля 2014

Вы также можете использовать Find_IN_SET вместо IN. Смотрите запрос ниже

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