SQL Server 2016-从单个表将逻辑层次结构构建为JSON结构

时间:2018-12-16 12:51:20

标签: sql json sql-server sql-server-2016

我有一个表,其中包含与非常大的文档有关的信息。该表如下所示:

this.configService.getConfig()
        .subscribe(
          (data: Config) => this.config = { ...data },
          error => this.error = error,
          () => this.preFetchData()
        );


  }
  preFetchData() {
    this.i = 0;
        for (const value of this.bases) {
          if (this.config) {
          this.i++;
          this.configService.tableData[this.i - 1].buy = this.config.rates[value.curr] - (this.config.rates[value.curr] / 100 * 5);
          this.configService.tableData[this.i - 1].sell = this.config.rates[value.curr] + (this.config.rates[value.curr] / 100 * 5);
          console.log(this.currency, this.date, this.configService.tableData[this.i - 1].buy);
          }
        }
  }

结果JSON应该看起来像(为了清楚起见,省略了ID | Title | Parent_ID | <other columns> --------+---------------+---------------+------------------- 0 | Root | null | ... 1 | Introduction | 0 | ... 2 | Glossary | 1 | ... 3 | Audience | 1 | ... 4 | "A" | 2 | ... 5 | "B" | 2 | ... 6 | "C" | 2 | ... 部分):

<other columns>

我确实有一个简单的(递归)过程可以处理此问题,但希望使用DBMS的JSON功能(可能使用CTE?)有更简单的方法。

1 个答案:

答案 0 :(得分:1)

是否知道父母/子女关系的最大深度?
然后,您可以像下面的示例那样将其拉开:

db <>小提琴here

的测试

测试数据:

CREATE TABLE documentdetails 
(
   ID INT PRIMARY KEY NOT NULL,
   Title VARCHAR(30) NOT NULL,
   Parent_ID INT,
   FOREIGN KEY (Parent_ID) REFERENCES documentdetails (ID) 
);

INSERT INTO documentdetails (ID, Title, Parent_ID) VALUES 
(1, 'Root', null), (2, 'Introduction', 1), (3, 'Glossary', 1),
(4, 'Audience', 1), (5, 'A', 2), (6,'B', 2), (7, 'C', 2), 
(8, 'Foo', null), (9, 'Bar Intro', 8), (10, 'Glossy stuff', 8), (11, 'What The Fook', 8),
(12, 'Yo', 9), (13, 'Ai', 10), (14, 'Potato', 11);

查询:

SELECT 
root.ID, 
root.Title,
(  
   SELECT lvl0.ID, lvl0.Title, 0 as Depth,
   (  
      SELECT lvl1.ID, lvl1.Title, 1 as Depth,
      ( 
         SELECT lvl2.ID, lvl2.Title, 2 as Depth,
         ( 
            SELECT lvl3.ID, lvl3.Title, 3 as Depth
            FROM documentdetails lvl3
            WHERE lvl3.Parent_ID = lvl2.ID
            FOR JSON PATH
         ) AS Contents
         FROM documentdetails lvl2
         WHERE lvl2.Parent_ID = lvl1.ID
         FOR JSON PATH
      ) AS Contents
      FROM documentdetails lvl1
      WHERE lvl1.Parent_ID = lvl0.ID
      FOR JSON PATH
   ) AS Contents
   FROM documentdetails lvl0
   WHERE lvl0.ID = root.ID
   FOR JSON PATH
) AS Contents
FROM documentdetails root
WHERE root.Parent_ID IS NULL;

结果:

ID  Title   Contents
--  -----   --------
1   Root    [{"ID":1,"Title":"Root","Depth":0,"Contents":[{"ID":2,"Title":"Introduction","Depth":1,"Contents":[{"ID":5,"Title":"A","Depth":2},{"ID":6,"Title":"B","Depth":2},{"ID":7,"Title":"C","Depth":2}]},{"ID":3,"Title":"Glossary","Depth":1},{"ID":4,"Title":"Audience","Depth":1}]}]
8   Foo     [{"ID":8,"Title":"Foo","Depth":0,"Contents":[{"ID":9,"Title":"Bar Intro","Depth":1,"Contents":[{"ID":12,"Title":"Yo","Depth":2}]},{"ID":10,"Title":"Glossy stuff","Depth":1,"Contents":[{"ID":13,"Title":"Ai","Depth":2}]},{"ID":11,"Title":"What The Fook","Depth":1,"Contents":[{"ID":14,"Title":"Potato","Depth":2}]}]}]

如果您不知道表格中的最大深度?
这是使用递归CTE来获得有关此想法的SQL。

WITH RCTE AS
(
   SELECT ID as rootID, 0 as lvl, ID, Parent_ID
   FROM documentdetails
   WHERE Parent_ID IS NULL

   UNION ALL

   SELECT r.rootID, lvl + 1, t.ID, t.Parent_ID
   FROM RCTE r
   JOIN documentdetails t ON t.Parent_ID = r.ID
)
SELECT rootID, MAX(lvl) as Depth, COUNT(*) as Nodes
FROM RCTE
GROUP BY rootID
ORDER BY MAX(lvl) DESC, COUNT(*) DESC;

或者相反,从孩子们那里播种。
(如果ID是PRIMARY KEY,则由于ID上的JOIN可能会更快)

WITH RCTE AS
(
   SELECT ID as baseID, 0 as lvl, ID, Parent_ID
   FROM documentdetails
   WHERE Parent_ID IS NOT NULL

   UNION ALL

   SELECT r.baseID, lvl + 1, t.ID, t.Parent_ID
   FROM RCTE r
   JOIN documentdetails t ON t.ID = r.Parent_ID
)
SELECT ID as rootID, MAX(lvl) as Depth
FROM RCTE 
WHERE Parent_ID IS NULL
GROUP BY ID
ORDER BY MAX(lvl) DESC, COUNT(*) DESC;