将嵌套的 JSON 规范化为多个表 [重复]

Normalize nested JSON into multiple tables [duplicate]

提问人:Suraj 提问时间:11/2/2023 最后编辑:marc_sSuraj 更新时间:11/3/2023 访问量:81

问:

我有一个表,其中每行都包含嵌套的 JSON。我想将此JSON规范化为包含数据的多个表。

例如,下面是 1 行的 JSON。

{
   "Arn":"UNLQ5HFPVXMX7",
   "Merchant":"UNL",
   "Aun":"aad9561d-17bc-4280-a44f-7fd4cdb2c57a",
   "ExternalArn":"b7e6d3da-4b63-44a8-adfc-884cce34288d",
   "Status":"Commenced",
   "PageIdentifier":"ValidationResult",
   "ProductRate":{
      "RollRate":false,
      "IsFromRate":false
   },
   "Contacts":[
      {
         "Contact":{
            "FirstName":"Lotsa",
            "Surname":"Deductibles",
            "Pun":"b5c648c5-0573-46b0-bc9d-4c4e24cbb1f1",
            "Dependants":2,
            "Emails":[
               {
                  "EmailAddress":"1d507ac2-0631-4d3d-906b-5e473b71cedc",
                  "Type":"PERSONAL",
                  "Id":85885,
                  "CreatedOn":"2023-01-02T22:48:53.127529+00:00",
                  "CreatedBy":"[email protected]",
                  "LastUpdatedOn":"2023-01-02T22:48:53.1275294+00:00",
                  "LastUpdatedBy":"[email protected]",
                  "RowVersion":"AAAAABhpD4k="
               }
            ],
            "PhoneNumbers":[
               {
                  "Type":"MOB",
                  "PhoneNumber":"+61403917776"
               }
            ],
            "TrackingIdentifiers":[
               
            ],
            "Id":89684,
            "CreatedOn":"2023-01-02T22:48:53.078947+00:00",
            "CreatedBy":"[email protected]",
            "LastUpdatedOn":"2023-01-02T22:48:53.0789472+00:00",
            "LastUpdatedBy":"[email protected]",
            "RowVersion":"AAAAABhpD4g="
         },
         "Dependants":2,
         "MaritalStatus":"MARRIED",
         "Type":"PA",
         "ApplicationDocumentIds":[
            
         ],
         "DocumentProvider":"EXT",
         "Assets":[
            {
               "Type":"CASH"
            }
         ],
         "Expenses":[
            {
               
            }
         ],
         "Incomes":[
            {
               "Type":"SAL",
               "Total":100000.0
            }
         ],
         "Employment":[
            {
               "BusinessName":"Nicks Bank",
               "Type":"CURRENT",
               "EmploymentType":"UN",
               "SalaryFrequency":"ANNUAL"
            }
         ],
         "PropertyAssets":[
            {
               "RentalIncome":1500.0,
               "MonthlyRepayment":2000.0,
               "OutstandingBalance":400000.0,
               "Usage":"IL",
               "Address":{
                  "CombinedAddress":"82 Hay St, HAYMARKET, NSW 2000"
               },
               "OwnershipPercentage":1.0
            },
            {
               "RentalIncome":2000.0,
               "MonthlyRepayment":2400.0,
               "OutstandingBalance":500000.0,
               "Usage":"IL",
               "Address":{
                  "CombinedAddress":"U 1 1 Malthouse Lane, Melbourne VIC 3000"
               },
               "OwnershipPercentage":0.5
            }
         ],
         "Liabilities":[
            {
               "Type":"CCL"
            },
            {
               "Type":"LCB"
            },
            {
               "Type":"OLB"
            },
            {
               "Type":"OLR"
            }
         ],
         "HasHecsDebt":true,
         "ResidentialStatus":"RENT",
         "Id":120028,
         "CreatedOn":"2023-01-02T22:48:53.1723177+00:00",
         "CreatedBy":"[email protected]",
         "LastUpdatedOn":"2023-01-02T22:48:53.1723178+00:00",
         "LastUpdatedBy":"[email protected]",
         "RowVersion":"AAAAABhpD5g="
      },
      {
         "Contact":{
            "FirstName":"Fewa",
            "Surname":"Deductibles",
            "Pun":"e0f64678-b116-4354-9c6b-70a31d966bf3",
            "Dependants":0,
            "Type":"OPPORTUNITY",
            "Emails":[
               {
                  "EmailAddress":"abcde-fghijk",
                  "Type":"PERSONAL",
                  "Id":85886,
                  "CreatedOn":"2023-01-02T22:48:54.5431097+00:00",
                  "CreatedBy":"[email protected]",
                  "LastUpdatedOn":"2023-01-02T22:48:54.54311+00:00",
                  "LastUpdatedBy":"[email protected]",
                  "RowVersion":"AAAAABhpD5M="
               }
            ],
            "PhoneNumbers":[
               {
                  "Type":"MOB",
                  "PhoneNumber":"+12345678",
                  "Id":87908,
                  "CreatedOn":"2023-01-02T22:48:54.547373+00:00",
                  "CreatedBy":"[email protected]",
                  "LastUpdatedOn":"2023-01-02T22:48:54.5473733+00:00",
                  "LastUpdatedBy":"[email protected]",
                  "RowVersion":"AAAAABhpD5Q="
               }
            ],
            "TrackingIdentifiers":[
               
            ],
            "Id":89685,
            "CreatedOn":"2023-01-02T22:48:54.5362658+00:00",
            "CreatedBy":"[email protected]",
            "LastUpdatedOn":"2023-01-02T22:48:54.536266+00:00",
            "LastUpdatedBy":"[email protected]",
            "RowVersion":"AAAAABhpD5I="
         },
         "Dependants":2,
         "MaritalStatus":"MARRIED",
         "Type":"SA",
         "ApplicationDocumentIds":[
            
         ],
         "DocumentProvider":"EXT",
         "Expenses":[
            {
               "Total":0.0
            }
         ],
         "Incomes":[
            {
               "Type":"SAL",
               "Total":90000.0
            }
         ],
         "Employment":[
            {
               "BusinessName":"Nicks Bank",
               "Type":"CURRENT",
               "EmploymentType":"UN",
               "SalaryFrequency":"ANNUAL"
            }
         ],
         "PropertyAssets":[
            {
               "RentalIncome":2000.0,
               "MonthlyRepayment":2400.0,
               "OutstandingBalance":500000.0,
               "Usage":"IL",
               "Address":{
                  "CombinedAddress":"U 1 1 Malthouse Lane, Melbourne VIC 3000"
               },
               "OwnershipPercentage":0.5
            }
         ],
         "Liabilities":[
            {
               "Type":"CCL"
            },
            {
               "Type":"LCB"
            },
            {
               "Type":"OLB"
            },
            {
               "Type":"OLR"
            }
         ],
         "HasHecsDebt":false,
         "ResidentialStatus":"RENT",
         "DriversLicense":{
            
         },
         "Id":120029,
         "CreatedOn":"2023-01-02T22:48:54.5511511+00:00",
         "CreatedBy":"[email protected]",
         "LastUpdatedOn":"2023-01-02T22:48:54.5511512+00:00",
         "LastUpdatedBy":"[email protected]",
         "RowVersion":"AAAAABhpD5o="
      }
   ],
   "FinancialValidationProvider":"EXT",
   "IsFirstHomeBuyer":false,
   "IsConditionalLoan":false,
   "IsAvmUsable":false,
   "IsPcUsable":false,
   "IsEvrUsable":false,
   "SelfEmployedLoan":false,
   "BusinessLoan":false,
   "Id":230417,
   "CreatedOn":"2023-01-02T22:48:53.0229128+00:00",
   "CreatedBy":"[email protected]",
   "LastUpdatedOn":"2023-01-02T22:48:54.5795498+00:00",
   "LastUpdatedBy":"[email protected]",
   "RowVersion":"AAAAABhpD6A="
}

