SQL Server:在存储过程中的变量中存储多个值

Posted

技术标签:

【中文标题】SQL Server:在存储过程中的变量中存储多个值【英文标题】:SQL Server : store multiple values in variable in stored procedure 【发布时间】:2021-06-02 16:13:52 【问题描述】:

我正在尝试向存储过程中的变量添加多个值。 我的要求是将多个值存储在一个变量中,稍后再次使用它来删除这些值。

DECLARE @EmpID

SET @EmpID = (SELECT e.id 
              FROM employee e,Technology t
              WHERE e.status = 'InActive'
                AND e.id = t.Mainid
              UNION
              SELECT e.id 
              FROM employee e,Technology t
              WHERE e.status = 'InActive'
                AND e.id = t.AssociateID)

DELETE FROM Technology
WHERE Mainid IN (@EmpID) OR AssociateID IN (@EmpID)

当我尝试这个查询时,我得到一个错误

子查询返回超过 1 个值。当子查询跟随 =、!=、、>= 或子查询用作表达式时,这是不允许的。

【问题讨论】:

一个标量变量只能包含一个原子值。使用表类型参数/变量。 即使在子查询错误之前,您也会收到错误“关键字'Set'附近的语法不正确”,因为没有为标量变量@987654324指定数据类型@. Bad habits to kick : using old-style JOINs - 旧式 逗号分隔的表格列表 样式已替换为 ANSI 中的 proper ANSI JOIN 语法-92 SQL 标准(近 30 年前),不鼓励使用它 最后,停止使用 1980 年的旧语法。显式 JOIN 语法于 1992 年问世;差不多30年前。 Bad Habits to Kick : Using old-style JOINs(Marc 以秒的优势击败了我,但今年离开是为了真正把钉子钉进去)。 如果除了删除之外您不需要任何 ID:WITH RowsToDelete AS (SELECT T.* FROM Technology T JOIN Employee E ON E.status = 'InActive' AND (E.id = T.Mainid OR E.id = T.AssociateID) DELETE RowsToDelete。不需要联合、变量或其他更复杂的构造。作为奖励,您可以在删除之前先使用SELECT * FROM RowsToDelete,看看实际会发生什么。 【参考方案1】:

如上所述,您不能将变量视为数组,您需要使用表变量或临时表。

这样做看起来像下面这样(注意我还使用了更可取且可读的join语法)

declare @EmpId table (Id int)

insert into @EmdId(Id)
select e.id 
from employee e join Technology t on e.id = t.Mainid
where e.status = 'InActive'
union
select e.id 
from employee e join Technology t on e.id = t.AssociateID
where e.status = 'InActive'

delete from t
from @EmpId e join Technology t 
    on t.Mainid = e.Id or t.AssociateID=e.Id

但是,我认为不需要存储行的 ID 来删除它们。只需直接删除行 - 有多种方法可以这样做,一种是使用 existsEmployees 表关联:

delete from t
from Technology t 
where exists (select * from employee e where e.id = t.Mainid and e.status 'InActive')
or exists (select * from employee e where e.id = t.AssociateID and e.status 'InActive')

您可以将上述内容作为select * from 运行以验证要删除的行。

【讨论】:

您可以简化:where exists (select 0 from employee e where e.status = 'InActive' and e.id in (t.mainId, t.associateId)) 谢谢@pwilcox - 是的,这是可能的,我可能想调查生成的执行计划,可能会产生索引扫描而不是两个低成本索引搜索的风险;取决于数据和表索引。 谢谢@Stu ?。以上查询符合我的要求【参考方案2】:

鉴于您需要存储值以供以后使用,您可以使用 XML 数据类型来完成此操作。

SET NOCOUNT ON;

DECLARE @Employees table ( id int, [status] varchar(10) );
    INSERT INTO @Employees VALUES ( 1, 'InActive' ), ( 2, 'Active' );

DECLARE @Technology table ( AssociateID int, MainID int );
    INSERT INTO @Technology VALUES ( 1, 1 ), ( 2, 2 );

DECLARE @EmpIds xml = (

    SELECT * FROM (
        SELECT
            e.id 
        FROM @Employees AS e, @Technology AS t
        WHERE 
            e.[status] = 'InActive'
            AND e.id = t.MainID
        UNION
        SELECT
            e.id 
        FROM @Employees AS e, @Technology AS t
        WHERE
            e.[status] = 'InActive'
            AND e.id = t.AssociateID
    ) AS x
    FOR XML PATH ( '' ), ROOT ( 'employeeIds' )

);

DELETE FROM @Technology WHERE MainID IN (
    SELECT x.f.value( '.', 'int' ) FROM @EmpIds.nodes( '//employeeIds/id' ) x( f )
) OR AssociateID IN (
    SELECT x.f.value( '.', 'int' ) FROM @EmpIds.nodes( '//employeeIds/id' ) x( f )
);

【讨论】:

不知道为什么投反对票。该请求明确指出My requirement is to store multiple values in a variable and use it again later to delete the values. 这完成了。 抱歉没有解释。有时,如果要求本身在上下文中存在问题,那么满足规定的要求并不是一个好主意。在这里,我相信处于类似情况的 OP 和其他人最好被告知不要尝试他们的目标。向他们展示如何实现他们的目标,而不加任何警告说这是一个坏主意可能会导致他们走上一条坏路。这就是我投反对票的原因。 不确定你对表变量或临时表有什么,但xml 不是解决方案

以上是关于SQL Server:在存储过程中的变量中存储多个值的主要内容,如果未能解决你的问题,请参考以下文章

我应该如何使用 sql server 中的存储过程将列名存储在变量中?

在SQL Server存储过程中声明变量列表

Sql server 存储过程中怎么将变量赋值?

将@@ rowcount分配给SQL Server存储过程中的变量

SQL server基础SQL存储过程和函数的区别

SQL server简单的存储过程问题?