将嵌套 JSON 标准化为多个表

问题描述 投票:0回答:2

我有一个表,其中每行都包含嵌套的 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]
GO

这帮助我将 JSON 解析为键和值对,但此查询的问题是在旋转时。对于每个 rowid,它都有多个联系人,因此在旋转过程中每个联系人都会更改顺序。

sql json sql-server normalization
2个回答
0
投票

我认为从 JSON 生成有用的 SQL Server 代码的一种快速有效的方法是利用 Visual Studio(社区版是免费的)和这个 Stack Overflow 答案中描述的方法。

首先,将 JSON 复制到剪贴板。然后,在 Visual Studio 中,导航到“编辑”菜单,选择“选择性粘贴”,然后选择“将 JSON 粘贴为类”。这将从您的 JSON 数据生成类,如下所示:

    public class Rootobject
    {
        public string Arn { get; set; }
        public string Merchant { get; set; }
        public string Aun { get; set; }
        public string ExternalArn { get; set; }
        public string Status { get; set; }
        public string PageIdentifier { get; set; }
        public Productrate ProductRate { get; set; }
        public Contact[] Contacts { get; set; }
        public string FinancialValidationProvider { get; set; }
        public bool IsFirstHomeBuyer { get; set; }
        public bool IsConditionalLoan { get; set; }
        public bool IsAvmUsable { get; set; }
        public bool IsPcUsable { get; set; }
        public bool IsEvrUsable { get; set; }
        public bool SelfEmployedLoan { get; set; }
        public bool BusinessLoan { get; set; }
        public int Id { get; set; }
        public DateTime CreatedOn { get; set; }
        public string CreatedBy { get; set; }
        public DateTime LastUpdatedOn { get; set; }
        public string LastUpdatedBy { get; set; }
        public string RowVersion { get; set; }
    }

    public class Productrate
    {
        public bool RollRate { get; set; }
        public bool IsFromRate { get; set; }
    }

    public class Contact
    {
        public Contact1 Contact { get; set; }
        public int Dependants { get; set; }
        public string MaritalStatus { get; set; }
        public string Type { get; set; }
        public object[] ApplicationDocumentIds { get; set; }
        public string DocumentProvider { get; set; }
        public Asset[] Assets { get; set; }
        public Expens[] Expenses { get; set; }
        public Income[] Incomes { get; set; }
        public Employment[] Employment { get; set; }
        public Propertyasset[] PropertyAssets { get; set; }
        public Liability[] Liabilities { get; set; }
        public bool HasHecsDebt { get; set; }
        public string ResidentialStatus { get; set; }
        public int Id { get; set; }
        public DateTime CreatedOn { get; set; }
        public string CreatedBy { get; set; }
        public DateTime LastUpdatedOn { get; set; }
        public string LastUpdatedBy { get; set; }
        public string RowVersion { get; set; }
        public Driverslicense DriversLicense { get; set; }
    }

    public class Contact1
    {
        public string FirstName { get; set; }
        public string Surname { get; set; }
        public string Pun { get; set; }
        public int Dependants { get; set; }
        public Email[] Emails { get; set; }
        public Phonenumber[] PhoneNumbers { get; set; }
        public object[] TrackingIdentifiers { get; set; }
        public int Id { get; set; }
        public DateTime CreatedOn { get; set; }
        public string CreatedBy { get; set; }
        public DateTime LastUpdatedOn { get; set; }
        public string LastUpdatedBy { get; set; }
        public string RowVersion { get; set; }
        public string Type { get; set; }
    }

    public class Email
    {
        public string EmailAddress { get; set; }
        public string Type { get; set; }
        public int Id { get; set; }
        public DateTime CreatedOn { get; set; }
        public string CreatedBy { get; set; }
        public DateTime LastUpdatedOn { get; set; }
        public string LastUpdatedBy { get; set; }
        public string RowVersion { get; set; }
    }

    public class Phonenumber
    {
        public string Type { get; set; }
        public string PhoneNumber { get; set; }
        public int Id { get; set; }
        public DateTime CreatedOn { get; set; }
        public string CreatedBy { get; set; }
        public DateTime LastUpdatedOn { get; set; }
        public string LastUpdatedBy { get; set; }
        public string RowVersion { get; set; }
    }

    public class Driverslicense
    {
    }

    public class Asset
    {
        public string Type { get; set; }
    }

    public class Expens
    {
        public float Total { get; set; }
    }

    public class Income
    {
        public string Type { get; set; }
        public float Total { get; set; }
    }

    public class Employment
    {
        public string BusinessName { get; set; }
        public string Type { get; set; }
        public string EmploymentType { get; set; }
        public string SalaryFrequency { get; set; }
    }

    public class Propertyasset
    {
        public float RentalIncome { get; set; }
        public float MonthlyRepayment { get; set; }
        public float OutstandingBalance { get; set; }
        public string Usage { get; set; }
        public Address Address { get; set; }
        public float OwnershipPercentage { get; set; }
    }

    public class Address
    {
        public string CombinedAddress { get; set; }
    }

    public class Liability
    {
        public string Type { get; set; }
    }

