sql >> Databáze >  >> RDS >> PostgreSQL

Postgres – Převeďte seznam sousedství na vnořený objekt JSON

Pomocí WITH RECURSIVE (https://www.postgresql.org/docs/current/static/queries-with.html) a funkce JSON (https://www.postgresql.org/docs/current/static/functions-json.html) I vytvořit toto řešení:

db<>housle

Základní funkce:

    WITH RECURSIVE tree(node_id, ancestor, child, path, json) AS  (
      SELECT 
          t1.node_id, 
          NULL::int, 
          t2.node_id,
          '{children}'::text[] || 
             (row_number() OVER (PARTITION BY t1.node_id ORDER BY t2.node_id) - 1)::text,-- C
          jsonb_build_object('name', t2.name, 'children', array_to_json(ARRAY[]::int[])) -- B
      FROM test t1
      LEFT JOIN test t2 ON t1.node_id = t2.parent_node                                   -- A
      WHERE t1.parent_node IS NULL

      UNION

      SELECT
          t1.node_id, 
          t1.parent_node, 
          t2.node_id,
          tree.path || '{children}' || (row_number() OVER (PARTITION BY t1.node_id ORDER BY t2.node_id) - 1)::text, 
          jsonb_build_object('name', t2.name, 'children', array_to_json(ARRAY[]::int[]))
      FROM test t1
      LEFT JOIN test t2 ON t1.node_id = t2.parent_node
      INNER JOIN tree ON (t1.node_id = tree.child)
      WHERE t1.parent_node = tree.node_id                                                -- D
    )
    SELECT                                                                               -- E
        child as node_id, path, json 
    FROM tree 
    WHERE child IS NOT NULL ORDER BY path

Každý WITH RECURSIVE obsahuje začátek SELECT a část rekurze (druhá SELECT ) kombinované pomocí UNION .

Odpověď:Spojení tabulky znovu pro nalezení potomků node_id .

B:Vytvoření objektu json pro potomka, který lze vložit do jeho rodiče

C:Vytvoření cesty, kam má být vložen podřízený objekt (z kořene). Funkce okna row_number() (https://www.postgresql.org/docs/current/static/tutorial-window.html) vygeneruje index potomka v poli dětí rodiče.

D:Část rekurze funguje jako počáteční část s jedním rozdílem:Nehledá kořenový prvek, ale prvek, který má nadřazený uzel poslední rekurze.

E:Provedení rekurze a filtrování všech prvků bez jakýchkoli potomků dává tento výsledek:

node_id   path                      json
2         children,0                {"name": "node2", "children": []}
4         children,0,children,0     {"name": "node4", "children": []}
5         children,0,children,1     {"name": "node5", "children": []}
6         children,0,children,2     {"name": "node6", "children": []}
3         children,1                {"name": "node3", "children": []}
7         children,1,children,0     {"name": "node7", "children": []}
8         children,1,children,1     {"name": "node8", "children": []}

Přestože jsem nenašel způsob, jak do rekurze přidat všechny podřízené prvky (počátek json není žádná globální proměnná; takže vždy zná změny přímých předků, nikoli jejich sourozenců), musel jsem iterovat řádky v sekundovém kroku.

Proto stavím funkci. Tam mohu provést iteraci pro globální proměnnou. Pomocí funkce jsonb_insert Všechny vypočítané prvky vkládám do kořenového objektu json - pomocí vypočítané cesty.

CREATE OR REPLACE FUNCTION json_tree() RETURNS jsonb AS $$
DECLARE
    _json_output jsonb;
    _temprow record;
BEGIN
    SELECT 
        jsonb_build_object('name', name, 'children', array_to_json(ARRAY[]::int[])) 
    INTO _json_output 
    FROM test 
    WHERE parent_node IS NULL;

    FOR _temprow IN
        /* Query above */
    LOOP
        SELECT jsonb_insert(_json_output, _temprow.path, _temprow.json) INTO _json_output;
    END LOOP;

    RETURN _json_output;
END;
$$ LANGUAGE plpgsql;

Posledním krokem je volání funkce a zvýšení čitelnosti JSON (jsonb_pretty() )

{
    "name": "node1",
    "children": [{
        "name": "node2",
        "children": [{
            "name": "node4",
            "children": []
        },
        {
            "name": "node5",
            "children": []
        },
        {
            "name": "node6",
            "children": []
        }]
    },
    {
        "name": "node3",
        "children": [{
            "name": "node7",
            "children": []
        },
        {
            "name": "node8",
            "children": []
        }]
    }]
}

Jsem si jistý, že je možné optimalizovat dotaz, ale pro náčrt to funguje.




  1. Proveďte analýzu produktu pomocí SQL Server Full-Text Search. Část 1

  2. Najít sloupce vrácené funkcí s hodnotou tabulky (příklady T-SQL)

  3. Změna hesla pomocí Oracle SQL Developer

  4. Použití zpětných značek kolem názvů polí