web-dev-qa-db-ja.com

ネストされていない配列の要素の元の順序を保持するにはどうすればよいですか?

文字列が与えられます:

「PostgreSQLは気の利いたものだと思います」

その文字列内で見つかった個々の単語を操作したいと思います。基本的に、Wordの詳細を取得できる別のものがあり、その文字列のネストされていない配列をこの辞書に結合したいと考えています。

これまでのところ:

select Word, meaning, partofspeech
from unnest(string_to_array('I think that PostgreSQL is nifty',' ')) as Word
from table t
join dictionary d
on t.Word = d.wordname;

これにより、私が望んでいたことの基本が実現しますが、元のWordの順序は保持されません。

関連する質問:
要素番号を持つPostgreSQL unnest()

19
swasheck

Postgres 9.4以降の_WITH ORDINALITY_

新しい機能は、このクラスの問題を単純化します。上記のクエリは次のようになります。

_SELECT *
FROM   regexp_split_to_table('I think Postgres is nifty', ' ') WITH ORDINALITY x(Word, rn);_

または、テーブルに適用されます:

_SELECT *
FROM   tbl t, regexp_split_to_table(t.my_column, ' ') WITH ORDINALITY x(Word, rn);_

詳細:

暗黙のLATERAL結合について:

Postgres 9.3以前-より一般的な説明

単一の文字列の場合

ウィンドウ関数 row_number() を適用して、要素の順序を記憶できます。ただし、通常のrow_number() OVER (ORDER BY col)では、文字列の元の位置ではなく、ソート順に従って数値を取得します。

_ORDER BY_を省略して、「現状のまま」の位置を取得できます。

_SELECT *, row_number() OVER () AS rn
FROM   regexp_split_to_table('I think Postgres is nifty', ' ') AS x(Word);_

長い文字列を使用すると、regexp_split_to_table()のパフォーマンスが低下します。 unnest(string_to_array(...))より適切にスケーリングされます:

_SELECT *, row_number() OVER () AS rn
FROM   unnest(string_to_array('I think Postgres is nifty', ' ')) AS x(Word);_

ただし、これは正常に機能し、単純なクエリで壊れることはありませんでしたが、Postgresは明示的な_ORDER BY_がない場合、行の順序について何もアサートしません。

guarantee元の文字列の要素の序数には、 generate_subscript() を使用します(@deszoによるコメントで改善):

_SELECT arr[rn] AS Word, rn
FROM   (
   SELECT *, generate_subscripts(arr, 1) AS rn
   FROM   string_to_array('I think Postgres is nifty', ' ') AS x(arr)
   ) y;_

文字列のテーブルの場合

_PARTITION BY id_をOVER句に追加...

デモ表:

_CREATE TEMP TABLE strings(string text);
INSERT INTO strings VALUES
  ('I think Postgres is nifty')
 ,('And it keeps getting better');
_

主キーのアドホック代替としてctidを使用しています。 1つ(またはの一意の列)がある場合は、代わりにそれを使用します。

_SELECT *, row_number() OVER (PARTITION BY ctid) AS rn
FROM  (
   SELECT ctid, unnest(string_to_array(string, ' ')) AS Word
   FROM   strings
   ) x;
_

これは明確なIDなしで機能します:

_SELECT arr[rn] AS Word, rn
FROM  (
   SELECT *, generate_subscripts(arr, 1) AS rn
   FROM  (
      SELECT string_to_array(string, ' ') AS arr
      FROM   strings
      ) x
   ) y;
_

SQLフィドル。

質問への回答

_SELECT z.arr, z.rn, z.Word, d.meaning   -- , partofspeech -- ?
FROM  (
   SELECT *, arr[rn] AS Word
   FROM  (
      SELECT *, generate_subscripts(arr, 1) AS rn
      FROM  (
         SELECT string_to_array(string, ' ') AS arr
         FROM   strings
         ) x
      ) y
   ) z
JOIN   dictionary d ON d.wordname = z.Word
ORDER  BY z.arr, z.rn;
_
24