2017-02-14 84 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 –