Предварительная строка с использованием CONCAT / CONCAT_WS с результатами STRING_AGG, если STRING_AGG НЕ пусто или равно NULL? - PullRequest
1 голос
/ 07 апреля 2020

У меня есть данные JSONB, которые хранятся в столбце данных следующим образом:

...
"dealers": [
    { "dealership":"LHM"},
    { "dealership”:"Camp"},
    { "dealership":"HMA"}
  ],

"cars": [
    { "name":"Ford"},
    { "name":"BMW" },
    { "name":"Fiat"}
  ],
...

Есть еще несколько атрибутов, которые хранят еще несколько массивов объектов, но ради упрощения моего вопроса я только хотел упомянуть эти два.

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

SELECT
    id
  , STRING_AGG(car->>'name', ', ')
FROM
  autos,
  json_array_elements(autos.data->'cars') as car
GROUP BY 1

Это приводит к одному полю, которое выглядит так:

Ford, BMW, Fiat

Моя конечная цель теперь состоит в том, чтобы иметь возможность объединить все другие атрибуты объектов STRING_AGG из массивов MULTIPLE объектов в одно поле с кратким описанием каждого, но ТОЛЬКО если результат STRING_AGG НЕ ПУСТО и NOT NULL (Наиболее важным в моем случае является НЕ ПУСТО (''). Например, это должно выглядеть следующим образом:

...//Cars: Ford, BMW, Fiat //Dealerships: LMH, Camp, HMA //... // etc.

Я пытался использовать CONCAT и CONCAT_WS, и пытался использовать NULLIF, но я не уверен, как добиться того, что я ищу. В частности, если можно добавить строку описания к каждому результату STRING_AGG. Вы можете помочь?

ОБНОВЛЕНИЕ: я смог выяснить, как это сделать с case-заявления внутри CONCAT, но теперь мне интересно, является ли это хорошим способом сделать это или это можно сделать аналогичным образом? способ S-Man дан ответ. Спасибо.

1 Ответ

1 голос
/ 07 апреля 2020

Нажмите: пошаговая демонстрация: db <> fiddle

WITH autos AS (
    SELECT '{"cars": [{"name": "BMW", "year": 1991}, {"name": "Ford", "model": "Mustang"}, {"name": "Fiat"}, {"name": "VW", "model": "Golf", "year": 2000}, {"name": "VW", "model": ""}]}'::jsonb as data
)
SELECT 
    'Cars: ' || STRING_AGG(NULLIF(car ->> 'name', ''), ',') || ' // '
    'Years: ' || STRING_AGG(NULLIF(car ->> 'year', ''), ',') || ' // '
    'Models: ' || STRING_AGG(NULLIF(car ->> 'model', ''), ',')
FROM autos,
    jsonb_array_elements(data->'cars') as car

Используя этот набор данных:

{
    "cars": [
        {
            "name": "BMW",
            "year": 1991
        },
        {
            "name": "Ford",
            "model": "Mustang"
        },
        {
            "name": "Fiat"
        },
        {
            "name": "VW",
            "year": 2000,
            "model": "Golf"
        },
        {
            "name": "VW",
            "model": ""
        }
    ]
}

Вы видите: нет model (== null) для BMW, нет year для Ford, вообще нет данных для Fiat, все атрибуты для VW Golf, пустое model для второго VW.

Сначала получите таблицу для всех данных:

SELECT 
    car ->> 'name' as name,
    car ->> 'year' as year,
    car ->> 'model' as model
FROM autos,
    jsonb_array_elements(data->'cars') as car

Вы уже использовали функцию jsonb_array_elements(), которая расширяет массив JSON. Оператор ->> дает значение атрибутов (в данном случае каждого элемента массива) как text.

Теперь вы можете нормализовать пустые значения (model второго VW, в этом случае) до NULL с использованием NULLIF(). Вы можете сделать значение равным NULL, если оно пустое:

NULLIF(myvalue, '')

Это приведет вас к:

SELECT 
    NULLIF(car ->> 'name', '') as name,
    NULLIF(car ->> 'year', '') as year,
    NULLIF(car ->> 'model', '') as model
FROM autos,
    jsonb_array_elements(data->'cars') as car

Теперь у вас больше нет пустых значений, но много NULL values.

Теперь вы можете использовать STRING_AGG() для агрегирования всех значений, как вы ожидаете. Подсказка в том, что STRING_AGG() автоматически игнорирует NULL значения, поэтому вам не нужно колебаться.

SELECT 
    STRING_AGG(NULLIF(car ->> 'name', ''), ',') as names,
    STRING_AGG(NULLIF(car ->> 'year', ''), ',') as years,
    STRING_AGG(NULLIF(car ->> 'model', ''), ',') as models
FROM autos,
    jsonb_array_elements(data->'cars') as car

Теперь у вас есть только три столбца и одна строка. Каждое значение агрегируется в строку.

Наконец, вы хотите объединить их в одну строку. Для этого вы можете использовать CONCAT() или оператор ||:

SELECT 
    'Cars: ' || STRING_AGG(NULLIF(car ->> 'name', ''), ',') || ' // '
    'Years: ' || STRING_AGG(NULLIF(car ->> 'year', ''), ',') || ' // '
    'Models: ' || STRING_AGG(NULLIF(car ->> 'model', ''), ',')
FROM autos,
    jsonb_array_elements(data->'cars') as car

Редактировать:

Уточненные данные:

{
    "cars": [
        {
            "name": "BMW"
        },
        {
            "name": "Ford"
        },
        {
            "name": "Fiat"
        }
    ],
    "dealers": [
        {
            "dealership": "LHM"
        },
        {
            "dealership": "Camp"
        },
        {
            "dealership": "HMA"
        }
    ]
}

Нажмите: demo: db <> fiddle

SELECT
    'Cars: ' || s1.names || ' // Dealers: ' || s2.dealerships
FROM
    autos, 
    LATERAL (SELECT string_agg(c ->> 'name', ',') as names FROM jsonb_array_elements(data->'cars') as c) s1,
    LATERAL (SELECT string_agg(d ->> 'dealership', ',') as dealerships FROM jsonb_array_elements(data->'dealers') as d) s2

В этом случае вам необходимо расширить и объединить массивы перед присоединением. Пожалуйста, добавьте функцию NULLIF() в соответствующие столбцы, как сделано выше.

...