2017-02-14 81 views
3

我需要從查詢完全結構化的JSON中得到結果。 我可以在postgres中看到有一些內置的函數可能有用。從sql查詢創建嵌套的json postgres 9.4

作爲一個例子我創建的結構如下:

-- Table: person 

-- DROP TABLE person; 

CREATE TABLE person 
(
    id integer NOT NULL, 
    name character varying(30), 
    CONSTRAINT person_pk PRIMARY KEY (id) 
) 
WITH (
    OIDS=FALSE 
); 
ALTER TABLE person 
    OWNER TO postgres; 

    -- Table: car 

-- DROP TABLE car; 

CREATE TABLE car 
(
    id integer NOT NULL, 
    type character varying(30), 
    personid integer, 
    CONSTRAINT car_pk PRIMARY KEY (id) 
) 
WITH (
    OIDS=FALSE 
); 
ALTER TABLE car 
    OWNER TO postgres; 

    -- Table: wheel 

-- DROP TABLE wheel; 

CREATE TABLE wheel 
(
    id integer NOT NULL, 
    whichone character varying(30), 
    serialnumber integer, 
    carid integer, 
    CONSTRAINT "Wheel_PK" PRIMARY KEY (id) 
) 
WITH (
    OIDS=FALSE 
); 
ALTER TABLE wheel 
    OWNER TO postgres; 

和一些數據:

INSERT INTO person(id, name) 
VALUES (1, 'Johny'), 
     (2, 'Freddy'); 

INSERT INTO car(id, type, personid) 
VALUES (1, 'Toyota', 1), 
     (2, 'Fiat', 1),  
     (3, 'Opel', 2);  

INSERT INTO wheel(id, whichone, serialnumber, carid) 
VALUES (1, 'front', '11', 1), 
     (2, 'back', '12', 1), 
     (3, 'front', '21', 2), 
     (4, 'back', '22', 2), 
     (5, 'front', '3', 3); 

正如我想有一個JSON對象,其中將包括的人列表中的結果,每個人將有汽車列表和每個車輪列表。

我想類似的東西,但它不是我想要的東西:

select json_build_object(
    'Persons', json_build_object(
    'person_name', person.name, 
    'cars', json_build_object(
     'carid', car.id,  
     'type', car.type, 
     'comment', 'nice car', -- this is constant 
     'wheels', json_build_object(
      'which', wheel.whichone, 
      'serial number', wheel.serialnumber 
     ) 

    )) 
) 

from 
person 
left join car on car.personid = person.id 
left join wheel on wheel.carid = car.id 

我想,我錯過了一些GROUP BY和json_agg,但我不知道如何做到這一點。

我想有作爲的結果是這樣的:

{ "persons": [ 
    { 
     "person_name": "Johny", 
     "cars": [ 
      { 
      "carid": 1, 
      "type": "Toyota", 
      "comment": "nice car", 
      "wheels": [{ 
       "which": "Front", 
       "serial number": 11 
      }, 
      { 
       "which": "Back", 
       "serial number": 12 
      }] 
      }, 
      { 
      "carid": 2, 
      "type": "Fiat", 
      "comment": "nice car", 
      "wheels": [{ 
       "which": "Front", 
       "serial number": 21 
      },{ 
       "which": "Back", 
       "serial number": 22 
      }] 
      } 
     ] 
    }, 
    { 
     "person_name": "Freddy", 
     "cars": [ 
      { 
      "carid": 3, 
      "type": "Opel", 
      "comment": "nice car", 
      "wheels": [{ 
       "which": "Front", 
       "serial number": 33 
      }] 
      }] 
    }] 
} 

http://www.jsoneditoronline.org/?id=7792a0a2bf11be724c29bb86c4b14577

+0

@a_horse_with_no_name done – Snorlax

回答

3

你應該建立一個分層查詢得到的分層結構的結果。

你想在單個json對象中有很多人,所以用json_agg()來收集json數組中的人。 類似地,一個人可以有多輛車,你應該把屬於一個人的汽車放在一個JSON數組中。這同樣適用於汽車和車輪。

select 
    json_build_object(
     'persons', json_agg(
      json_build_object(
       'person_name', p.name, 
       'cars', cars 
      ) 
     ) 
    ) persons 
