web-dev-qa-db-ja.com

文字列のクリーンアップ機能を高速化するにはどうすればよいですか?

文字列をクリーンアップして、特定のASCIIコード文字が文字列から除外され、他の文字が置き換えられるようにする必要があります。

私はPostgresを初めて使用します。私の関数ufn_cie_easy()のパフォーマンスが遅すぎます:

DECLARE
  letter char = '';
  str_result TEXT = '';
  x integer;
  y integer;
  asc_code int;
BEGIN
  y:=1;
  x:=char_length(arg);
  LOOP
    letter=substring(arg from y for 1);
    asc_code=ascii(letter);
    IF (asc_code BETWEEN 47 and 58) or (asc_code BETWEEN 65 and 90) or (
        asc_code BETWEEN 97 and 122) THEN
      str_result := str_result || letter;
      ELSIF (asc_code BETWEEN 192 and 197) THEN
      str_result := str_result || 'A';
      ELSIF (asc_code BETWEEN 200 and 203) THEN
      str_result := str_result || 'E';
      ELSIF (asc_code BETWEEN 204 and 207) THEN
      str_result := str_result || 'I';
      ELSIF (asc_code BETWEEN 210 and 214) OR (asc_code=216) THEN
      str_result := str_result || 'O';
      ELSIF (asc_code BETWEEN 217 and 220) THEN
      str_result := str_result || 'U';
      ELSIF (asc_code BETWEEN 224 and 229) THEN
      str_result := str_result || 'a';
      ELSIF (asc_code BETWEEN 232 and 235) THEN
      str_result := str_result || 'e';
      ELSIF (asc_code BETWEEN 236 and 239) THEN
      str_result := str_result || 'i';
      ELSIF (asc_code BETWEEN 242 and 246) OR (asc_code=248) THEN
      str_result := str_result || 'o';
      ELSIF (asc_code BETWEEN 249 and 252) THEN
      str_result := str_result || 'u';
      ELSE
      CASE asc_code
        WHEN 352 THEN str_result := str_result || 'S';
        WHEN 338 THEN str_result := str_result || 'OE';
        WHEN 381 THEN str_result := str_result || 'Z';
        WHEN 353 THEN str_result := str_result || 's';
        WHEN 339 THEN str_result := str_result || 'oe';
        WHEN 382 THEN str_result := str_result || 'z';
        WHEN 162 THEN str_result := str_result || 'c';
        WHEN 198 THEN str_result := str_result || 'AE';
        WHEN 199 THEN str_result := str_result || 'C';
        WHEN 208 THEN str_result := str_result || 'D';
        WHEN 209 THEN str_result := str_result || 'N';
        WHEN 223 THEN str_result := str_result || 'ss';
        WHEN 230 THEN str_result := str_result || 'ae';
        WHEN 231 THEN str_result := str_result || 'c';
        WHEN 241 THEN str_result := str_result || 'n';
        WHEN 376 THEN str_result := str_result || 'Y';
        WHEN 221 THEN str_result := str_result || 'Y';
        WHEN 253 THEN str_result := str_result || 'y';
        WHEN 255 THEN str_result := str_result || 'y';
        ELSE str_result := str_result;
      END CASE;
      END IF;    
    y:=y+1;
    exit when  y=x+1;
  END LOOP;
  return str_result;
END;
1
W. Smets

この関数は、割り当てが少ない(実際には、この更新バージョンではありません)で著しく高速になるはずです。

_CREATE FUNCTION ufn_cie_easy(text)
  RETURNS text AS
$func$
BEGIN
RETURN replace(replace(replace(replace(replace(replace(
         translate($1,'ŠŽšžŸÝÀÁÂÃÄÅÇÈÉÊËÌÍÎÏÑÒÓÔÕÖØÙÚÛÜàáâãäåçèéêëìíîïñòóôõöøùúûüýÿ!"#$%&()*+,-./:;<=>?@[\]^_`{|}~€‚ƒ„…†‡ˆ‰‹‘’“”•–—˜™›¢£¤¥¦§¨©ª«¬®¯°±²³´µ¶·¸¹º»¼½¾¿×Þð÷þÐ'
                     ,'SZszYYAAAAAACEEEEIIIINOOOOOOUUUUaaaaaaceeeeiiiinoooooouuuuyy')
         ,'Œ','OE')
         ,'Æ','AE')
         ,'œ','oe')
         ,'æ','ae')
         ,'ß','ss')
         ,'''','');
END
$func$  LANGUAGE plpgsql;
_

Plpgsqlでは割り当ては比較的高価です。

しかし、本当にすべてのアクセント(発音区別符号)を削除したいと思うでしょう。 Postgresは関数unaccent()に追加のモジュールを提供します unaccent

_CREATE OR REPLACE FUNCTION ufn_cie_easy(text)
  RETURNS text AS
$func$
SELECT translate(unaccent(
        replace(replace(replace(replace(replace(
          $1
         ,'Œ','OE')
         ,'Æ','AE')
         ,'œ','oe')
         ,'æ','ae')
         ,'ß','ss')
       ), '!"#$%&()*+,-./:;<=>?@[\]^_`{|}~€‚ƒ„…†‡ˆ‰‹‘’“”•–—˜™›¢£¤¥¦§¨©ª«¬®¯°±²³´µ¶·¸¹º»¼½¾¿×Þð÷þÐ''', '');
