3

PostgreSQL 9.0.1 で少し複雑な文字列変換をしようとしています。の値はmy_col、次の形式の長い文字列です。

'12345_sometext_X12B_1'
'12345_sometext_optionaltext_Y09B_1'
'12345_sometext_optionaltext_X12A_1'

「X12」部分を既知の数値に変換する必要があります。いくつかの異なる既知の値 (最大 5) があります。

サブクエリを必要とせずに、1 つのクエリ内でこれを判断できると期待しています。ただし、以下は私にとってはうまくいきません。最後の列は、例外をスローする列です。CASE何らかの理由で、これらの関数の出力を組み合わせてステートメントを実行できないようです。これまでのコラムは、デモンストレーションのみを目的として含まれています。

select
          regexp_matches(my_col, E'^.*_([^_]*)[A-Z]{1}_\\d*$'), -- returns {'X12'}
         (regexp_matches(my_col, E'^.*_([^_]*)[A-Z]{1}_\\d*$'))[1], -- returns 'X12'
    case (regexp_matches(my_col, E'^.*_([^_]*)[A-Z]{1}_\\d*$'))[1]
        when 'X12' then '1200'
        when 'Y09' then '950'
        else '?' end -- should return '1200' but throws error
from my_table;

代わりに、次のエラーが表示されます。

ERROR: set-valued function called in context that cannot accept a set
SQL state: 0A000

誰かが私にアドバイスできますか?

4

2 に答える 2

8

与えられたデータ:

create table my_table(my_col text);
insert into my_table(my_col) values
('12345_sometext_X12B_1'),
('12345_sometext_optionaltext_Y09B_1'),
('12345_sometext_optionaltext_X12A_1'),
('nomatch');

上記のクエリは、報告するエラーを生成します。非常に奇妙な、以来:

SELECT pg_typeof((regexp_matches(my_col, E'^.*_([^_]*)[A-Z]{1}_\\d*$'))[1]);

'text'を返します。それは本当に言うべきsetof textです、そしてそれは罠です:regex_matchesは集合を返す関数です。これらは、PostgreSQLのFROM句の外部で呼び出されたときに...興味深い...動作をします。

パターンマッチングから:

regexp_matches関数は、POSIX正規表現パターンとの一致の結果としてキャプチャされたすべてのサブストリングのテキスト配列を返します。構文はregexp_matches(string、pattern [、flags])です。この関数は、行なし、1行、または複数行を返すことができます

サブクエリを使用してSRFを呼び出すように、クエリを再定式化してみてください。ただし、マッチャーから複数の行が返された場合、これは失敗します。

SELECT 
  CASE (SELECT x[1] FROM regexp_matches(my_col, E'^.*_([^_]*)[A-Z]{1}_\\d*$') x)
    WHEN 'X12' THEN '1200'
    WHEN 'Y09' THEN '950'
    ELSE '?'
  END
FROM my_table;

SELECTのSRFがPgでどのように奇妙であるかを見たいですか?これらのクエリの結果を比較します。

SELECT generate_series(1,10), generate_series(1,15);

と:

SELECT generate_series(1,10), generate_series(1,20);

1つ目は30行を生成します。2番目は20を生成します。理由を説明して楽しんでください。PgのSELECTリストに複数のSRFがあると、ときどき有用な結果が得られます。

PostgreSQL 9.3は、LATERALTom LaneのおかげでSQL標準句をサポートします。これは、現在の動作に代わる、適切で明確に定義された代替手段を提供します。

于 2012-10-09T09:07:27.110 に答える
2

regexp_matches()SETOF text[](テキスト配列のセット) を返します。これは、同じ文字列内の 1 つのパターンに対する複数の一致に役立ちます。しかし、それは単にこのタスクには不適切なツールです。

substring()代わりに正規表現を使用してください。返しますtext。@Craigの回答でデモテーブルを再利用する:

SELECT CASE substring(my_col, '^.*_([^_]*)[A-Z]_\d*$')
         WHEN 'X12' THEN '1200'
         WHEN 'Y09' THEN '950'
         ELSE            '?'
       END As result
FROM   my_table;

戻り値:

 result
--------
 1200
 950
 1200
 ?

また、正規表現を少し簡略化しました。{1}ただの騒音でした。

パフォーマンスを最適化する必要がある場合は、強力ですが比較的高価な正規表現を使用しないでください。何かのようなもの:

reverse(right(split_part(reverse(my_col), '_', 2), -1))

より複雑に見えますが、私のテストではまだ高速です。

于 2012-10-10T01:47:07.507 に答える