from person p 
left join (
    select 
     personid, 
     json_agg(
      json_build_object(
       'carid', c.id,  
       'type', c.type, 
       'comment', 'nice car', -- this is constant 
       'wheels', wheels 
       ) 
      ) cars 
    from 
     car c 
     left join (
      select 
       carid, 
       json_agg(
        json_build_object(
         'which', w.whichone, 
         'serial number', w.serialnumber 
        ) 
       ) wheels 
      from wheel w 
      group by 1 
     ) w on c.id = w.carid 
    group by personid 
) c on p.id = c.personid; 

的(格式化)結果:

{ 
    "persons": [ 
     { 
      "person_name": "Johny", 
      "cars": [ 
       { 
        "carid": 1, 
        "type": "Toyota", 
        "comment": "nice car", 
        "wheels": [ 
         { 
          "which": "front", 
          "serial number": 11 
         }, 
         { 
          "which": "back", 
          "serial number": 12 
         } 
        ] 
       }, 
       { 
        "carid": 2, 
        "type": "Fiat", 
        "comment": "nice car", 
        "wheels": [ 
         { 
          "which": "front", 
          "serial number": 21 
         }, 
         { 
          "which": "back", 
          "serial number": 22 
         } 
        ] 
       } 
      ] 
     }, 
     { 
      "person_name": "Freddy", 
      "cars": [ 
       { 
        "carid": 3, 
        "type": "Opel", 
        "comment": "nice car", 
        "wheels": [ 
         { 
          "which": "front", 
          "serial number": 3 
         } 
        ] 
       } 
      ] 
     } 
    ] 
} 

如果你不熟悉嵌套派生表,你可以使用公共表表達式。 這種變異說明了查詢應建朝從最高級別的最嵌套的對象開始:我想出了這個解決方案

with wheels as (
    select 
     carid, 
     json_agg(
      json_build_object(
       'which', w.whichone, 
       'serial number', w.serialnumber 
      ) 
     ) wheels 
    from wheel w 
    group by 1 
), 
cars as (
    select 
     personid, 
     json_agg(
      json_build_object(
       'carid', c.id,  
       'type', c.type, 
       'comment', 'nice car', -- this is constant 
       'wheels', wheels 
       ) 
      ) cars 
    from car c 
    left join wheels w on c.id = w.carid 
    group by c.personid 
) 
select 
    json_build_object(
     'persons', json_agg(
      json_build_object(
       'person_name', p.name, 
       'cars', cars 
      ) 
     ) 
    ) persons 
from person p 
left join cars c on p.id = c.personid; 
+0

如果我想限制結果只給來自db的前10人,該怎麼辦? – Snorlax

+1

你應該用派生表替換'person',即用'from person p' use'from(select * from person ... limit 10)p'代替'person'。 – klin

+0

完美!還有一件事,如果我確定所有人都只有一輛車,並且我不想顯示對象數組而只是在結果json中顯示對象,如何輕鬆修改它? – Snorlax

3

。它非常緊湊,適用於任何特定情況。 然而,與其他更多使用json_build_object的解決方案相比,不確定會對性能產生何種影響。使用row_to_json優於json_build_object的好處是所有的工作都是在引擎蓋下完成的,這使得查詢更具可讀性。

SELECT json_build_object('persons', json_agg(p)) persons 
FROM (
     SELECT 
     person.name person_name, 
     (
      SELECT json_agg(row_to_json(c)) 
      FROM (
        SELECT 
        id carid, 
        type, 
        (
         SELECT json_agg(row_to_json(w)) 
         FROM (
          SELECT 
           whichone which, 
           serialnumber 
          FROM wheel 
          WHERE wheel.carid = car.id 
          ) w 
        ) wheels 
        FROM car 
        WHERE car.personid = person.id 
       ) c 
     ) AS  cars 
     FROM person 
) p 
+0

好吧,讓我們說,另外汽車可能有0或1個引擎。如何將它嵌套爲對象而不是對象數組? – Snorlax

+1

然後只需在次選中的車輪旁邊添加引擎,並使用to_json函數將其轉換爲json。 SELECT SELECT json_build_object('persons',json_agg(p))persons FROM(SELECT person.name person_name,(SELECT json_agg(row_to_json(c))FROM(SELECT id carid,type,(SELECT to_json(e)FROM(SELECT horsepower FROM engine其中engine.carid = car.id)e)引擎,(SELECT json_agg(row_to_json(w))FROM(SELECT whereone,serialnumber FROM wheel WHERE wheel.carid = car.id)w)wheels FROM car WHERE car.personid = person .id)c)AS車從人)p –