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

时间:2017-02-24 21:27:34

标签: 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

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

9 个答案:

答案 0 :(得分:32)

The preferred method for passing an array of values to a stored procedure in SQL server is to use table valued parameters.

First you define the type like this:

CREATE TYPE UserList AS TABLE ( UserID INT );

Then you use that type in the stored procedure:

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)

So before you call that stored procedure, you fill a table variable:

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

And finally call the SP:

EXEC dbo.get_user_names @UL, @username OUTPUT;

答案 1 :(得分:8)

据我所知,有三个主要的竞争者:表值参数,分隔列表字符串和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年以上,您可能更愿意明确说明其类型/架构。参数而不是传递一个字符串并以某种方式解析它。

答案 2 :(得分:3)

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

  1. 在C#中我构建了一个String变量。
  2.   

    string userId =“”;

    1. 我将列表的项目放在此变量中。我把','。
    2. 分开了
        

      例如:在C#中

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

      并发送到SQL-Server

       SqlParameter param = cmd.Parameters.AddWithValue("@user_id_list",userId);
      
      1. 我在SQL服务器中创建分隔函数用于将我收到的列表(它的类型为NVARCHAR(Max))转换为表格。
      2. 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. 在主存储过程中,使用以下命令,我使用条目列表。
        2.   

          SELECT user_id = Item FROM dbo.SplitInts(@user_id_list,',');

答案 3 :(得分:2)

这对我来说是完美的工作。我希望这个完美的例子解决了很多用户的问题。

第 1 步 像这样在sql中创建引用表

 Create TYPE dbo.tblNames
AS TABLE
(
    [Name] nvarchar(max)
);
go

create TYPE dbo.tblNamesWithCols
AS TABLE
(
    [Name] nvarchar(max)
);
go

第 2 步 使用这样的引用表参数创建存储过程

    create proc syTest
    @VarTbleNameList AS dbo.tblNames READONLY,
    @VarTbleNameColsList AS dbo.tblNamesWithCols READONLY,
    @VarWhereQuery nvarchar(max)
    as
    begin
    ......
......  End

**使用参数调用存储过程**

DECLARE @VarTbleList AS dbo.tblNames
INSERT INTO @VarTbleList
VALUES ( 'tblEmployes' )
INSERT INTO @VarTbleList
VALUES ( 'tblDepartments' )
INSERT INTO @VarTbleList
VALUES ( 'tblCities' )

DECLARE @VarTbleColList AS dbo.tblNamesWithCols
INSERT INTO @VarTbleColList
VALUES ( 'tblEmployes.EmployeId as empId;' )
INSERT INTO @VarTbleColList
VALUES ( 'tblEmployes.EmployeName as empName;' )
INSERT INTO @VarTbleColList
VALUES ( 'tblDepartments.DepartmentName as deptName;'  )
INSERT INTO @VarTbleColList
VALUES ( 'tblDepartments.DepartmentId as deptId;' )

EXECUTE  syTest @VarTbleList , @VarTbleColList , @VarWhereQuery ='test'

答案 4 :(得分:1)

你可以试试这个:

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 :(得分:1)

Azure DB,Azure Data WH和SQL Server 2016中的数据,您可以使用STRING_SPLIT获得与@sparrow描述的结果类似的结果。

从@sparrow回收代码

useDispatch

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

答案 6 :(得分:1)

检查下面的代码这对我有用

@ManifestNoList      VARCHAR(MAX)


WHERE
    (
        ManifestNo IN (SELECT value FROM dbo.SplitString(@ManifestNoList, ','))
    )

答案 7 :(得分:0)

您可以使用这种简单的“内联”方法构造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 @MyParam=@p1

答案 8 :(得分:0)

正确的方法是创建用户定义的数据类型:

CREATE TYPE [dbo].[IntArray] AS TABLE
(
    [ID] [INT] NULL
)

然后你可以使用这个自定义数据类型:

CREATE OR ALTER PROCEDURE [dbo].[sp_GetUserNames]
(
    @userIds [IntArray] READONLY
)
AS
BEGIN
    SET NOCOUNT ON;

    SELECT 
        "Name" = u.LastName + ', ' + u.FirstName
    FROM dbo.User u
    JOIN @userIds uid ON u.Id = uid.Id;
END

用法:

@DECLARE @result TABLE
(
    Name NVARCHAR(max)
);

@DECLARE @ids [IntArray] = SELECT x.userId FROM dbo.sometable x;

SET @result = EXECUTE [dbo].[sp_GetUserNames] @userIds = @ids;

SELECT * FROM @result;