【问题标题】:How to exec a stored procedure for a list of constant values?如何为常量值列表执行存储过程?
【发布时间】:2019-10-28 11:39:14
【问题描述】:

我可以轻松地为单个值执行存储过程:

EXEC FooStored @ID = 269

但是我怎样才能为列表中的许多值执行相同的存储(我有一个逗号分隔的大约 1000 个常量值的列表,它们来自 SQL 外部)。

我的想法是这样的:

EXEC FooStored @ID IN (269,270,274,280,282,292,300,320,324)  -- doesn't work

编辑:由于我无权更改此过程或对此数据库创建新的存储过程,因此我更愿意在查询级别解决此问题,而不是要求某人为我进行所需的更改。

【问题讨论】:

  • 最好是table-valued parameter。另一种选择是将分隔列表作为单个字符串传递,然后拆分值。
  • @DanGuzman 我对这两种技术都不熟悉。您介意将您的评论扩展到答案吗?
  • 你的 SQL 版本是多少?
  • 如果您不想更改现有过程,那么最好的选择是复制当前存储过程并将副本更改为使用表值参数。我知道它有点违反 DRY,但这在 SQL 中通常不如在编程中重要。
  • @Zhorov SQL Server 2016

标签: sql sql-server tsql stored-procedures sql-server-2016


【解决方案1】:

以下是使用表值参数的示例。

我从您对字符串拆分答案的评论中看到,您不想修改现有的存储过程接口。考虑为列表接口创建一个新的存储过程或使用可选的列表参数重载现有接口以避免重大更改。

CREATE TYPE dbo.IntegerList AS TABLE(
    IntegerValue int NOT NULL PRIMARY KEY
    )
GO

CREATE PROC dbo.FooStored
    @IntegerList dbo.IntegerList READONLY
AS
SELECT *
FROM dbo.YourTable
WHERE ID IN(
    SELECT IntegerValue
    FROM @IntegerList
);
GO

DECLARE @IntegerList dbo.IntegerList;
INSERT INTO @IntegerList VALUES(269),(270),(274),(280),(282),(292),(300),(320),(324);
EXEC dbo.FooStored @IntegerList = @IntegerList;
GO

【讨论】:

  • 对我的问题添加了一个编辑,这清除了我的需求。
  • @ruohola,如果您无法更改 proc 或创建新的,则没有 T-SQL 解决方案。
【解决方案2】:

你应该使用表格类型 在您的存储过程中,使用用户定义的表类型作为您的值的类型。 可能是这样的:

CREATE PROCEDURE FooStored 
(
    @IDs ListOfID READONLY
)
AS
.
.
.

ListOfID 是用户定义的表类型,如下所示:

CREATE TYPE ListOfID AS TABLE
(
    [ID] int NULL
)

要使用您的 FooStored 存储过程,您必须首先声明一个临时表并将您的数据插入该临时表并将您的数据传递给您的存储过程。 可能是这样的:

 DECLARE @list ListOfID
    INSERT INTO @list VALUES(1)
    INSERT INTO @list VALUES(2)
    EXEC FooStored @list

【讨论】:

  • 谢谢,但我编辑了我的问题,表明我不想编辑现有程序。
  • 因此,唯一的方法是执行存储过程的次数与列表长度一样多
  • 是的,似乎是这样,我提交的答案也反映了这一点。
【解决方案3】:

你需要将参数作为逗号分隔的字符串带入存储过程,然后检查存储过程内部 “如果'@ID'在列表中。”

如果您使用的是SQL Server 2016 及更高版本,您可以使用SPLIT_STRING() function

如果您使用的是SQL Server 的低版本,那么您可以使用此函数来拆分字符串:

CREATE FUNCTION dbo.splitstring ( @stringToSplit VARCHAR(MAX) )
RETURNS
 @returnList TABLE ([Name] [nvarchar] (500))
AS
BEGIN

 DECLARE @name NVARCHAR(255)
 DECLARE @pos INT

 WHILE CHARINDEX(',', @stringToSplit) > 0
 BEGIN
  SELECT @pos  = CHARINDEX(',', @stringToSplit)  
  SELECT @name = SUBSTRING(@stringToSplit, 1, @pos-1)

  INSERT INTO @returnList 
  SELECT @name

  SELECT @stringToSplit = SUBSTRING(@stringToSplit, @pos+1, LEN(@stringToSplit)-@pos)
 END

 INSERT INTO @returnList
 SELECT @stringToSplit

 RETURN
END

在存储过程中拆分字符串:

IF (@ID IN (SELECT * FROM dbo.splitstring(@CommaSeparatedString))

注意! 使用上述splitstring 函数时请注意空格。对于 '269, 270, 274' -> 逗号后的空格:

IF (@ID IN (SELECT RTRIM(LTRIM([Name])) FROM dbo.splitstring(@CommaSeparatedString))

【讨论】:

  • 我更喜欢通过添加可选参数来重载存储过程正如@Dan Guzman所说。这样其他用户就不会受到您的更改的影响
  • 对我的问题添加了一个编辑,这理清了我的推理。
  • 如果你无权更改或创建新的sp,那么我认为唯一的方法就是调用sp N次
  • 可能是这样......如果真的是这样,那就太糟糕了。
【解决方案4】:

正如@ikram 和@DanGuzman 所说,在不编辑现有过程或创建新过程的情况下,唯一的方法是执行存储的 N 次。使用例如 Vim 可以非常快速地构建查询。

从文件开始(实际数字更多):

269,270,274,280,282,292,300,320,324

并运行命令:

qqf,s<Enter><Esc>q
1000@q
<C-V>ggIEXEC FooStored @ID = 

结果:

EXEC FooStored @ID = 269
EXEC FooStored @ID = 270
EXEC FooStored @ID = 274
EXEC FooStored @ID = 280
EXEC FooStored @ID = 282
EXEC FooStored @ID = 292
EXEC FooStored @ID = 300
EXEC FooStored @ID = 320
EXEC FooStored @ID = 324

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2010-12-23
    • 2013-05-14
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多