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

Глядя на передачу списка идентификаторов пользователей, чтобы вернуть имена списков. У меня есть план обработки выданных имен (с COALESCE что-то или другое), но попытка найти лучший способ передать список идентификаторов пользователей. Чувства моего sproc будут выглядеть примерно так:

create procedure [dbo].[get_user_names]
@user_id_list, --which would equal a list of incoming ID numbers like (5,44,72,81,126)
@username varchar (30) output
as
select last_name+', '+first_name 
from user_mstr
where user_id in @user_id_list

Передача значений для @user_id_list - моя главная проблема здесь.

Ответ 1

Предпочтительным методом передачи массива значений хранимой процедуре в SQL-сервере является использование табличных параметров.

Сначала вы определяете тип следующим образом:

CREATE TYPE UserList AS TABLE ( UserID INT );

Затем вы используете этот тип в хранимой процедуре:

create procedure [dbo].[get_user_names]
@user_id_list UserList READONLY,
@username varchar (30) output
as
select last_name+', '+first_name 
from user_mstr
where user_id in (SELECT UserID FROM @user_id_list)

Итак, прежде чем вы вызываете эту хранимую процедуру, вы заполняете переменную таблицы:

DECLARE @UL UserList;
INSERT @UL VALUES (5),(44),(72),(81),(126)

И, наконец, вызовите SP:

EXEC dbo.get_user_names @UL, @username OUTPUT;

Ответ 2

Насколько я могу судить, есть три основных претендента: Табличные параметры, строка списка с разделителями и строка JSON.

С 2016 года вы можете использовать встроенный STRING_SPLIT, если вам нужен маршрут с разделителями: https://docs.microsoft.com/en-us/sql/t-sql/functions/string-split-transact-sql

Вероятно, это будет самый простой/самый простой/простой подход.

Также с 2016 года JSON может передаваться как nvarchar и использоваться с OPENJSON: https://docs.microsoft.com/en-us/sql/t-sql/functions/openjson-transact-sql

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

Кажется, что TVP раньше были каноническим способом передачи более структурированных параметров, и они все еще хороши, если вам нужна такая структура, явность и базовая проверка значения/типа. Хотя они могут быть немного более громоздкими для потребителя. Если у вас нет 2016+, это, вероятно, вариант по умолчанию/лучший.

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

Ответ 3

Я решил эту проблему следующим образом:

  • В С# я построил переменную String.

string userId = "";

  1. Я помещаю свой элемент списка в эту переменную. Я разделил ",".

например: в С#

userId= "5,44,72,81,126";

и отправить на SQL-сервер

 SqlParameter param = cmd.Parameters.AddWithValue("@user_id_list",userId);
  1. Я создаю раздельную функцию в SQL-сервере. Для преобразования моего полученного списка (типа NVARCHAR(Max)) в таблицу.
CREATE FUNCTION dbo.SplitInts  
(  
   @List      VARCHAR(MAX),  
   @Delimiter VARCHAR(255)  
)  
RETURNS TABLE  
AS  
  RETURN ( SELECT Item = CONVERT(INT, Item) FROM  
      ( SELECT Item = x.i.value('(./text())[1]', 'varchar(max)')  
        FROM ( SELECT [XML] = CONVERT(XML, '<i>'  
        + REPLACE(@List, @Delimiter, '</i><i>') + '</i>').query('.')  
          ) AS a CROSS APPLY [XML].nodes('i') AS x(i) ) AS y  
      WHERE Item IS NOT NULL  
  );
  1. В основной процедуре Хранилища, используя приведенную ниже команду, я использую список записей.

SELECT user_id = элемент FROM dbo.SplitInts(@user_id_list, ',');

Ответ 4

Вы можете попробовать следующее:

create procedure [dbo].[get_user_names]
    @user_id_list varchar(2000), -- You can use any max length

    @username varchar (30) output
    as
    select last_name+', '+first_name 
    from user_mstr
    where user_id in (Select ID from dbo.SplitString( @user_id_list, ',') )

И вот пользовательская функция для SplitString:

Create FUNCTION [dbo].[SplitString]
(    
      @Input NVARCHAR(MAX),
      @Character CHAR(1)
)
RETURNS @Output TABLE (
      Item NVARCHAR(1000)
)
AS
BEGIN
      DECLARE @StartIndex INT, @EndIndex INT

      SET @StartIndex = 1
      IF SUBSTRING(@Input, LEN(@Input) - 1, LEN(@Input)) <> @Character
      BEGIN
            SET @Input = @Input + @Character
      END

      WHILE CHARINDEX(@Character, @Input) > 0
      BEGIN
            SET @EndIndex = CHARINDEX(@Character, @Input)

            INSERT INTO @Output(Item)
            SELECT SUBSTRING(@Input, @StartIndex, @EndIndex - 1)

            SET @Input = SUBSTRING(@Input, @EndIndex + 1, LEN(@Input))
      END

      RETURN
END

Ответ 5

Azure DB, Azure Data WH и SQL Server 2016, вы можете использовать STRING_SPLIT для достижения результата, аналогичного описанному @sparrow.

Переработка кода из @sparrow

where user_id in (Select value from STRING_SPLIT( @user_id_list, ',')

Простой и эффективный способ принятия списка значений в хранимую процедуру

Ответ 6

Возможно, вы могли бы использовать:

select last_name+', '+first_name 
from user_mstr
where ',' + @user_id_list + ',' like '%,' + convert(nvarchar, user_id) + ',%'

Ответ 7

Вы можете использовать этот простой метод inline для построения параметра string_list_type (работает в SQL Server 2014):

declare @p1 dbo.string_list_type
insert into @p1 values(N'myFirstString')
insert into @p1 values(N'mySecondString')

Пример использования хранимой процедуры:

exec MyStoredProc @[email protected]