PostgreSQL unnest () с номером элемента
когда у меня есть столбец с запятыми, я могу использовать unnest()
функция:
myTable
id | elements
---+------------
1 |ab,cd,efg,hi
2 |jk,lm,no,pq
3 |rstuv,wxyz
select id, unnest(string_to_array(elements, ',')) AS elem
from myTable
id | elem
---+-----
1 | ab
1 | cd
1 | efg
1 | hi
2 | jk
...
как я могу включить номера элемента? То есть:
id | elem | nr
---+------+---
1 | ab | 1
1 | cd | 2
1 | efg | 3
1 | hi | 4
2 | jk | 1
...
Я хочу исходное положение каждого элемента в исходной строке. Я пробовал с оконными функциями (row_number()
,rank()
etc.) но я всегда получаю 1
. Может, потому, что они находятся в одной строке исходной таблицы?
Я знаю, что это плохой дизайн таблицы. Это не мое, я просто пытаюсь исправить это.
5 ответов:
Postgres 9.4 или более поздней версии
использовать
WITH ORDINALITY
для возвращения функции:когда функция
FROM
предложение имеет суффиксWITH ORDINALITY
, abigint
столбец добавляется к выходу, который начинается с 1 и увеличивается на 1 для каждой строки вывода функции. Это самое полезно в случае набора возвращающих функций, таких какUNNEST()
.в сочетании с
LATERAL
характеристика на стр. 9.3+, и согласно этому поток на pgsql-хакеры, приведенный выше запрос теперь может быть записан как:SELECT t.id, a.elem, a.nr FROM tbl AS t LEFT JOIN LATERAL unnest(string_to_array(t.elements, ',')) WITH ORDINALITY AS a(elem, nr) ON TRUE;
LEFT JOIN ... ON TRUE
сохраняет все строки в левой таблице, даже если табличное выражение справа не возвращает ни одной строки. Если это не имеет никакого значения, вы можете использовать этот эквивалент в противном случае,менее многословен форма с неявнойCROSS JOIN LATERAL
:SELECT t.id, a.elem, a.nr FROM tbl t, unnest(string_to_array(t.elements, ',')) WITH ORDINALITY a(elem, nr);
или проще, если на основе массива (
arr
будучи столбца массива):SELECT t.id, a.elem, a.nr FROM tbl t, unnest(t.arr) WITH ORDINALITY a(elem, nr);
или даже с минимальным синтаксис:
SELECT id, a, ordinality FROM tbl, unnest(arr) WITH ORDINALITY a;
a
автоматически в таблице и псевдоним столбца. Имя добавленного столбца ординарности по умолчанию -ordinality
. Но лучше (безопаснее, чище) добавлять явные псевдонимы столбцов и столбцы с табличной квалификацией.Postgres 8.4-9.3
С
row_number() OVER (PARTITION BY id ORDER BY elem)
вы получаете номера в соответствии с порядком сортировки, а не порядковый номер оригинальный порядковый номер в строке.вы можете просто опустить
ORDER BY
:SELECT *, row_number() OVER (PARTITION by id) AS nr FROM (SELECT id, regexp_split_to_table(elements, ',') AS elem FROM tbl) t;
хотя это обычно работает, и я никогда не видел, чтобы он ломался в простых запросах, PostgreSQL ничего не утверждает о порядке строк без
ORDER BY
. Это происходит из-за детали реализации.до гарантия порядковых номеров элементов разделенных пробелами строка:
SELECT id, arr[nr] AS elem, nr FROM ( SELECT *, generate_subscripts(arr, 1) AS nr FROM (SELECT id, string_to_array(elements, ' ') AS arr FROM tbl) t ) sub;
или проще, если на основе массива:
SELECT id, arr[nr] AS elem, nr FROM (SELECT *, generate_subscripts(arr, 1) AS nr FROM tbl) t;
связанный ответ на dba.SE:
Postgres 8.1-8.4
ни одна из этих функций не доступна, но:
RETURNS TABLE
,generate_subscripts()
,unnest()
,array_length()
.
Но это работает:CREATE FUNCTION f_unnest_ord(anyarray, OUT val anyelement, OUT ordinality integer) RETURNS SETOF record LANGUAGE sql IMMUTABLE AS 'SELECT [i], i - array_lower(,1) + 1 FROM generate_series(array_lower(,1), array_upper(,1)) i';
обратите внимание, в частности, что индекс массива может отличаться от порядковых положений элементов. Рассмотрим это демо с расширенной функцией:
CREATE FUNCTION f_unnest_ord_idx(anyarray, OUT val anyelement, OUT ordinality int, OUT idx int) RETURNS SETOF record LANGUAGE sql IMMUTABLE AS 'SELECT [i], i - array_lower(,1) + 1, i FROM generate_series(array_lower(,1), array_upper(,1)) i'; SELECT id, arr, (rec).* FROM ( SELECT *, f_unnest_ord_idx(arr) AS rec FROM (VALUES (1, '{a,b,c}'::text[]) -- short for: '[1:3]={a,b,c}' , (2, '[5:7]={a,b,c}') , (3, '[-9:-7]={a,b,c}') ) t(id, arr) ) sub; id | arr | val | ordinality | idx ----+-----------------+-----+------------+----- 1 | {a,b,c} | a | 1 | 1 1 | {a,b,c} | b | 2 | 2 1 | {a,b,c} | c | 3 | 3 2 | [5:7]={a,b,c} | a | 1 | 5 2 | [5:7]={a,b,c} | b | 2 | 6 2 | [5:7]={a,b,c} | c | 3 | 7 3 | [-9:-7]={a,b,c} | a | 1 | -9 3 | [-9:-7]={a,b,c} | b | 2 | -8 3 | [-9:-7]={a,b,c} | c | 3 | -7
сравниваем:
попробуй:
select v.*, row_number() over (partition by id order by elem) rn from (select id, unnest(string_to_array(elements, ',')) AS elem from myTable) v
использовать создание индекса Functions.
http://www.postgresql.org/docs/current/static/functions-srf.html#FUNCTIONS-SRF-SUBSCRIPTSнапример:
SELECT id , elements[i] AS elem , i AS nr FROM ( SELECT id , elements , generate_subscripts(elements, 1) AS i FROM ( SELECT id , string_to_array(elements, ',') AS elements FROM myTable ) AS foo ) bar ;
просто:
SELECT id , unnest(elements) AS elem , generate_subscripts(elements, 1) AS nr FROM ( SELECT id , string_to_array(elements, ',') AS elements FROM myTable ) AS foo ;
Если порядок элементов не важен, вы можете
select id, elem, row_number() over (partition by id) as nr from ( select id, unnest(string_to_array(elements, ',')) AS elem from myTable ) a
unnest2()
упражненияболее старые версии перед pg v8. 4 нуждаются в определяемом пользователем
unnest()
. Мы можем адаптировать эту старую функцию для возврата элементов с индексом:CREATE FUNCTION unnest2(anyarray) RETURNS TABLE(v anyelement, i integer) AS $BODY$ SELECT [i], i FROM generate_series(array_lower(,1), array_upper(,1)) i; $BODY$ LANGUAGE sql IMMUTABLE;