简体   繁体   English

将 JSON 文件保存到 SQL 服务器数据库表

[英]Saving JSON file to SQL Server Database tables

I am having a nested JSON file as shown below (where condition and rules can be nested to multiple levels)我有一个嵌套的JSON文件,如下所示(条件和规则可以嵌套到多个级别)

    {
    "condition": "and",
    "rules": [
      {
        "field": "26",
        "operator": "=",
        "value": "TEST1"
      },
      {
        "field": "36",
        "operator": "=",
        "value": "TEST2"
      },
      {
        "condition": "or",
        "rules": [
          {
            "field": "2",
            "operator": "=",
            "value": 100
          },
          {
            "field": "3",
            "operator": "=",
            "value": 12
          },
          {
            "condition": "or",
            "rules": [
              {
                "field": "12",
                "operator": "=",
                "value": "CA"
              },
              {
                "field": "12",
              "operator": "=",
              "value": "AL"
            }
          ]
        }
      ]
    }
  ]
}

I want to save this JSON (conditon and rules fields in json file can be nested to multiple levels) in to SQL Server Tables and later wanted to construct the same JSON from these created tables. I want to save this JSON (conditon and rules fields in json file can be nested to multiple levels) in to SQL Server Tables and later wanted to construct the same JSON from these created tables. How can i do this?我怎样才能做到这一点? From these table i am planning to get other json formats also that is why decided to split the json to table columns.从这些表中,我计划获得其他 json 格式,这也是为什么决定将 json 拆分为表列的原因。

I think need to create a recursive sql function to do same.我认为需要创建一个递归 sql function 来做同样的事情。

i have created following tables to save the same json.我创建了下表来保存相同的 json。

CREATE TABLE [Ruleset]
([RulesetID]       [BIGINT] IDENTITY(1, 1) NOT NULL PRIMARY KEY,
 [Condition]       [VARCHAR](50) NOT NULL,
 [ParentRuleSetID] [BIGINT] NULL
);
GO
CREATE TABLE [Rules]
([RuleID]    [BIGINT] IDENTITY(1, 1) NOT NULL PRIMARY KEY,
 [Fields]    [VARCHAR](MAX) NOT NULL,
 [Operator]  [VARCHAR](MAX) NOT NULL,
 [Value]     [VARCHAR](MAX) NOT NULL,
 [RulesetID] [BIGINT] NULL
                      FOREIGN KEY REFERENCES [Ruleset](RulesetID)
);

insert script as follows,插入脚本如下,

INSERT INTO [Ruleset] values  
 ('AND',0),
 ('OR',1),
 ('OR',2) 

 INSERT INTO [Rules] values  
 ('26','=','TEST1',1),
 ('364','=','TEST2',1),
 ('2','=','100',2),
 ('3','=','12',2),
  ('12','=','CA',3),
 ('12','=','AL',3)

Will these tables are enough?这些表就够了吗? Will be able to save all details?能保存所有细节吗?

Attaching the values that i have added to these tables manually.手动附加我添加到这些表中的值。

在此处输入图像描述

How can i save this JSON to these table and later will construct the same JSON from these tables via stored procedure or queries?如何将此 JSON 保存到这些表中,以后将通过存储过程或查询从这些表中构造相同的 JSON?

please provide suggestions and samples!请提供建议和样品!

Actually you can declare the column type as NVARCHAR(MAX), and save the json string into it.实际上,您可以将列类型声明为 NVARCHAR(MAX),并将 json 字符串保存到其中。

I feel like I would need to know more about how you plan to use the data to answer this.我觉得我需要更多地了解您计划如何使用数据来回答这个问题。 My heart is telling me that there is something wrong about storing this information in MSSQL, if not wrong, problematic.我的心在告诉我,将这些信息存储在 MSSQL 中是有问题的,如果没有错,那就是有问题。

If i had to do it, I would convert these conditions into a matrix lookup table of rotatable events within your branch, so for each conceivable logic branch you could create a row in a lookup to evaluate this.如果我必须这样做,我会将这些条件转换为分支内可旋转事件的矩阵查找表,因此对于每个可能的逻辑分支,您可以在查找中创建一行来评估它。

Depending out on your required output / feature set you can either do something like the above or just throw everything in a NVARCHAR as suggested by rkortekaas.根据您所需的 output / 功能集,您可以执行上述操作,也可以按照 rkortekaas 的建议将所有内容放入 NVARCHAR 中。

As JSON case sensitive please check your schema definition and sample data.由于 JSON 区分大小写,请检查您的架构定义和示例数据。 I see a discrepancy between the definition of the tables, their contents and your JSON我发现表格的定义、它们的内容和您的 JSON 之间存在差异

All scripts tested on MS SQL Server 2016在 MS SQL Server 2016 上测试的所有脚本

I used a temporary table variable in this script, but you can do without it.我在这个脚本中使用了一个临时表变量,但你可以不用它。 See an example in SQL Fiddle请参阅 SQL 小提琴中的示例

