【问题标题】:Insert JSON array objects into two tables using SQL Server使用 SQL Server 将 JSON 数组对象插入两个表中
【发布时间】:2018-10-17 22:42:02
【问题描述】:

我有一个程序应该 OPENJSON 并同时插入到两个表中,JSON 数组中的每个对象都有一行。例如,数组中的这两个对象会将两行数据插入“case_idents”表并输出 2 个 case_ident_id(101 和 102)并将这两个 id 插入“case_to_case_idents”表中。此过程不这样做,而是仅将 case_ident_id (102) 两次插入到“case_to_case_idents”表中,创建一个副本而不是创建唯一的行对象。

CREATE PROCEDURE case_idents_addAll

@case_ident_id INT OUTPUT,
@seq_id INT OUTPUT,
@identObj NVARCHAR(MAX),

/*
DECLARE @case_ident_id INT
DECLARE @seq_id INT
DECLARE @identObj NVARCHAR(MAX) = N'[
      {  
            "name": "Jack", 
            "entityTypeCd": "SM", 
            "identStatus": "PR", 
            "caseId": 10034,
            "caseStatusCd": "NUA"
       },
       {  
            "name": "Jill", 
            "entityTypeCd": "SF", 
            "identStatus": "PR", 
            "caseId": 10035,
            "caseStatusCd": "NA"
       }
    ]';
 EXECUTE case_idents_addAll @identObj=@identObj, @case_ident_id=@case_ident_id OUTPUT, @seq_id=@seq_id OUTPUT
*/

AS

BEGIN

    INSERT INTO case_idents
    (name, entity_type_cd, ident_status, case_id)
    SELECT name, entity_type_cd, ident_status, case_id
    FROM OPENJSON(@identObj)
    WITH (
        name NVARCHAR(50) '$.name', 
        entity_type_cd CHAR(5) '$.entityTypeCd', 
        ident_status CHAR(5) '$.identStatus', 
        case_id INT '$.caseId'
    )
    SET @case_ident_id=SCOPE_IDENTITY();

    INSERT INTO case_to_case_ident
    (case_id, case_ident_id, case_status_cd, case_ident_status_cd)
    SELECT case_id, @case_ident_id, case_status_cd, case_ident_status_cd
    FROM OPENJSON(@identObj)
    WITH ( 
        case_id INT '$.caseId',
        case_status_cd CHAR(5) '$.caseStatusCd',
        case_ident_status_cd CHAR(5) '$.identStatus'
    )
    SET @seq_id = SCOPE_IDENTITY();

END

【问题讨论】:

  • case_id 在@identObj 中是否总是唯一的?阅读其他 cmets 我知道您可以进行多个调用,并且 case_id 在两个不同的调用中可能相同,但是同一 @identObj 值中是否会有多个 case_id ?如果您没有多个,可以在第一次插入时使用 OUTPUT 来捕获插入时的所有标识值,并在第二次插入中使用它。告诉我,我可以举个例子。
  • case_id 在@identObj 中不会是唯一的。因为每个案例可能有多个案例标识。

标签: json sql-server tsql


【解决方案1】:

这种事情最好用一个中间staging table来解决:

首先,我按原样将您的 JSON 数据读入一个临时表。此时,您可以在将卷输入目标表之前进行任何类型的验证和清理。

DECLARE @identObj NVARCHAR(MAX) = 
N'[
      {  
            "name": "Jack", 
            "entityTypeCd": "SM", 
            "identStatus": "PR", 
            "caseId": 10034,
            "caseStatusCd": "NUA"
       },
       {  
            "name": "Jill", 
            "entityTypeCd": "SF", 
            "identStatus": "PR", 
            "caseId": 10035,
            "caseStatusCd": "NA"
       }
    ]';

SELECT B.* 
INTO #tmpCases
FROM OPENJSON(@identObj) A
CROSS APPLY OPENJSON(A.value) 
WITH
(
    [name]       VARCHAR(100)
   ,entityTypeCd VARCHAR(100)
   ,identStatus  VARCHAR(100)
   ,caseId       INT
   ,caseStatusCd VARCHAR(100)
) B;

--目标表的模型

CREATE TABLE #case_idents(ID INT IDENTITY, [name] VARCHAR(100),entity_type_cd VARCHAR(100),ident_status VARCHAR(100),case_id INT);
CREATE TABLE #case_to_case_ident(case_id INT, case_ident_id INT /*fk to other table*/,case_status_cd VARCHAR(100),case_ident_status_cd VARCHAR(100));

