如何将列表作为参数传递给存储过程?

sha*_*011 31 sql sql-server stored-procedures

希望传递用户ID列表以返回列表名称.我有计划处理输出的名称(使用COALESCE或其他东西),但试图找到传递用户ID列表的最佳方法.我的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
Run Code Online (Sandbox Code Playgroud)

传递@user_id_list的值是我的主要关注点.

Mat*_*tum 38

将值数组传递给SQL Server中的存储过程的首选方法是使用表值参数.

首先,您定义这样的类型:

CREATE TYPE UserList AS TABLE ( UserID INT );
Run Code Online (Sandbox Code Playgroud)

然后在存储过程中使用该类型:

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)
Run Code Online (Sandbox Code Playgroud)

因此,在调用该存储过程之前,请填充表变量:

DECLARE @UL UserList;
INSERT @UL VALUES (5),(44),(72),(81),(126)
Run Code Online (Sandbox Code Playgroud)

最后打电话给SP:

EXEC dbo.get_user_names @UL, @username OUTPUT;
Run Code Online (Sandbox Code Playgroud)

  • 当我尝试运行第一行时:“ CREATE TYPE UserList AS TABLE(UserID INT);” 我收到以下错误:“关键字'AS'附近的语法不正确。有任何建议吗?我正在使用SQL Server 2014。 (2认同)
  • @CowboyBebop 尝试在 UserList 前面加上您的架构名称(通常是 dbo)。因此该行变为: CREATE TYPE dbo.UserList AS TABLE ( UserID INT ); (2认同)

Amb*_*tle 13

据我所知,有三个主要的竞争者:表值参数,分隔列表字符串和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+,这可能是默认/最佳选择.

我认为这是在任何这些具体考虑因素之间的权衡,以及您对明确的params结构的偏好,这意味着即使您有2016+,您可能更愿意明确说明参数的类型/模式而不是传递一个字符串并以某种方式解析它.

  • @JoelFan,不,这不会生成要评估的动态 SQL。STRNG_SPLIT 生成一个单列表,其中每个拆分值作为一行。 (3认同)

Pet*_*nry 9

从 Azure DB、Azure Data WH 和 SQL Server 2016 开始,您可以使用 STRING_SPLIT 来实现与 @sparrow 所描述的类似的结果。

从@sparrow 回收代码

WHERE user_id IN (SELECT value FROM STRING_SPLIT( @user_id_list, ',')
Run Code Online (Sandbox Code Playgroud)

将值列表接受到存储过程中的简单有效的方法

  • STRING_SPLIT 返回一个单列表,其行是子字符串。该列的名称是**值**。正确的子查询是`Select value from STRING_SPLIT(@myvar, ',')`(而不是**id**)作为https://docs.microsoft.com/en-us/sql/t-sql/functions/字符串分割-transact-sql?view=sql-server-2017 (3认同)

Beh*_*lem 6

我通过以下方法解决了这个问题:

  1. 在C#中,我建立了一个String变量。

字符串userId =“”;

  1. 我将列表项放在此变量中。我将“,”分开。

例如:在C#中

userId= "5,44,72,81,126";
Run Code Online (Sandbox Code Playgroud)

并发送到SQL-Server

 SqlParameter param = cmd.Parameters.AddWithValue("@user_id_list",userId);
Run Code Online (Sandbox Code Playgroud)
  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  
  );
Run Code Online (Sandbox Code Playgroud)
  1. 在主存储过程中,使用以下命令,使用条目列表。

SELECT user_id =来自dbo.SplitInts(@user_id_list,',');的项目


Spa*_*row 5

你可以试试这个:

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, ',') )
Run Code Online (Sandbox Code Playgroud)

这是 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
Run Code Online (Sandbox Code Playgroud)