2017-02-14 10 views
3

Мне нужно получить результат запроса полностью структурированного JSON. В postgres я вижу, что есть некоторые встроенные функции, которые могут быть полезны.Создайте вложенный json из sql-запросов 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 

Я полагаю, что я пропускаю некоторую группу по и 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 сделал – 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; 

The (отформатированный) Результат:

{ 
    "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

А что делать, если я хотел бы ограничить результаты только для первых 10 человек из БД? – Snorlax

+1

Вы должны заменить 'person' производной таблицей, то есть вместо' from person p' use' from (выберите * from person ... limit 10) p'. – klin

+0

Отлично! И еще одно: если я уверен, что у всего человека есть только одна машина, и я не хочу отображать массив объектов, но объект в результатах json? Как легко изменить его? – Snorlax

3

Я пришел к этому решению. Он довольно компактный и работает в любом конкретном случае. Не знаете, какое влияние оказывает на производительность по сравнению с другими решениями, которые больше используют json_build_object. Преимущество использования row_to_json over 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

, то просто добавьте двигатель рядом с колесами в подзапрос и преобразуйте его в json с функцией to_json. SELECT json_build_object ('people', json_agg (p)) people 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, который, серийный номер FROM wheel WHERE wheel.carid = car.id) w) колеса FROM car WHERE car.personid = человек .id) c) AS cars FROM person) p –