例如,我想为联系人创建一个新表,如下所示,并转换下表中的JSON数据

create table contact 
(
    id int, 
    FirstName nvarchar(255), 
    MiddleName nvarchar(255), 
    Surname nvarchar(255),
    Pun nvarchar(255), 
    Gender nvarchar(255),
    DateOfBirth date
)

我尝试使用以下查询解析JSON

WITH JSONRoot AS 
( 
    SELECT 
        Id as RowId,
        CAST(hierarchyid::GetRoot().ToString() + CAST(ROW_NUMBER() OVER(ORDER BY (SELECT NULL)) AS NVARCHAR(4000)) + '/' AS NVARCHAR(4000)) as [HierarchyId], 
        [key],
        [value],
        CAST([type] AS INT) AS [type] 
    FROM 
        dbo.app2023
        CROSS APPLY OPENJSON(payload,'$') where [key] = 'Contacts'
    UNION ALL 
    SELECT 
        RowId,
        CAST(JSONRoot.[HierarchyId] + CAST(ROW_NUMBER() OVER(ORDER BY (SELECT NULL)) AS NVARCHAR(4000)) + '/' AS NVARCHAR(4000)), 
        CASE WHEN JSONRoot.[type] = 4 THEN JSONRoot.[key]+'['+t.[key]+']' ELSE t.[key] END,
        t.[value],
        CAST(t.[type] AS INT) 
    FROM 
        JSONRoot 
        CROSS APPLY OPENJSON(JSONRoot.[value],'$') t 
    WHERE [JSONRoot].[key] like 'Contact%' and
        JSONRoot.[type] > 3 /* Only parse complex data types */
) 
SELECT 
    RowId,
    --CAST([HierarchyId] AS HierarchyId) AS [HierarchyId],
    [key],
    [value]
    --,[type]
