Хранимая процедура T-SQL, которая принимает несколько значений Id

Существует ли изящный способ обработки списка идентификаторов в качестве параметра хранимой процедуры?

Например, я хочу, чтобы департаменты 1, 2, 5, 7, 20 были возвращены моей хранимой процедурой. Раньше я перешел в список идентификаторов с разделителями-запятыми, например, код ниже, но чувствую себя действительно грязным.

SQL Server 2005 - мое единственное применимое ограничение, которое я думаю.

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

Ответ 1

Эрланд Соммарског сохранил авторитетный ответ на этот вопрос за последние 16 лет: Массивы и списки в SQL Server.

Существует не менее дюжины способов передать массив или список в запрос; каждый из них имеет свои собственные плюсы и минусы.

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

Ответ 2

Да, ваше текущее решение подвержено атакам SQL-инъекций.

Лучшим решением, которое я нашел, является использование функции, которая разбивает текст на слова (здесь есть несколько статей, или вы можете использовать этот из моего блога), а затем присоедините это к своей таблице. Что-то вроде:

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

Ответ 3

Вы можете использовать XML.

например.

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) 

Создана команда sp_xml_preparedocument.

Это даст результат:

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

у которого есть все (больше?) того, что вам нужно.

Ответ 4

Один из способов, который вы, возможно, захотите рассмотреть, если вы собираетесь работать со значительными значениями, - это сначала записать их во временную таблицу. Затем вы просто присоединяетесь к нему, как обычно.

Таким образом, вы только разобираете один раз.

Проще всего использовать один из "Сплит" UDF, но так много людей разместили примеры из них, я решил, что пойдет по другому пути;)

В этом примере будет создана временная таблица для подключения (#tmpDept) и заполните ее идентификатором отдела, в который вы прошли. Я предполагаю, что вы разделяете их запятыми, но вы можете - конечно - измените его на все, что хотите.

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 @[email protected]
    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

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

Итак, если вы сделали что-то вроде:

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

Вы увидите имена всех идентификаторов отдела, которые вы передали в...

Опять же, это можно упростить, используя функцию для заполнения временной таблицы... Я в основном делал это без одного, чтобы убить некоторую скуку: -P

- Кевин Фэрчайлд

Ответ 5

Супербыстрый XML-метод, если вы хотите использовать хранимую процедуру и передать список разделов с разделителями-запятыми:

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))

Весь кредит принадлежит Guru Блог Брэда Шульца

Ответ 6

Попробуйте следующее:

@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)  +'%')

очень просто.