値が分離された列がある場合、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()
など)を試しましたが、常に_1
_を取得します。おそらく、ソーステーブルの同じ行にあるからでしょうか?
私はそれが悪いテーブルデザインであることを知っています。それは私のものではありません、私はそれを修正しようとしています。
集合を返す関数には _WITH ORDINALITY
_ を使用します。
FROM
句の関数の末尾に_WITH ORDINALITY
_が付いている場合、bigint
列が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
_を持つless verbose形式を使用できます。
_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
は自動的にテーブルになりますand列のエイリアス。追加された順序列のデフォルト名はordinality
です。ただし、明示的な列エイリアスとテーブル修飾列を追加する方が安全です。
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に関連する回答:
これらの機能はまだ利用できません: _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
_
比較する:
試してください:
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
添え字生成関数を使用します。
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 $1[i], i
FROM generate_series(array_lower($1,1),
array_upper($1,1)) i;
$BODY$ LANGUAGE sql IMMUTABLE;