-- JSON -> hierarchy table
DECLARE @ExpectedJSON NVARCHAR(MAX) = '
{
    "condition": "and",
    "rules": [
      {
        "field": "26",
        "operator": "=",
        "value": "TEST1"
      },
      {
        "field": "36",
        "operator": "=",
        "value": "TEST2"
      },
      {
        "condition": "or",
        "rules": [
          {
            "field": "2",
            "operator": "=",
            "value": 100
          },
          {
            "field": "3",
            "operator": "=",
            "value": 12
          },
          {
            "condition": "or",
            "rules": [
              {
                "field": "12",
                "operator": "=",
                "value": "CA"
              },
              {
                "field": "12",
              "operator": "=",
              "value": "AL"
            }
          ]
        }
      ]
    }
  ]
}
'

DECLARE @TempRuleset AS TABLE 
(RulesetID          BIGINT NOT NULL PRIMARY KEY,
 condition          VARCHAR(50) NOT NULL,
 ParentRuleSetID    BIGINT NOT NULL,
 RulesJSON          NVARCHAR(MAX)
)

;WITH ParseRuleset AS (
    SELECT  1 AS RulesetID,
            p.condition,
            p.rules,
            0 AS ParentRuleSetID
    FROM OPENJSON(@ExpectedJSON, '$') WITH (
        condition   VARCHAR(50),
        rules       NVARCHAR(MAX) AS JSON
    ) AS p

    UNION ALL

    SELECT  RulesetID + 1,
            p.condition,
            p.rules,
            c.RulesetID AS ParentRuleSetID
    FROM ParseRuleset AS c
        CROSS APPLY OPENJSON(c.rules) WITH (
            condition   VARCHAR(50),
            rules       NVARCHAR(MAX) AS JSON
        ) AS p
    where
        p.Rules IS NOT NULL
)

INSERT INTO @TempRuleset (RulesetID, condition, ParentRuleSetID, RulesJSON)
SELECT  RulesetID,
        condition,
        ParentRuleSetID,
        rules
FROM ParseRuleset

 -- INSEERT INTO Ruleset ...
SELECT RulesetID,
        condition,
        ParentRuleSetID,
        RulesJSON
FROM @TempRuleset

-- INSERT INTO Rules ...
SELECT  RulesetID,
        field,
        operator,
        value
FROM @TempRuleset tmp
     CROSS APPLY OPENJSON(tmp.RulesJSON) WITH (
                field       VARCHAR(MAX),
                operator    VARCHAR(MAX),
                value       VARCHAR(MAX)
             ) AS p
WHERE   p.field IS NOT NULL

SQL Fiddle SQL 小提琴

Hierarchy tables -> JSON:层次结构表-> JSON:

CREATE TABLE Ruleset
(RulesetID       BIGINT IDENTITY(1, 1) NOT NULL PRIMARY KEY,
 condition       VARCHAR(50) NOT NULL,
 ParentRuleSetID BIGINT NULL
);
GO
CREATE TABLE Rules
(RuleID     BIGINT IDENTITY(1, 1) NOT NULL PRIMARY KEY,
 field      VARCHAR(MAX) NOT NULL,
 operator   VARCHAR(MAX) NOT NULL,
 value      VARCHAR(MAX) NOT NULL,
 RulesetID  BIGINT NULL FOREIGN KEY REFERENCES Ruleset(RulesetID)
);

INSERT INTO Ruleset values  
    ('and',0),
    ('or',1),
    ('or',2) 

INSERT INTO Rules values  
    ('26','=','TEST1',1),
    ('36','=','TEST2',1),
    ('2','=','100',2),
    ('3','=','12',2),
    ('12','=','CA',3),
    ('12','=','AL',3)

-- hierarchy table -> JSON
;WITH GetLeafLevel AS 
(
    SELECT  Ruleset.RulesetID,
            Ruleset.condition,
            Ruleset.ParentRuleSetID,
            1 AS lvl,
            (   SELECT  field,
                        operator,
                        value
                FROM    Rules
                WHERE   Rules.RulesetID = Ruleset.RulesetID
                FOR JSON AUTO, WITHOUT_ARRAY_WRAPPER 
            ) AS JSON_Rules
    FROM    Ruleset
    WHERE   ParentRuleSetID = 0
    UNION ALL
    SELECT  Ruleset.RulesetID,
            Ruleset.condition,
            Ruleset.ParentRuleSetID,
            GetLeafLevel.lvl + 1,
            (   SELECT  field,
                        operator,
                        value
                FROM    Rules
                WHERE   Rules.RulesetID = Ruleset.RulesetID
                FOR JSON AUTO, WITHOUT_ARRAY_WRAPPER 
            )
    FROM    Ruleset
            INNER JOIN GetLeafLevel ON Ruleset.ParentRuleSetID = GetLeafLevel.RulesetID
),
-- SELECT * FROM GetLeafLevel       -- debug 
ConcatReverseOrder AS 
(
    SELECT  GetLeafLevel.*,
            CONCAT('{"condition":"',
                    GetLeafLevel.condition,
                    '","rules":[',
                    GetLeafLevel.JSON_Rules,
                    ']}'
                    ) AS js
    FROM    GetLeafLevel
    WHERE   GetLeafLevel.lvl = (SELECT MAX(lvl) FROM GetLeafLevel)
    UNION ALL
    SELECT  GetLeafLevel.*,
            CONCAT('{"condition":"',
                            GetLeafLevel.condition,
                            '","rules":[',
                            GetLeafLevel.JSON_Rules,
                            ',',
                            ConcatReverseOrder.js,
                            ']}'
                            ) AS js
    FROM    GetLeafLevel
            INNER JOIN ConcatReverseOrder ON GetLeafLevel.RuleSetID = ConcatReverseOrder.ParentRuleSetID 
)
-- SELECT * FROM ConcatReverseOrder     -- debug 