INTO
    ParseContact2023
FROM 
    JSONRoot 
ORDER BY 
    RowId,
    [HierarchyId]

这有助于我将 JSON 解析为键和值对,但此查询的问题在于在透视时。对于每个 rowid,它有多个联系人,因此在透视期间,每个联系人都会更改顺序。

JSON SQL-SERVER 规范化

评论

0赞 tinazmu 11/2/2023
您使用的 SQL Server 版本:OPENJSON 表值函数可用于此目的,在 SQLServer 2017 及更高版本中,它返回可用于保留联系人顺序的列。key

答:

1赞 tinazmu 11/2/2023 #1

假设您的表有一个 id,并且 JSON 文本位于名为 ;您可以使用表值函数来解析它,但是捕获/保留联系人的顺序有点困难,如下所示(我没有包括您的所有列):jsondatOPENJSON

select T.id
    , TJSONContact.*
from YourTable T
     CROSS APPLY OPENJSON (T.jsondat)
        with (
          Contacts nvarchar(max) as JSON
        ) TJSON
     CROSS APPLY OPENJSON (TJSON.Contacts, '$.Contact')
           WITH (  FirstName    nvarchar(60)
                 , Surname      nvarchar(60)
                 , Dependants   integer
            ) as TJSONContact;

如果在不使用架构的情况下使用并提供数组,则该函数将返回一个列,您可以使用该列来保留联系人的顺序。OPENJSONWITHkey

例如,这里有一组列。TJSON[key], value

select T.id
    , TJSONContact.*
from YourTable T
     CROSS APPLY OPENJSON (T.jsondat, '$.Contacts') TJSON
     CROSS APPLY OPENJSON (TJSON.value, '$.Contact')
           WITH (  FirstName    nvarchar(60)
                 , Surname      nvarchar(60)
                 , Dependants   integer
            ) as TJSONContact;

评论

0赞 tinazmu 11/3/2023
@Charlieface感谢您的建议;我认为 OP 可能想结合 TJSON 级别的一些属性。我同意这个想法,但我失去了测试环境;随意添加您的答案(或编辑此答案)。