要使用 Stack Overflow 答案中的 FlySwat 代码:

  1. 在 Visual Studio 中创建一个新的类库项目。
  2. 将生成的类粘贴到新项目中。哪个命名空间并不重要
  3. 构建项目。这将在项目的 bin/Debug 或 bin/Release 目录中创建一个 DLL。
  4. 从 Visual Studio 输出窗口复制 DLL 的完整文件路径。
  5. 从 Stack Overflow 答案运行控制台应用程序,提供 DLL 的路径作为参数。

此过程将为所有表生成 SQL Server 表定义。虽然它可能并不完美,但它应该提供一个坚实的起点。当然你可以根据需要更改C#

需要注意的一件事:您的类包含多个“浮动”属性。在 SQL Server 端,您可能需要考虑对这些列使用整数或小数数据类型。


0
投票
CREATE TABLE Contact (
    RowId INT,
    FirstName NVARCHAR(255),
    Surname NVARCHAR(255),
    Pun NVARCHAR(255),
    Dependants INT,
    MaritalStatus NVARCHAR(255),
    Type NVARCHAR(255),
    Id INT,
    CreatedOn DATETIMEOFFSET,
    CreatedBy NVARCHAR(255),
    LastUpdatedOn DATETIMEOFFSET,
    LastUpdatedBy NVARCHAR(255),
    RowVersion NVARCHAR(255)
);

您现在可以使用下面的 SQL 查询来解析 JSON 并将联系人数据输入到“联系人”表中:

-- Parse and insert contact data
WITH JSONContacts AS (
    SELECT
        Id AS RowId,
        [key] AS ContactKey,
        [value] AS ContactJson
    FROM dbo.app2023
    CROSS APPLY OPENJSON(payload, '$.Contacts') 
),
ParsedContacts AS (
    SELECT
        RowId,
        ContactKey,
        FirstName = JSON_VALUE(ContactJson, '$.Contact.FirstName'),
        Surname = JSON_VALUE(ContactJson, '$.Contact.Surname'),
        Pun = JSON_VALUE(ContactJson, '$.Contact.Pun'),
        Dependants = JSON_VALUE(ContactJson, '$.Contact.Dependants'),
        MaritalStatus = JSON_VALUE(ContactJson, '$.Contact.MaritalStatus'),
        Type = JSON_VALUE(ContactJson, '$.Contact.Type'),
        Id = JSON_VALUE(ContactJson, '$.Contact.Id'),
        CreatedOn = JSON_VALUE(ContactJson, '$.Contact.CreatedOn'),
        CreatedBy = JSON_VALUE(ContactJson, '$.Contact.CreatedBy'),
        LastUpdatedOn = JSON_VALUE(ContactJson, '$.Contact.LastUpdatedOn'),
        LastUpdatedBy = JSON_VALUE(ContactJson, '$.Contact.LastUpdatedBy'),
        RowVersion = JSON_VALUE(ContactJson, '$.Contact.RowVersion')
    FROM JSONContacts
)
INSERT INTO Contact (RowId, FirstName, Surname, Pun, Dependants, MaritalStatus, Type, Id, CreatedOn, CreatedBy, LastUpdatedOn, LastUpdatedBy, RowVersion)
SELECT RowId, FirstName, Surname, Pun, Dependants, MaritalStatus, Type, Id, CreatedOn, CreatedBy, LastUpdatedOn, LastUpdatedBy, RowVersion
FROM ParsedContacts;
© www.soinside.com 2019 - 2024. All rights reserved.