ネストされていない配列の要素の元の順序を保持する方法は?


17

与えられた文字列:

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

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

これまでのところ:

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;

これは、私が望んでいたことの基本を達成しますが、元の単語の順序を保持しません。

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


1つの文字列または文字列のテーブル全体を処理しますか?ある場合、テーブルには主キーがありますか?
アーウィンブランドステッター

@ErwinBrandstetterテーブル内の1つの文字列(主キーを持つ)
-swasheck

回答:


22

WITH ORDINALITY Postgres 9.4以降

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

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  (
   SELECT regexp_split_to_table('I think postgres is nifty', ' ') AS word
   ) x;

regexp_split_to_table()長い文字列ではパフォーマンスが低下します。 unnest(string_to_array(...))より良いスケール:

SELECT *, row_number() OVER () AS rn
FROM  (
   SELECT unnest(string_to_array('I think postgres is nifty', ' ')) AS word
   ) x;

ただし、これは通常は機能し、単純なクエリで破損することは一度もありませんが、PostgreSQLは明示的なORDER BY

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

SELECT arr[rn] AS word, rn
FROM   (
   SELECT *, generate_subscripts(arr, 1) AS rn
   FROM  (
      SELECT string_to_array('I think postgres is nifty', ' ') AS arr
      ) x
   ) y;

文字列の表の場合

追加PARTITION BY idOVER句...

デモ表:

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;

1
Pgの風変わりなSRF-in-SELECTリストの動作も利用できますSELECT generate_series(1,array_length(word_array,1)), unnest(word_array) FROM ....。9.3はLATERAL、この問題に対するより良い解決策を提供するかもしれません。
クレイグリンガー

2
generate_subscripts(arr, 1)代わりに動作しませんgenerate_series(1, array_upper(arr, 1))か?明確にするために前者の方が好きです。
-dezso

@dezso:良い点。クエリをさらに簡素化します。それに応じて答えを修正しました。
アーウィンブランドステッター

1
@Erwin depeszのWITH WITH ORDINALITYの投稿を見ましたか?
ジャックダグラス

1
@JackDouglas:たまたま、金曜日関連するトピックについて議論しました。答えに少し追加しました。
アーウィンブランドステッター
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.