【问题标题】:Postgres LTREE display by grouping parents and childrenPostgres LTREE 通过对父母和孩子进行分组显示
【发布时间】:2019-07-18 18:12:34
【问题描述】:

我有一个示例 Ltree 结构,但我想将其作为 JSON 结构返回。 我试过搜索堆栈溢出,但结果给出了错误的响应。

create table node
(
    id   integer not null,
    name varchar(255),
    path ltree   not null
);

我有这些数据

INSERT INTO node (id,name,path) VALUES (1,'Residential','1');
INSERT INTO node (id,name,path) VALUES (2,'Commercial','2');
INSERT INTO node (id,name,path) VALUES (3,'Industrial','3');
INSERT INTO node (id,name,path) VALUES (4,'Res type 1','1.4');
INSERT INTO node (id,name,path) VALUES (5,'Comm type 1','2.5');
INSERT INTO node (id,name,path) VALUES (6,'Industrial 1','3.6');
INSERT INTO node (id,name,path) VALUES (7,'Residential 2','1.4.7');
INSERT INTO node (id,name,path) VALUES (8,'Commercial 2','2.5.8');
INSERT INTO node (id,name,path) VALUES (9,'Industrial 2','3.6.9');

这就是我想通过查询收集的内容

[
  {
    "name": "Residentioal",
    "children": [
      {
        "name": "Res type 1",
        "children": [
          {
            "name": "Residential 2",
            "children": []
          }
        ]
      }
    ]
  },
  {
    "name": "Commercial",
    "children": [
      {
        "name": "Comm type 1",
        "children": [
          {
            "name": "Commercial 2",
            "children": []
          }
        ]
      }
    ]
  },
  {
    "name": "Industrial",
    "children": [
      {
        "name": "Industrial 1",
        "children": [
          {
            "name": "Industrial 2",
            "children": []
          }
        ]
      }
    ]
  }
]

我试过recursive with ..,但它一直在循环,没有返回正确的值。

【问题讨论】:

  • 您的 ltree 路径不符合您的预期输出。 “商业”位于 id 1 和“工业 1”下。 “工业 2”位于不存在的 3.6 下。我在下面的示例中更正了数据。
  • @S-Man 对不起,我这边有错字

标签: sql postgresql psql ltree


【解决方案1】:

您需要两个部分,一个是递归,另一个是一个函数。我已经在hereherehere 解释了这一点,所以请查看那里以获得进一步的解释。

demo:db<>fiddle

递归

WITH RECURSIVE cte AS (
    SELECT 
        id,
        name,
        path,
        json_build_object('name', name, 'children', ARRAY[]::text[]) AS jsonobject,
        ARRAY[]::text[] || (row_number() OVER () - 1)::text as jsonpath,
        0 as depth        
    FROM node
    WHERE path = subpath(path, 0, 1) --parents

    UNION ALL

    SELECT
        n.id, 
        n.name, 
        n.path,
        json_build_object('name', n.name, 'children', ARRAY[]::text[]),
        jsonpath || '{children}' || (row_number() OVER (PARTITION BY subpath(n.path, depth, 1)::text ORDER BY subpath(n.path, depth + 1, 1)::text::int) - 1)::text,
        c.depth + 1
    FROM
        node n
    JOIN cte c 
    ON c.id = subpath(n.path, depth, 1)::text::int
       AND nlevel(n.path) = depth + 2 AND subpath(n.path, depth + 1, 1)::text::int = n.id
)
SELECT * FROM cte

功能

CREATE OR REPLACE FUNCTION nested_json() RETURNS jsonb AS $$
DECLARE
    _json_output jsonb;
    _temprow record;
BEGIN   
    _json_output := '[]'::jsonb;

    FOR _temprow IN
        -- <Add the CTE from above here>
    LOOP
        SELECT 
        jsonb_insert(
            _json_output, 
            _temprow.jsonpath, 
            _temprow.jsonobject
        )
        INTO _json_output;
    END LOOP;   

    RETURN _json_output;
END;
$$ LANGUAGE plpgsql;

请注意: ltree 结构对于这个用例来说并不是一个很好的选择,因为您需要一次又一次地计算子路径。对父级的简单引用会更有帮助和更快。


编辑: dbfiddle 管理员很棒并且安装了 ltree 扩展,所以有一个新的 fiddle

【讨论】:

  • 我正在运行 Postgres 9.5,而 JSONB_INSERT 仅从 9.6 开始可用,是否可以替代它?
  • @Bardia jsonb_set 在这种情况下同样有效dbfiddle.uk/…
  • 我是根据您提供的其他示例执行此操作的,但是有一个小问题,如果我有两个或多个有孩子的父母,它将全部分配给第一个父母SQL
  • @Bardia 你是对的。但是在您的示例中只有一个根,它可以正常工作dbfiddle.uk/…
  • @Bardia 如果它对您有用,请不要忘记投票(这是为了表彰回复者在您问题上投入的工作和时间)并接受(这表明问题已经解决)。否则,请告诉我需要进行哪些更改。
猜你喜欢
  • 2017-07-20
  • 1970-01-01
  • 2016-07-31
  • 2021-06-04
  • 2021-10-17
  • 2020-03-30
  • 1970-01-01
  • 2017-06-26
相关资源
最近更新 更多