Postgres 9.4 или по-нова версия
Използвайте WITH ORDINALITY
за функции за връщане на набори:
Когато функция в
FROM
клаузата е суфиксирана отWITH ORDINALITY
, abigint
колона се добавя към изхода, който започва от 1 и се увеличава с 1 за всеки ред от изхода на функцията. Това е най-полезно в случай на функции за връщане на набор, катоunnest()
.
В комбинация с LATERAL
функция в pg 9.3+ и според тази тема за pgsql-hackers, горната заявка вече може да бъде написана като:
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 $1[i], i - array_lower($1,1) + 1
FROM generate_series(array_lower($1,1), array_upper($1,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 $1[i], i - array_lower($1,1) + 1, i
FROM generate_series(array_lower($1,1), array_upper($1,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
Сравнете:
- Нормализирайте индексите на масива за едномерен масив, така че да започват с 1