--插入第一个

INSERT INTO #case_idents([name],entity_type_cd,ident_status,case_id)
SELECT [name],entityTypeCd,identStatus,caseId 
FROM #tmpCases;

--插入第二个

WITH cte AS
(
    SELECT c.*,ci.ID AS case_ident_id
    FROM #tmpCases c
    INNER JOIN #case_idents ci ON c.caseId=ci.case_id
)
INSERT INTO #case_to_case_ident(case_id,case_ident_id,case_status_cd,case_ident_status_cd)
SELECT caseId,case_ident_id,caseStatusCd,identStatus
FROM cte;

--检查结果

SELECT * FROM #case_idents
SELECT * FROM #case_to_case_ident;
GO

--clean(注意真实数据!)

DROP TABLE #tmpCases;
DROP TABLE #case_idents;
DROP TABLE #case_to_case_ident;

【讨论】:

  • SELECT B.* 时.* 是什么意思?此外,我还有一些不是通过过程创建的表,而这正是我希望存储永久数据的地方,所以我想知道为什么我需要删除这些表?
  • @hypnagogia B. * 解析 With 子句。不,不要删除你的表。这就是我写“模型”的原因。在我的场景中,我出于测试目的删除它们。希望这现在很清楚....
  • 该过程可以将正确的数据添加到两个表中,但是当从#tmpCases 表中选择时,我有一些红色曲线显示“列名无效”。我假设这是因为表 #tmpCases 尚未创建,并且 IntelliSense 在我运行该过程之前无法识别该对象。
  • @hypnagogia 确实,智能感知不够聪明,无法从 openjson 的 With 子句中计算出动态创建的列...
  • 实际上,在向表中添加数据的更多测试后,看起来数据被多次插入到第二个表“case_to_case_idents”中。例如,如果我一次执行该过程,我将两个 case_ident_id 插入到“case_idents”表中,并且第一次将相同的数量添加到“case_to_case_ident”表中。但是当我再次运行该过程时,我在“case_idents”表中获得了另外两个唯一的 case_ident_id,但“case_to_case_ident”表将前两个 id 以及新的 id 附加到它上面。例如,(130、131、130、131、132、133)。
【解决方案2】:

您使用 SCOPE_IDENTITY() 的方式是为什么在第二次插入时两条记录具有相同的值。

SCOPE_IDENTITY() - 返回插入到 同一范围内的标识列。范围是一个模块:一个存储的 过程、触发器、函数或批处理。因此,如果两个语句 在同一个存储过程、函数或批处理中,它们在 范围相同。

SCOPE_IDENTITY() 只会返回最后一个身份值。

如果您的@identObj 数据中有唯一标识符,您可以使用OUTPUT clause 并在插入数据时将所有标识列值捕获到表变量中,然后在第二次插入时加入。

我问了这个问题,但我假设你也可以探索这个选项,case_id 在@identObj 中是唯一的。

