SQL: in в хранимой процедуре: как передать значения
Я хочу написать хранимую процедуру SQL Server 2005, которая будет выбирать и возвращать записи пользователя из таблицы пользователей для некоторых пользователей, которые передаются в хранимую процедуру как параметр.
Как это сделать?
Я могу передать идентификаторы пользователя в виде строки, разделенной запятой. Чтобы я мог использовать
select *
from users
where userid in (userids)
например.: Я хочу выбрать записи для id 5,6,7,8,9
Как записать хранимую процедуру?
Ответы
Ответ 1
Для 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)
Подробнее здесь.
Марк
Ответ 2
Просто используйте его, как это будет работать
Create procedure sp_DoctorList
@userid varchar(100)
as
begin
exec ('select * from doctor where userid in ( '+ @userid +' )')
end
Ответ 3
вы можете использовать динамический 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
см. мой предыдущий ответ на этот вопрос
это лучший источник:
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)
Как только таблица Numbers настроена, создайте эту функцию:
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
Ответ 5
Предполагая 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') )
Вы можете изменить сохраненную функцию, чтобы работать с разделенными запятыми строками вместо разделенных пробелами.
Если вы не хотите/не можете использовать сохраненную функцию, вы можете включить ее код в хранимую процедуру там, где это необходимо.
EDIT: это невероятно более результативно, чем конкатенация строк.
Ответ 6
попробуйте это, это работает для меня
DECLARE @InClause NVARCHAR(100)
SET @InClause = 'tom,dick,harry'
DECLARE @SafeInClause NVARCHAR(100)
SET @SafeInClause = ',' + @InClause + ','
SELECT * FROM myTable WHERE PATINDEX(',' + myColumn + ',', @SafeInClause) > 0
Ответ 7
Вы также можете использовать 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