Транспонирование неизвестного набора произвольных строк в столбцы - PullRequest
0 голосов
/ 11 января 2019

Я работаю в системе, где у нас есть произвольные входящие данные, хранящиеся в структуре таблицы, которая в основном является хранилищем на основе значений ключей.

Структура таблицы в действительности немного сложнее, но для решения проблемы я приведу упрощенный пример:

CREATE TABLE records (id BIGINT, type TEXT, identifier TEXT);
CREATE TABLE record_items (id BIGINT, rec_id BIGINT, key TEXT, value TEXT);

INSERT INTO records VALUES (10, 'PERSON', 'P12823');
INSERT INTO record_items VALUES 
(11, 10, 'FIRST_NAME', 'James'),
(12, 10, 'LAST_NAME', 'Burwell'),
(13, 10, 'AGE', '76'),
(14, 10, 'HEIGHT', '5-9'),
(15, 10, 'WEIGHT', '149'),
(16, 10, 'BLOOD', 'A+');

INSERT INTO records VALUES (20, 'PERSON', 'P83494');
INSERT INTO record_items VALUES 
(21, 20, 'FIRST_NAME', 'David'),
(22, 20, 'LAST_NAME', 'McDonnell'),
(23, 20, 'AGE', '47'),
(24, 20, 'COMPANY', 'Super Saver Foods');

INSERT INTO records VALUES (30, 'PERSON', 'P81323');
INSERT INTO record_items VALUES 
(31, 30, 'FIRST_NAME', 'William'),
(32, 30, 'LAST_NAME', 'Nelson'),
(32, 30, 'HEIGHT', '5-6'),
(33, 30, 'VEHICLE', '2005 Dodge Charger');

Как видите, входящие данные имеют произвольные ключи. Есть некоторые ключи, которые присутствуют для всех входящих данных, а затем есть некоторые, которые изменяются от записи к записи, а некоторые отсутствуют:

SELECT r.id, r.type, r.identifier, ri.key, ri.value
  FROM records r
  JOIN record_items ri ON ri.rec_id = r.id
 ORDER BY r.id, ri.key

id | type   | ident  | key        | value
-------------------------------------------
10 | PERSON | P12823 | AGE        | 76
10 | PERSON | P12823 | BLOOD      | A+
10 | PERSON | P12823 | FIRST_NAME | James
10 | PERSON | P12823 | HEIGHT     | 5-9
10 | PERSON | P12823 | LAST_NAME  | Burwell
10 | PERSON | P12823 | WEIGHT     | 149
20 | PERSON | P83494 | AGE        | 47
20 | PERSON | P83494 | COMPANY    | Flix
20 | PERSON | P83494 | FIRST_NAME | David
20 | PERSON | P83494 | LAST_NAME  | Donnell
30 | PERSON | P81323 | FIRST_NAME | William
30 | PERSON | P81323 | HEIGHT     | 5-6
30 | PERSON | P81323 | LAST_NAME  | Nelson
30 | PERSON | P81323 | VEHICLE    | Dodge

Однако нам нужно получить все эти произвольные строки, транспонированные в столбцы (порядок столбцов не важен):

id | identifier | first_name | last_name | age | height | weight | company | vehicle
-----------------------------------------------------------------------------------
10 | P12823     | James      | Burwell   | 76  | 5-9    | 149    |         |
20 | P83494     | David      | Donnell   | 47  |        |        | Flix    |
30 | P81323     | William    | Nelson    |     | 5-6    |        |         | Dodge

Мне известно о существовании функции crosstab, однако я не смог заставить ее работать так, как нам нужно. Основная проблема заключается в том, что, насколько я понимаю, crosstab ожидает, что все записи имеют одинаковые ключи в одинаковом порядке, а любые отсутствующие или произвольные ключи смешивают набор результатов. Другая проблема, связанная с crosstab, заключается в том, что мне нужно явно определить столбцы для набора результатов, но нам нужно иметь возможность вызывать функцию аналогично следующему:

SELECT * 
  FROM some_crosstab_like_function('PERSON') -- PERSON identifies record type

Я думаю о создании какого-либо механизма, который состоит из функций и временных таблиц или представлений, которые создаются на лету и возвращают необходимые данные. Однако, прежде чем продолжить, я хотел бы узнать, есть ли другое, более простое решение моей проблемы.

Мы являемся PostgreSQL 10.5.

Любые идеи будут высоко оценены.

1 Ответ

0 голосов
/ 11 января 2019

Создание временной таблицы с агрегированными данными в объектах jsonb:

create temp table json_data as
select id, identifier, jsonb_object_agg(lower(key), value) as data
from (
    select r.id, r.type, r.identifier, ri.key, ri.value
    from records r
    join record_items ri on ri.rec_id = r.id
    ) s
group by 1, 2;

Таблица содержит данные в этом формате:

select * 
from json_data;

 id | identifier |                                                     data                                                      
----+------------+---------------------------------------------------------------------------------------------------------------
 10 | P12823     | {"age": "76", "blood": "A+", "height": "5-9", "weight": "149", "last_name": "Burwell", "first_name": "James"}
 20 | P83494     | {"age": "47", "company": "Super Saver Foods", "last_name": "McDonnell", "first_name": "David"}
 30 | P81323     | {"height": "5-6", "vehicle": "2005 Dodge Charger", "last_name": "Nelson", "first_name": "William"}
(3 rows)

Используйте функцию, описанную в Сведение агрегированных пар ключ / значение из поля JSONB

select create_jsonb_flat_view('json_data', 'id, identifier', 'data');

select *
from json_data_view

 id | identifier | age | blood |      company      | first_name | height | last_name |      vehicle       | weight 
----+------------+-----+-------+-------------------+------------+--------+-----------+--------------------+--------
 10 | P12823     | 76  | A+    |                   | James      | 5-9    | Burwell   |                    | 149
 20 | P83494     | 47  |       | Super Saver Foods | David      |        | McDonnell |                    | 
 30 | P81323     |     |       |                   | William    | 5-6    | Nelson    | 2005 Dodge Charger | 
(3 rows)

Примечание: вы можете (и, вероятно, должны) изменить функцию в соответствии со своими потребностями, в частности, вы можете захотеть создать временное представление вместо обычного.

...