--temp tables
CREATE TABLE [#case_idents]
    (
        [ID] INT IDENTITY
      , [name] VARCHAR(255)
      , [entity_type_cd] VARCHAR(255)
      , [ident_status] VARCHAR(255)
      , [case_id] INT
    );
CREATE TABLE [#case_to_case_ident]
    (
        [case_id] INT
      , [case_ident_id] INT
      , [case_status_cd] VARCHAR(255)
      , [case_ident_status_cd] VARCHAR(255)
    );

--We'll use a table variable to capture all the identity field values at time of insert.
DECLARE @OutPut TABLE
    (
        [ID] INT
      , [case_id] INT
    );

DECLARE @identObj NVARCHAR(MAX) = N'[
      {  
            "name": "Jack", 
            "entityTypeCd": "SM", 
            "identStatus": "PR", 
            "caseId": 10034,
            "caseStatusCd": "NUA"
       },
       {  
            "name": "Jill", 
            "entityTypeCd": "SF", 
            "identStatus": "PR", 
            "caseId": 10035,
            "caseStatusCd": "NA"
       }
    ]';

INSERT INTO [#case_idents] (
                               [name]
                             , [entity_type_cd]
                             , [ident_status]
                             , [case_id]
                           )
--capture all the identities at time of insert into our table variable along with what the case_id was.
--Add OUTPUT right after your insert into the table variable.  Will capture the inserted values, the identity values you're after and what case_id they are associated with
OUTPUT [Inserted].[ID]
     , [Inserted].[case_id]
INTO @OutPut
            SELECT [name]
                 , [entity_type_cd]
                 , [ident_status]
                 , [case_id]
            FROM
                   OPENJSON(@identObj)
                       WITH (
                                [name] NVARCHAR(50) '$.name'
                              , [entity_type_cd] CHAR(5) '$.entityTypeCd'
                              , [ident_status] CHAR(5) '$.identStatus'
                              , [case_id] INT '$.caseId'
                            );


INSERT INTO [#case_to_case_ident] (
                                      [case_id]
                                    , [case_ident_id]
                                    , [case_status_cd]
                                    , [case_ident_status_cd]
                                  )
            SELECT     [ident].[case_id]
                     , [op].[ID]
                     , [ident].[case_status_cd]
                     , [ident].[case_ident_status_cd]
            FROM
                       OPENJSON(@identObj)
                           WITH (
                                    [case_id] INT '$.caseId'
                                  , [case_status_cd] CHAR(5) '$.caseStatusCd'
                                  , [case_ident_status_cd] CHAR(5) '$.identStatus'
                                ) AS [ident]
            INNER JOIN @OutPut [op]
                ON [op].[case_id] = [ident].[case_id]; --join here on case_id to get the identity value that was just created before that we captured and stored in our table variable.

SELECT *
FROM   [#case_idents];
SELECT *
FROM   [#case_to_case_ident];

【讨论】:

  • 谢谢,我回复了您对我的问题的评论。案例 ID 不是唯一的。另外,我不知道 SCOPE_IDENTITY()。我很困惑为什么它只添加了数组中的最后一个 ID。
【解决方案3】:

谢谢@Shnugo 和@Tim,把它放到一个临时表中是可行的,但是我不得不更改过程的“插入第二个表”部分,因为行被复制并且标识对象可以具有相同的 caseId,因此存在通过使用 caseId 将 case_ident 表连接到 #tmpCase 表的问题,这不是 case_to_case_ident 表所独有的。将相同的 importId 列添加到 case_ident 表和#tmpCases 表解决了 caseId 不唯一的问题,以及不重复 case_to_case_ident 表中的行。

DECLARE @identObj NVARCHAR(MAX) = 
N'[
      {  
            "name": "Jack", 
            "entityTypeCd": "SM", 
            "identStatus": "PR", 
            "caseId": 10034,
            "caseStatusCd": "NUA",
            "importId": 100
       },
       {  
            "name": "Jill", 
            "entityTypeCd": "SF", 
            "identStatus": "PR", 
            "caseId": 10035,
            "caseStatusCd": "NA",
            "importId": 101
       }
    ]';

SELECT B.* 
INTO #tmpCases
FROM OPENJSON(@identObj) A
CROSS APPLY OPENJSON(A.value) 
WITH
(
   [name] NVARCHAR(50),
   entityTypeCd CHAR(5),
   identStatus CHAR(5),
   caseId INT,
   caseStatusCd CHAR(5),
   importId INT
) B;

INSERT INTO case_idents 
(
    [name],
    entity_type_cd, 
    ident_status, 
    case_id, 
    import_id
)
SELECT 
    [name], 
    entityTypeCd, 
    identStatus, 
    caseId, 
    importId 
FROM #tmpCases;

-- 插入第二个表

MERGE case_to_case_ident AS cc
USING (
    case_idents ci 
    INNER JOIN #tmpCases t 
    ON ci.import_id = t.importId
)
ON ci.case_ident_id = cc.case_ident_id
WHEN NOT MATCHED THEN
INSERT 
(
    case_id,
    case_ident_id,
    case_status_cd,
    case_ident_status_cd
)
VALUES
(
    ci.case_id, 
    ci.case_ident_id, 
    t.caseStatusCd, 
    ci.ident_status
);

【讨论】:

    猜你喜欢
    • 2018-06-03
    • 1970-01-01
    • 2016-01-26
    • 2021-02-19
    • 2013-04-02
    • 2018-09-16
    • 1970-01-01
    • 2021-08-11
    • 1970-01-01
    相关资源
    最近更新 更多