如何在 SQL Server 中的 UPDATE 查询的子查询中引用表变量

Posted

技术标签:

【中文标题】如何在 SQL Server 中的 UPDATE 查询的子查询中引用表变量【英文标题】:How to reference a table variable in sub-query of an UPDATE query in SQL Server 【发布时间】:2015-09-02 14:34:18 【问题描述】:

我在一个存储过程中有一个 UPDATE 查询,我需要在其中更新一个已经被一些数据填充的表变量。由于在子查询中引用了表变量,即在子查询中引用了@daysPerEmail.ProductId,因此该查询(如下面的代码)给出了错误。我尝试使用 UPDATE @daysPerEmail d SET LastEmailDate... 中的表变量别名,但没有帮助。

问题:如何重写查询,以便在子查询中正确引用 ProductId 的表变量列?

UPDATE @daysPerEmail SET LastEmailDate = (SELECT max(ActionDate) FROM 
        useractionsreport  WHERE ProductId = @daysPerEmail.ProductId 
       and ActionTypeId = @userActionTypeId );

更新 1

为了在 SSMS 中重现这一点,我使用以下查询。 SSMS 给出错误提示 Must declare the scalar variable "@daysPerEmail".

DECLARE @daysPerEmail TABLE (
    VendorId INT,
    DaysPerEmail SMALLINT,
    LastEmailDate DATE,
    ProductId INT 
)

INSERT INTO @daysPerEmail (VendorId, DaysPerEmail, LastEmailDate, ProductId)
    VALUES (10, 5, NULL, 11), (12, 3, NULL, 15), (14, 1, NULL, 22);

UPDATE @daysPerEmail
SET LastEmailDate = (SELECT
    MAX(ActionDate)
FROM UserActionsReport   
WHERE ProductId = @daysPerEmail.ProductId
AND ActionTypeId = 2);

更新 2

Paolo 的回答按预期工作,但我还找到了另一个使用查询的简单解决方案,如下所示。我所要做的就是为 @daysPerEmail 表变量中的 ProductId 列提供一个唯一的名称。我将名称更改为 ProdId,它在 UPDATE 查询涉及的任何表中都不存在。然后我不必在子查询中引用表变量,因为 ProdId 列名仅在 UPDATE 查询中使用的表中的 @daysPerEmail 表变量中找到。

 DECLARE @daysPerEmail TABLE (
    VendorId INT,
    DaysPerEmail SMALLINT,
    LastEmailDate DATE,
    ProdId INT 
)

INSERT INTO @daysPerEmail (VendorId, DaysPerEmail, LastEmailDate,ProdId)
    VALUES (78, 5, NULL, 2), (78, 3, NULL, 387), (78, 1, NULL, 295);

UPDATE @daysPerEmail
SET LastEmailDate = (SELECT
    MAX(ActionDate)
FROM UserActionsReport   
WHERE ProductId = ProdId
AND ActionTypeId = 2);

【问题讨论】:

这是存储过程的表类型变量吗???您的更新声明是有效的。 是的。这是一个表变量,如declare @daysPerEmail (ProductId int, LastEmailDate Date) 如果您仔细查看过程定义,过程中的表类型变量为 READONLY ,您需要从该表类型变量 (select * into #temp from @daysPerEmail) 创建另一个表,然后更新该 @ 987654330@ 表。此表变量将仅为 READONLY。 这是无效的,因为 SSMS 给出了一个错误提示 Must declare the scalar variable "@daysPerEmail",即使它之前已经在存储过程中声明了。 你有一个列 @daysPerEmail.ProductId 的引用,它不在表声明本身中。 【参考方案1】:

使用方括号,例如 [@daysPerEmail] 而不是 @daysPerEmail

这样您就可以将别名与表变量一起使用。

【讨论】:

【参考方案2】:

加入更新怎么样? 如果我没记错的话,它不符合标准,但SQL-Server 允许:

UPDATE  X
SET     LastEmailDate = Z.MaxActionDate
FROM    @daysPerEmail AS X
        JOIN (
            SELECT  MAX(Y.ActionDate) AS MaxActionDate
                    , Y.ProductId
            FROM    UserActionsReport AS Y
            WHERE   ActionTypeId = 2
            GROUP BY Y.ProductId
        ) AS Z ON Z.ProductId = X.ProductId;

【讨论】:

在这种情况下 IIRC 是什么? 如果我没记错的话 另一个有效的解决方案是在定义表变量@daysPerEmail 时简单地将 ProductId 列名称更改为 ProdId。这意味着不需要在子查询中引用表变量,因为 ProdId 列名在 UPDATE 查询的所有表中都是唯一的。 是的,我现在明白了。听起来像是 SQL Server 中的一些重要术语。哈哈。

以上是关于如何在 SQL Server 中的 UPDATE 查询的子查询中引用表变量的主要内容,如果未能解决你的问题,请参考以下文章

SQL Server 锁定的 UPDATE 优化

如何在SQL Server 2005中回滚UPDATE查询?

SQL Server UPDATE语句的用法详解

SQL server 如何调试触发器!

相当于 Sql Server 中的 MySQL ON DUPLICATE KEY UPDATE

访问不允许SQL-Server列中的空值的INSERT或UPDATE(访问运行时错误3162)