2015-01-15 2 views
0

У меня есть данные следующим образом:Необходимо выбрать элемент массива JSON динамически из таблицы PostGreSQL

ID Name Data 
1 Joe  ["Mary","Joe"] 
2 Mary ["Sarah","Mary","Mary"] 
3 Bill ["Bill","Joe"] 
4 James ["James","James","James"] 

Я хочу написать запрос, который выбирает последний элемент из массива, который не делает равное поле Имя , Например, я хочу, чтобы запрос возвращать следующие результаты:

ID Name Last 
1 Joe Mary 
2 Mary Sarah 
3 Bill Joe 
4 James (NULL) 

Я получаю близко - я могу выбрать последний элемент с помощью следующего запроса:

SELECT ID, Name, 
(Data::json->(json_array_length(Data::json)-1))::text AS Last 
FROM table; 

ID Name Last 
1  Joe  Joe 
2  Mary Mary 
3  Bill Joe 
4  James James 

Однако, мне нужен еще один уровень - оценить последний элемент и, если он совпадает с полем имени, попробовать следующее последнее поле и т. д.

Любые помощь или указатели были бы очень признательны!

+1

Важная информация: ваша версия Postgres? Тип данных 'json' или' jsonb'? –

+0

PostgreSQL версия? –

ответ

2

json в Postgres 9.3

Это трудно в пг 9.3, так как полезная функциональность отсутствует.

Метод 1

Unnest в LEFT JOIN LATERAL (чистый и соответствующий стандартам), отделка двойные кавычки из json после отливки к text. См. Ссылки ниже.

SELECT DISTINCT ON (1) 
     t.id, t.name, d.last 
FROM tbl t 
LEFT JOIN LATERAL (
    SELECT ('[' || d::text || ']')::json->>0 AS last 
    FROM json_array_elements(t.data) d 
) d ON d.last <> t.name 
ORDER BY 1, row_number() OVER() DESC; 

В то время как это работает, и я никогда не видел, что это не получится, порядок безгнездных элементов зависит от незарегистрированного поведения. См. Ссылки ниже!
Улучшено преобразование с json в text с выражением provided by @pozs in the comment. Все еще хакерский, но должен быть в безопасности.

Метод 2

SELECT DISTINCT ON (1) 
     id, name, NULLIF(last, name) AS last 
FROM (
    SELECT t.id, t.name 
     ,('[' || json_array_elements(t.data)::text || ']')::json->>0 AS last 
     , row_number() OVER() AS rn 
    FROM tbl t 
    ) sub 
ORDER BY 1, (last = name), rn DESC; 
  • Unnest в списке SELECT (нестандартные).
  • Приложить номер строки (rn) параллельно (более надежно).
  • Преобразование в text, как указано выше.
  • Выражение (last = name) в предложении ORDER BY сортирует соответствующие имена последних (но до NULL). Поэтому сопоставимое имя выбирается только в том случае, если другое имя не доступно. Последняя ссылка ниже. В списке SELECTNULLIF заменяет соответствующее имя на NULL, исходя из того же результата, что и выше.

SQL Fiddle.

json или jsonb в Postgres 9.4

пг 9,4 отгружается все необходимые усовершенствования:

SELECT DISTINCT ON (1) 
     t.id, t.name, d.last 
FROM tbl t 
LEFT JOIN LATERAL json_array_elements_text(data) WITH ORDINALITY d(last, rn) 
     ON d.last <> t.name 
ORDER BY d.rn DESC; 

Использование jsonb_array_elements_text() для jsonb. Все остальное то же самое.

json/jsonb functions in the manual

Похожие ответы с более подробного объяснения:

+1

Отличный ответ, но обрезка строки 'json' может завершиться неудачно, если эта строка содержит некоторое экранирование. Существует (более известный как hack-ish, я знаю, но) более надежный способ сделать «бросок» в 9.3: '('[' || d :: text || ']') :: json - >> 0' http://sqlfiddle.com/#!15/99d11/1 – pozs

+0

Спасибо. Это также то, что [Марти Раудсепп предложил в обсуждении plpgsql-general] (http://www.postgresql.org/message-id/flat/[email protected]om). Это лучше, чем предыдущие варианты, поэтому я обновил ваше выражение. –

+0

Спасибо за это - я испытаю сегодня днем. Выглядит многообещающе! –

Смежные вопросы