Postgres 9.4 lub nowszy
Używaj WITH ORDINALITY
do funkcji zwracających zestaw:
Gdy funkcja w FROM
klauzuli ma przyrostek, do wyniku dodawana WITH ORDINALITY
jest
bigint
kolumna, która zaczyna się od 1 i zwiększa o 1 dla każdego wiersza wyniku funkcji. Jest to najbardziej przydatne w przypadku ustawionych funkcji zwracających, takich jak unnest()
.
W połączeniu z LATERAL
funkcją w pg 9.3+ i zgodnie z tym wątkiem na pgsql- hackers , powyższe zapytanie można teraz zapisać jako:
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
zachowuje wszystkie wiersze w lewej tabeli, nawet jeśli wyrażenie tabeli po prawej stronie nie zwraca żadnych wierszy. Jeśli to nie ma znaczenia, możesz użyć tej równoważnej, mniej szczegółowej formy z niejawnym CROSS JOIN LATERAL
:
SELECT t.id, a.elem, a.nr
FROM tbl t, unnest(string_to_array(t.elements, ',')) WITH ORDINALITY a(elem, nr);
Lub prościej, jeśli opiera się na rzeczywistej tablicy ( arr
będącej kolumną tablicy):
SELECT t.id, a.elem, a.nr
FROM tbl t, unnest(t.arr) WITH ORDINALITY a(elem, nr);
Lub nawet z minimalną składnią:
SELECT id, a, ordinality
FROM tbl, unnest(arr) WITH ORDINALITY a;
a
jest automatycznie aliasem tabeli i kolumny. Domyślna nazwa dodanej kolumny porządkowej toordinality
. Ale lepiej (bezpieczniej, czyściej) dodać jawne aliasy kolumn i kolumny kwalifikujące do tabeli.
Postgres 8.4 - 9.3
Dzięki temu row_number() OVER (PARTITION BY id ORDER BY elem)
otrzymujesz liczby zgodnie z porządkiem sortowania, a nie liczbą porządkową pierwotnej pozycji porządkowej w ciągu.
Możesz po prostu pominąć ORDER BY
:
SELECT *, row_number() OVER (PARTITION by id) AS nr
FROM (SELECT id, regexp_split_to_table(elements, ',') AS elem FROM tbl) t;
Chociaż normalnie to działa i nigdy nie widziałem, aby zawodziło w prostych zapytaniach, PostgreSQL nie zapewnia nic dotyczącego kolejności wierszy bez ORDER BY
. Zdarza się, że działa z powodu szczegółów implementacji.
Aby zagwarantować numerów porządkowych elementów w oddzielonych pustym ciągiem :
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;
Lub prostsze, jeśli oparte na rzeczywistej tablicy :
SELECT id, arr[nr] AS elem, nr
FROM (SELECT *, generate_subscripts(arr, 1) AS nr FROM tbl) t;
Powiązana odpowiedź na dba.SE:
Postgres 8.1 - 8.4
Żadna z tych funkcji są dostępne jeszcze: RETURNS TABLE
, generate_subscripts()
, unnest()
, array_length()
. Ale to działa:
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';
Zwróć uwagę w szczególności, że indeks tablicy może różnić się od porządkowych pozycji elementów. Rozważ to demo z rozszerzoną funkcją :
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[])
, (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
Porównać:
WITH ORDINALITY
jest preferowanygenerate_subscripts()
? Wydaje mi się, żegenerate_subscripts()
jest lepszy, ponieważ pokazuje rzeczywistą lokalizację elementu w tablicy. Jest to przydatne, na przykład, podczas aktualizowania tablicy ... czyWITH ORDINALITY
zamiast tego powinienem używać ?WITH ORDINALITY
jest to ogólne rozwiązanie umożliwiające uzyskanie numerów wierszy dla dowolnej funkcji zwracającej zestaw w zapytaniu SQL. Jest to najszybszy, niezawodny sposób i zdarza się, doskonale działa na 1-dimenstional, tablic opartych 1 (domyślnie dla tablic PostgreSQL, uznają to ). Jeśli pracujesz z jakimkolwiek innym rodzajem tablic (większość ludzi tego nie robi) i faktycznie musisz zachować / pracować z oryginalnymi indeksami dolnymi, togenerate_subscripts()
jest najlepsza droga. Ale wszystkounnest()
spłaszcza się na początku ...Table functions appearing in FROM can also be preceded by the key word LATERAL, but for functions the key word is optional; the function's arguments can contain references to columns provided by preceding FROM items in any case.
Próbować:
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
źródło
Użyj funkcji generujących indeksy dolne .
http://www.postgresql.org/docs/current/static/functions-srf.html#FUNCTIONS-SRF-SUBSCRIPTS
Na przykład:
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 ;
Prościej:
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 ;
źródło
Jeśli kolejność elementów nie jest ważna, możesz
select id, elem, row_number() over (partition by id) as nr from ( select id, unnest(string_to_array(elements, ',')) AS elem from myTable ) a
źródło
unnest2()
jako ćwiczenieStarsze wersje przed wersją pg v8.4 wymagają zdefiniowania przez użytkownika
unnest()
. Możemy dostosować tę starą funkcję do zwracania elementów z indeksem:CREATE FUNCTION unnest2(anyarray) RETURNS setof record AS $BODY$ SELECT $1[i], i FROM generate_series(array_lower($1,1), array_upper($1,1)) i; $BODY$ LANGUAGE sql IMMUTABLE;
źródło
RETURNS TABLE
ma. Do mojej odpowiedzi dodałem rozdział omawiający rozwiązanie.setof record
.