使用 JSON 加载数据时在存储过程中处理 CROSS APPLY

Deal with CROSS APPLY in stored procedure when using a JSON to load data

提问人:codebot 提问时间:10/31/2023 最后编辑:David Browne - Microsoftcodebot 更新时间:10/31/2023 访问量:38

问:

我有 2 个表要使用 SQL Server 中的 SP 进行填充。就我而言,我将传递一个 JSON 数组作为数据输入,然后填充数据。在这 2 个表中,所有主键都是自动生成的。

当我生成表时,我需要将新创建的 s 用作每条记录的表中的外键。我能够使用下面的 SP 执行此操作,但是当我使用它时,它会将记录复制为数组的大小。PARENTIDPRODUCTCROSS APPLY

SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO
ALTER PROCEDURE [dbo].[SP_CREATE_PRODUCT]
    @parentJSONData NVARCHAR(MAX)
AS
BEGIN
BEGIN TRANSACTION;
    SAVE TRANSACTION StartPoint;
    DECLARE @PARENT TABLE ([ID] INT, [PARENT_NAME] NVARCHAR(50), [EFFECTIVE_FROM_WEEK] INT, [EFFECTIVE_TO_WEEK] INT, [ADD_FLAG] INT, [DEL_FLAG] INT)
    DECLARE @PRODUCT TABLE ([PRODUCT_CODE_ID] INT, [PRODUCT_STATUS_ID] INT, [PRODUCT_NAME] NVARCHAR(50), [PARENT_ID] INT NOT NULL)
    DECLARE @Output TABLE ([ID] INT,[PARENT_NAME] NVARCHAR(50), [EFFECTIVE_FROM_WEEK] INT)

    DECLARE @ErrorMessage  nvarchar(MAX)
    DECLARE @ErrorSeverity nvarchar(MAX)
    DECLARE @ErrorState    nvarchar(MAX)
BEGIN TRY
INSERT INTO PARENT 
        ([PARENT_NAME], [EFFECTIVE_FROM_WEEK], [EFFECTIVE_TO_WEEK], [ADD_FLAG], [DEL_FLAG]) OUTPUT inserted.ID, inserted.PARENT_NAME, inserted.EFFECTIVE_FROM_WEEK INTO @Output
SELECT [PARENT_NAME], [EFFECTIVE_FROM_WEEK], [EFFECTIVE_TO_WEEK], [ADD_FLAG], [DEL_FLAG]
FROM OPENJSON(@parentJSONData)  WITH (
    PARENT_NAME      NVARCHAR(50) '$.parent.parentName'
        , EFFECTIVE_FROM_WEEK INT '$.parent.effectiveFromWeek'
        , EFFECTIVE_TO_WEEK   INT '$.parent.effectiveToWeek'
        , ADD_FLAG        INT '$.parent.addFlag.id'
        , DEL_FLAG        INT '$.parent.delFlag.id'
    ) AS jsonValues;

INSERT INTO PRODUCT
([PRODUCT_CODE_ID], [PRODUCT_STATUS_ID], [PRODUCT_NAME], [PARENT_ID])
SELECT PRODUCT_CODE_ID, PRODUCT_STATUS_ID, PRODUCT_NAME, ID
FROM OPENJSON(@parentJSONData) WITH (
    PRODUCT_CODE_ID      NVARCHAR(50) '$.productCodeID'
        , PRODUCT_STATUS_ID INT '$.productStatusID'
        , PRODUCT_NAME   NVARCHAR(50) '$.productName'
    ) AS jsonValues CROSS APPLY @Output;
COMMIT TRANSACTION
END TRY
BEGIN CATCH
IF @@TRANCOUNT > 0
        ROLLBACK TRANSACTION
        SET @ErrorMessage  = ERROR_MESSAGE()
        SET @ErrorSeverity = ERROR_SEVERITY()
        SET @ErrorState    = ERROR_STATE()
        RAISERROR(@ErrorMessage, @ErrorSeverity, @ErrorState)
END CATCH
END;
GO

JSON格式

[{"parent":{"parentName":"Paper Pack","effectiveFromWeek":202423,"effectiveToWeek":202534,"addFlag":{"id":1,"state":"4x4"},"delFlag":{"id":1,"state":"4x4"}},"productCodeID":23386,"productStatusID":71013,"productName":"Paper Package"},{"parent":{"parentName":"Pen Pack","effectiveFromWeek":202423,"effectiveToWeek":202534,"addFlag":{"id":1,"state":"100"},"delFlag":{"id":1,"state":"100"}},"productCodeID":23387,"productStatusID":71023,"productName":"Pen Box"}]

有什么方法可以在进行交叉应用时仅在表中添加一次记录?否则,如果有办法在不使用 CROSS APPLY 的情况下执行此操作,请提出建议。PRODUCT

sql-server 存储过程

评论

0赞 siggemannen 10/31/2023
您应该以某种方式将 @t 表与 JSON 值联接起来,这样您就可以使行相互匹配,而不仅仅是交叉联接。当然,有某种键列可用于此
0赞 BateTech 10/31/2023
为什么要做一个 to 而不是一个 join to to 表或一个 join to ?交叉应用完全符合您的体验,如果其中有 3 条记录,那么每条记录都会将这 3 个父级应用于每个孩子,这将导致表中出现重复项。cross apply@outputparent@output@outputproduct
0赞 codebot 10/31/2023
我尝试了两者,并加入了。但这是一样的。INNERLEFTRIGHT
0赞 siggemannen 10/31/2023
那么,你要加入什么?
1赞 Charlieface 10/31/2023
旁注:你的错误处理是个坏主意。要重新引发错误,您应该只使用 但是,与其尝试回滚和重新引发自己,不如在顶部执行,服务器会为您执行此操作。也不需要THROW;SET XACT_ABORT ON;SAVE TRANSACTION

答:

2赞 siggemannen 10/31/2023 #1

以下是解决您的问题的一种解决方案:

INSERT INTO PRODUCT
([PRODUCT_CODE_ID], [PRODUCT_STATUS_ID], [PRODUCT_NAME], [PARENT_ID])
SELECT PRODUCT_CODE_ID, PRODUCT_STATUS_ID, PRODUCT_NAME, ID
FROM OPENJSON(@parentJSONData) WITH (
    PRODUCT_CODE_ID      NVARCHAR(50) '$.productCodeID'
        , PRODUCT_STATUS_ID INT '$.productStatusID'
        , PRODUCT_NAME   NVARCHAR(50) '$.productName'
        , PARENT_NAME      NVARCHAR(50) '$.parent.parentName' -- NEW!
        , EFFECTIVE_FROM_WEEK INT '$.parent.effectiveFromWeek'  -- NEW!
    ) AS jsonValues
INNER JOIN @Output o 
    ON  o.EFFECTIVE_FROM_WEEK = jsonValues.EFFECTIVE_FROM_WEEK -- NEW!
    AND o.PARENT_NAME = jsonValues.PARENT_NAME -- NEW!

我添加了一些列以在父表和输出表之间联接。如果这些还不够,你可以添加更多,直到你得到某种独特的 1 对 1 的事情。

评论

0赞 codebot 10/31/2023
这行得通。问题是我不知道我可以以这种方式使用此JSON值。谢谢你的帮助。学到新东西:)
0赞 siggemannen 10/31/2023
伟大!OPENJSON 就像一个虚拟表,因此可以像常规表一样使用它