使用多个表源合并(用户定义的表类型和输入参数)
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 & Cutomer_Id
列,因为我们总是在验证这些列的匹配列以进入更新逻辑。但是,我怎样才能只更新正在修改的列(Customer_ID, Customer_Email, Customer_Notes)
,而不是在更改或不更改现有记录中的原始值的情况下更新它们。这个问题的原因是我为CustomOrderReqs
表创建了一个历史表并且不想创建重复
对不起,我的错。当我们检查匹配以更新现有记录时,我们可以从更新语句中删除列OrderNumber, Product_Id & 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
我创建了一个历史记录或日志表来跟踪更改,我不想记录没有对正在更新的列进行任何更改的记录以上是关于使用多个表源合并(用户定义的表类型和输入参数)的主要内容,如果未能解决你的问题,请参考以下文章