SELECT  js
FROM    ConcatReverseOrder
WHERE   ParentRuleSetID = 0

SQL Fiddle SQL 小提琴

Your use case really does seem a perfect match for a NoSql Option such as MongoDb , Azure Table storage , or CosmosDB (CosmosDB can be pricey if you don't know your way round it).您的用例似乎确实与 NoSql 选项(例如MongoDbAzure 表存储CosmosDB不知道你的价格)完美匹配。

Extract from MongoDB page:摘自MongoDB页面:

In MongoDB, data is stored as documents.在 MongoDB 中,数据作为文档存储。 These documents are stored in MongoDB in JSON (JavaScript Object Notation) format.这些文档以 JSON(JavaScript Object 表示法)格式存储在 MongoDB 中。 JSON documents support embedded fields, so related data and lists of data can be stored with the document instead of an external table. JSON 文档支持嵌入字段,因此相关数据和数据列表可以与文档一起存储,而不是外部表。

However, from here on I'm going to assume you are tied to SQL Server for other reasons.但是,从这里开始,我将假设您因其他原因与 SQL 服务器相关联。

You have stated that you are going to are just putting the document in and getting the same document out, so it doesn't make sense to go to the effort of splitting out all the fields.您已声明您将只是将文档放入并取出相同的文档,因此 go 将所有字段分开是没有意义的。

SQL Server is much better at handling text fields than it used to be IMO. SQL 服务器在处理文本字段方面比 IMO 更好。

Systems I've worked on before have had the following columns (I would write the sql, but I'm not at my dev machine):我以前工作过的系统有以下几列(我会写 sql,但我不在我的开发机器上):

  • Id [Primary Key, Integer, Incrementing index] Id [主键,Integer,递增索引]
  • UserId [a Foreign Key to what this relates to - probably not 'user' in your case!] UserId [与此相关的外键 - 在您的情况下可能不是“用户”!]
  • Value [nvarchar(1000) contains the json as a string] Value [nvarchar(1000) 包含 json 作为字符串]

The lookup is easily done based on the foreign key.查找很容易根据外键完成。

However, suppose you want it to be more NoSql like, you could have:但是,假设您希望它更像 NoSql,您可以:

  • Id [Primary Key, Integer, Incrementing index] Id [主键,Integer,递增索引]
  • Key [nvarchar(100) a string key that you make, and can easily re-make for looking up the value (eg User_43_Level_6_GameData - this column should have an index] Key [nvarchar(100) 您创建的字符串键,并且可以轻松地重新创建以查找值(例如User_43_Level_6_GameData - 此列应该有一个索引]
  • Value [nvarchar(1000) contains the json as a string] Value [nvarchar(1000) 包含 json 作为字符串]

The reason I've kept to having an integer ID is to avoid fragmentation .我一直拥有 integer ID 的原因是为了避免碎片化 You could obviously make the Value column bigger.您显然可以使Value列更大。

Json can easily be converted between a json object and a json string. Json 可以在 json object 和 Z466DEEC76ECDF5FCA6D38571F6324D54 字符串之间轻松转换。 In Javascript, you would use Json Parse and Stringify .在 Javascript 中,您将使用 Json ParseStringify If you are using C# you could use the following snippets , though there are many ways to do this task (the objects can be nested as deep as you like)如果您使用的是 C#,您可以使用以下代码片段,尽管有很多方法可以完成此任务(对象可以嵌套任意深度)

.NET Object to Json .NET Object 至 Json

Weather w = new Weather("rainy", "windy", "32");天气 w = new Weather("rainy", "windy", "32"); var jsonString = JsonSerializer.Serialize(w); var jsonString = JsonSerializer.Serialize(w);

Json to .NET Object (C#) Json 到 .NET Object (C#)

var w = JsonSerializer.Deserialize(jsonString); var w = JsonSerializer.Deserialize(jsonString);

UPDATE更新

Although this is the way I've done things in the past, it looks like there are new options in sql server to handle JSON - OPENJSON and JSONQUERY could be potential options, though I haven't used them myself - they still use nvarchar for the JSON column.虽然这是我过去做事的方式,但看起来 sql 服务器中有新选项来处理 JSON - OPENJSONJSONQUERY可能是潜在的选项,尽管我自己没有使用它们 - 他们仍然使用nvarchar JSON 列。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM