2015-02-26

PostgreSQL: как получить несколько выборок (роусетов)

Переводя проект с использования AzureSQL (MSSQL) на PostgreSQL я столкнулся с тем, что последний не умеет возвращать результаты нескольких запросов SELECT (так называемые роусеты, т.е. наборы строк). К примеру, некая процедура генерирует SELECT из нескольких таблиц, чтобы отобразить отчет по этой сборной солянке. Так вот, в отличии от MSSQL, PostgreSQL возвращает только последий из них, такая уж особенность. Странно, но факт.

Что делать? Мне в голову приходили три варианта.

Один из них - создать в процедуре временные таблицы с гарантированно уникальными именами, сложить наборы строк в них и вернуть их список. На клиенте соответственно понадобится сделать запросы к этим таблицам и при необходимости их уничтожить. Минусов у такого подхода полно, взять хотя бы увеличение количества запросов к БД и возможные коллизии в случае забывания удаления временных таблиц (хотя это решаемо).

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

И наконец, третий вариант, который работает в PostgreSQL благодаря поддержке типа данных JSON. Думаю, простой демонстрации кода будет достаточно, чтобы понять, как это работает:

SELECT json_agg(json_build_array(r.id, r.title))
FROM (SELECT id, title FROM queries LIMIT 3) r

Результат:

[
    [1, "Query 1"],
    [2, "Query 2"],
    [3, "Query 3"],
]

Запрос:

SELECT json_build_array(
    (SELECT json_agg(json_build_array(r.id, r.title))
    FROM (SELECT id, title FROM queries LIMIT 2) r),
    (SELECT json_agg(json_build_array(r.id, r.name))
    FROM (SELECT id, name FROM companies LIMIT 2 OFFSET 2) r)
);

Результат:

[
    [
        [1, "Query 1"],
        [2, "Query 2"]
    ],
    [
        [3, "Company 1"],
        [4, "Company 2"]
    ]
]