$func$  LANGUAGE sql IMMUTABLE;
_

実質的に高速ですが。そして、実際にすべての発音区別符号を削除したい場合は、あらゆる面で優れています。

unaccent()に加えて:

  • エスケープされた_'_(_''_)をtranslate()に直接含めます。
  • 単純なSQL関数にします。
  • 関数をIMMUTABLEにします。

unaccent()IMMUTABLE関数が必要な場合に考慮すべき、詳細な説明と関連する問題:

Postgres 9.5以前では、unaccent()が常にsingle文字に置き換わるため、「Œ」や「ß」などの合字を手動で拡張する必要があります。

_    SELECT unaccent('Œ Æ œ æ ß');

    unaccent
    ----------
    E A e a S
_

Postgres9.6で何をするか

アクセントなしへのこの更新

_contrib/unaccent_の標準_unaccent.rules_ファイルを拡張して、Unicodeで知られているすべての発音区別符号を処理し、合字を正しく拡張します(Thomas Munro、LéonardBenedetti)

大胆な強調鉱山。上記のリンクされた回答で指示されているようにunaccent()を使用する:

_CREATE OR REPLACE FUNCTION ufn_cie_easy(text)
  RETURNS text AS
$func$
SELECT trim(regexp_replace(regexp_replace(public.unaccent('public.unaccent', $1)
                                        , '[^a-zA-Z\d\s]', '', 'g')
                         , '\s+', ' ', 'g'));
$func$  LANGUAGE sql IMMUTABLE;
_

ほぼ、しかし完全にあなたが持っていたものと完全に同一ではありません。 より良い私見:

  1. すべてのアクセントを置き換え、すべての合字をunaccent()で展開します。
  2. すべてのノイズ文字を削除します(ASCII文字、数字、空白を除くすべて)。
  3. 空白を単一のスペースに折ります(_' '_)。
  4. 先頭と末尾のスペースをトリミングします。
2

私の理解では、translate()を使用して目的を達成できます。

小さなデモンストレーション:

_SELECT translate('Some ţext with ひ inţereşt平ing chăracters', 'ţşăひ平', 'tsa');
               translate                
────────────────────────────────────────
 Some text with  interesting characters
_

したがって、最初に、置換する文字を、関数の3番目のパラメーターに(指定された順序で!)一致するように配置します。次に、削除する必要のあるすべての文字をリストします。置換リストには一致しません。

複数文字の置換が必要な文字の場合も、同様の方法でreplace()を使用できますが、文字ごとに1回の呼び出しが必要です。

_SELECT replace(replace('Æ small blæblæ', 'Æ', 'AE'), 'æ', 'ae');
      replace      
───────────────────
 AE small blaeblae
_
1
dezso

参考までに、関数の改良版(受信:text_to_cleanテキスト)。見栄えは良くありませんが、機能します。

DECLARE
result text;
BEGIN
result=translate(text_to_clean,'ŠŽšžŸÝÀÁÂÃÄÅÇÈÉÊËÌÍÎÏÑÒÓÔÕÖØÙÚÛÜàáâãäåçèéêëìíîïñòóôõöøùúûüýÿ!"#$%&()*+,-./:;<=>?@[\]^_`{|}~€‚ƒ„…†‡ˆ‰‹‘’“”•–—˜™›¢£¤¥¦§¨©ª«¬®¯°±²³´µ¶·¸¹º»¼½¾¿×Þð÷þÐ','SZszYYAAAAAACEEEEIIIINOOOOOOUUUUaaaaaaceeeeiiiinoooooouuuuyy');
result=replace(result,'Œ','OE');
result=replace(result,'Æ','AE');
result=replace(result,'œ','oe');
result=replace(result,'æ','ae');
result=replace(result,'ß','ss');
result=replace(result,'''','');
return result;
END;

助けてくれてありがとう。

0
W. Smets