使用多个表源合并(用户定义的表类型和输入参数)

Posted

技术标签:

【中文标题】使用多个表源合并(用户定义的表类型和输入参数)【英文标题】:Merge using Multiple table sources (User Defined Table Type & Input Parameters) 【发布时间】:2020-11-23 00:48:57 【问题描述】:

我正在尝试将数据插入到名为“CustomOrderReqs”的表中。我将使用存储过程的用户定义的表类型和输入参数定期将数据插入/更新到此表中。我的决赛桌如下:

CustomOrderReqs

我已经定义了一个用户定义的表类型在 Upsert 存储过程中使用,如下

CREATE TYPE [dbo].[CustomOrderRequestsType] AS TABLE(
[Customer_ID] [varchar](50) NOT NULL,
[Customer_Email] [varchar](50) NOT NULL,
[Customer_Notes] [nvarchar](Max) NOT NULL ) Go

以下是我执行此操作的存储过程,当我尝试在 Merge 语句中使用用户定义的表类型和输入参数时,它会出现一些错误。

ALTER PROCEDURE [dbo].[sp_UpsertCustomDesignReqsTable] 
 @OrderNumber VARCHAR(30)
,@Product_Id VARCHAR(50)
,@Purchase_amt VARCHAR(Max)
,@Details CustomOrderRequestsType READONLY
AS
BEGIN
    SET NOCOUNT ON;

MERGE [dbo].[CustomOrderReqs] AS TARGET
USING (VALUES (@OrderNumber, @Product_Id, ,@Purchase_amt, @Details) ) AS SOURCE  /*** Error Here **/
ON (SOURCE.[@OrderNumber] = TARGET.[Order Number]) 
    AND (SOURCE.[@Product_Id] = TARGET.[Product_Id] 
    AND (SOURCE.[Cutomer_Id] = TARGET.[Customer_Id]))
WHEN MATCHED THEN 
UPDATE SET TARGET.[Order Number] = @OrderNumber,
           TARGET.[Product_Id] = @NegItem,
           TARGET.[Purchase_amt] = @Purchase_amt,
           TARGET.[Customer_ID] = Source.[Customer_ID],
           TARGET.[Customer_Email = Source.[Customer_Email,
           TARGET.[Customer_Notes] = Source.[Customer_Notes]
WHEN NOT MATCHED BY TARGET THEN
INSERT VALUES (
                @OrderNumber,
                @NegItem,
                @Purchase_amt,
                Source.[Customer_ID],
                Source.[Customer_Email,
                Source.[Customer_Notes]);
END

通过上述查询,我​​在 Source Declaration 行收到一条错误消息,消息如下:

消息 137,级别 16,状态 1,过程 sp_UpsertCustomDesignReqsTable,第 21 行 [批处理开始第 7 行] 必须声明标量变量“@Details”

任何建议将不胜感激。

【问题讨论】:

@Details 是一个表 - 你把它当作一个列?请说明您实际上要对包含 vars 和 table var 的行执行什么操作。 感谢@DaleK 的评论。我正在尝试使用用户定义的表类型(@Details)(Customer_Id) 来验证CustomOrderReqs 表中的匹配或现有记录,以便更新(如果存在)。它不是唯一被使用的信息,而且还使用@OrderNumber & @Product_Id 列,在@Details 用户定义的表类型之外。您可以在上述过程中的 WHEN MATCHED THEN 语句上方看到相同的内容。 【参考方案1】:

什么将变量添加到合并表中?为什么不直接使用它们。

此外,如果您先解决所有语法错误,包括定义@NegItem,您可能会发现事情更容易。

在插入时显式列出列也是最佳实践。

MERGE [dbo].[CustomOrderReqs] AS [TARGET]
USING @Details AS [SOURCE]
ON [SOURCE].OrderNumber = @OrderNumber 
    AND [SOURCE].Product_Id = @Product_Id 
    AND [SOURCE].Cutomer_Id = [TARGET].Customer_Id
WHEN MATCHED THEN 
UPDATE SET
    [TARGET].Customer_ID = [SOURCE].Customer_ID
    , [TARGET].Customer_Email = [SOURCE].Customer_Email
    , [TARGET].Customer_Notes = [SOURCE].Customer_Notes
WHEN NOT MATCHED BY TARGET THEN
INSERT (OrderNumber, ProductId, Amount, Customer_Id, Customer_Email, Customer_Notes)
VALUES (@OrderNumber, @Product_Id, @Purchase_amt, [SOURCE].[Customer_ID], [SOURCE].[Customer_Email, [SOURCE].[Customer_Notes]);

由于您在 cmets 中添加了仅在值更改时更新的进一步要求,请考虑将行 WHEN MATCHED THEN 更改为

WHEN MATCHED THEN AND (
    -- Assume ID is never null
    [SOURCE].Customer_ID <> [TARGET].Customer_ID
    -- Handle null
    OR COALESCE([SOURCE].Customer_Email,'') <> COALESCE([TARGET].Customer_Email,'')
    -- Handle null
    OR COALESCE([SOURCE].Customer_Notes,'') <> COALESCE([TARGET].Customer_Notes,'')
)

但是我会在日志记录触发器中而不是在此处添加重复检测。

注意:Official Docs 解释了所有这些以及更多内容。

【讨论】:

最后一个问题,我怎样才能只更新正在修改的列.. 在这种情况下,我们可以删除更新集的 OrderNumber, Product_Id &amp; Cutomer_Id 列,因为我们总是在验证这些列的匹配列以进入更新逻辑。但是,我怎样才能只更新正在修改的列(Customer_ID, Customer_Email, Customer_Notes),而不是在更改或不更改现有记录中的原始值的情况下更新它们。这个问题的原因是我为CustomOrderReqs 表创建了一个历史表并且不想创建重复 对不起,我的错。当我们检查匹配以更新现有记录时,我们可以从更新语句中删除列OrderNumber, Product_Id &amp; Cutomer_Id,如下所示。` WHEN MATCHED THEN UPDATE SET [TARGET].Purchase_amt = @Purchase_amt, [TARGET].Customer_ID = [SOURCE].Customer_ID, [TARGET].Customer_Email = [SOURCE].Customer_Email , [TARGET].Customer_Notes = [SOURCE].Customer_Notes WHEN NOT MATCHED BY TARGET THEN ` 但是,我只想更新 (Customer_ID, Customer_Email, Customer_Notes 列,它们只是被修改,而不是每次更新逻辑命中时都更新它们 你可以忽略@negItem,它应该是@Product_Id 我创建了一个历史记录或日志表来跟踪更改,我不想记录没有对正在更新的列进行任何更改的记录

以上是关于使用多个表源合并(用户定义的表类型和输入参数)的主要内容,如果未能解决你的问题,请参考以下文章

如何制作一个以用户定义的表类型为参数并在sql中返回相同的函数?

c++链表类模板问题(不要用c语言,用c++)

更改链接表源访问 2016

EF Core 使用许多用户定义的表类型执行过程

用户定义表类型的缓冲区格式

CodeIgniter 中的表类和页面视图