使用SQL Server

时间:2018-10-17 22:42:02

标签: json sql-server tsql

我有一个过程应为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

3 个答案:

答案 0 :(得分:1)

最好用中间的登台表解决这种问题:

首先,我按原样将您的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

-干净(小心真实的数据!)

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

答案 1 :(得分:1)

您使用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];

答案 2 :(得分:0)

谢谢@Shnugo和@Tim,将其放入一个临时表中可以正常工作,但是我不得不更改过程的“插入第二张表”部分,因为行被重复且ident对象可以具有相同的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
);