最後に特殊文字を含むOracleソートvarchar2列


8

OracleでVarchar2またはNVarchar2列を独自のカスタム定義の順序に並べ替えるにはどうすればよいですか。または、最初に文字、次に数字、次にすべての特殊文字を配置する既存のオプションがあります。

最初のアプローチは、文字を数字に手動でマッピングする関数を使用することでした。

select id, sorted_column
from some_table
order FN_SPECIAL_SORT_KEY(sorted_column,'asc')

特殊なソート関数は、各文字を2桁の数値にマップし、戻り値はソートに使用されます。これは本当に高額な連結であるように見え、間違っているように感じます。

        for i in 1..length(sorted_text)
        loop
            v_result:=v_result ||  case substr(sorted_text,i,1)
                WHEN ' '   THEN 82 WHEN  '!'   THEN 81 WHEN '"'    THEN 80 WHEN  '#'   THEN 79 WHEN  '$'
                ..............
                WHEN 'u'   THEN 15 WHEN  'U'   THEN 15 WHEN  'v'   THEN 14 WHEN  'V'   THEN 14 WHEN  'w'   THEN 13 WHEN  'W'   THEN 13 WHEN  'x'
                ....
                else 90 end;
        end loop;

別の方法を考え出すのに苦労しています。このアプローチにはどのような問題があるのか​​知りたい。たぶん、私たちには他に選択肢はありません。

補遺1:

ソートされたデータの例を追加します。一般に、すべての英字は大文字と小文字を区別せず、次に0〜9の数字、次に任意の順序の特殊文字。

以下は、昇順リストのサンプルです。特殊文字は交換可能であり、すべて文字と数字の後に置く必要があることに注意してください。バイナリソートでは、一部の特殊文字が文字の前にあります(つまり、 ')

私の希望する注文、

AB1 $
aCC#
ac '
BZ

Oracleバイナリ順序

AB1 $
BZ
ac '
acc#

回答:


5

指定するソート順がOracleですでにサポートされている場合は、NLSSORT関数で順序付けすることにより、次のように行うことができます。

ORDER BY NLSSORT(sorted_column, 'NLS_SORT = XDanish') -- Replace XDanish as appropriate

サポートされている並べ替え順のリストは、こちらで確認できます。


これらはケースと発音区別符号を扱っているので、このケースで機能するものは本当にあるのでしょうか?
Leigh Riffel、2011

5

いくつかのオプション:

  1. ソートされたバージョンのデータをトリガーを介してテーブルに永続化し、それを使用します。

  2. Oracle Locale Builderを使用して、カスタムの並べ替え順序を作成します。(注意:私はこれを使用したことがないので、そこにどんな問題が存在するかわかりません。)次に、そのカスタムソート順序でNLSSORT関数を使用できます。


4

別のアプローチは、関数ベースのインデックスをに追加することFN_SPECIAL_SORT_KEY(sorted_column,'asc')です。追加の列とトリガーの必要性を回避し、クエリを変更する必要はありません。


4

あなたの説明に基づいて、TRANSLATEがあなたのために仕事をすることができるようです。Jeffrey Kempが示唆するように、関数ベースのインデックスを作成できます。

セットアップ:

drop table t1;

create table t1 as (
   select 'AB$$' c1 from dual
   union all select 'AB1$' from dual
   union all select 'ABz$' from dual
   union all select 'BZ'   from dual
   union all select 'ac''' from dual
   union all select 'acc#' from dual
   union all select 'aCC#' from dual
);

デモンストレーション:

select * from t1 order by c1;

SELECT c1 FROM t1 
ORDER BY translate(c1
  ,'abcdefghijklmnopqrstuvwxyz0123456789`-=[]\;'',./~!@#$%^&*()_+{}|:"<>?'
  ,'ABCDEFGHIJKLMNOPQRSTUVWXYZ' || rpad(chr(123),10,chr(123)) 
     || rpad(chr(124),31,chr(124)));

出力:

C1 
----
AB$$ 
AB1$ 
ABz$ 
BZ   
aCC# 
ac'                                       '(For Syntax Highlighter)
acc#   
 7 rows selected 

C1 
----
ABz$ 
AB1$ 
AB$$ 
aCC# 
acc# 
ac'  
BZ       
 7 rows selected 

すべての文字の順序を確認します。

SELECT 32+level Value, CHR(32 + level), ascii(CHR(32 + level)) CV FROM dual 
CONNECT BY level <= 255-32 
ORDER BY TRANSLATE(CHR(32 + level)
   , 'abcdefghijklmnopqrstuvwxyz0123456789`-=[]\;'',./~!@#$%^&*()_+{}|:"<>?'
   , 'ABCDEFGHIJKLMNOPQRSTUVWXYZ' || rpad(chr(123),10,chr(123)) 
       || rpad(chr(124),31,chr(124)));

ジャックダグラスが指摘したように、これらの結果の順序は、翻訳された特殊文字に関しては予測できません。したがって、この答えはOPの質問を解決しますが、一貫したシンボルの順序が必要な場合は役に立たない可能性があります。
Leigh Riffel、2011

3
with w as ( select 'AB1$' as foo from dual
  union all select 'aCC#' from dual
  union all select 'ac' from dual
  union all select 'BZ' from dual
  union all select '1' from dual
  union all select 'a' from dual
  union all select '!' from dual )
select foo
from w
order by regexp_replace(lower(foo), '[^a-z]', '~'), regexp_replace(foo, '[^0-9]', '~'), foo;
/*
FOO  
---- 
a    
AB1$ 
ac   
aCC# 
BZ   
1    
!    
*/

を使用したクエリでの並べ替えを回避するためにデータにインデックスを付ける場合は、次のようにしますorder by

create table bar(foo varchar(100) not null, 
                 foo_o1 as (substr(regexp_replace(lower(foo), '[^a-z]', '~'),1,100)), 
                 foo_o2 as (substr(regexp_replace(foo, '[^0-9]', '~'),1,100)));
create index bar_i on bar (foo_o1, foo_o2, foo);
insert into bar(foo)
select 'AB1$' as foo from dual
union all select 'aCC#' from dual
union all select 'ac' from dual
union all select 'BZ' from dual
union all select '1' from dual
union all select 'a' from dual
union all select '!' from dual;
commit;

explain plan for select foo_o1 from bar order by foo_o1, foo_o2, foo;
select * from table(dbms_xplan.display);
/*
--------------------------------------------------------------------------
| Id  | Operation        | Name  | Rows  | Bytes | Cost (%CPU)| Time     |
--------------------------------------------------------------------------
|   0 | SELECT STATEMENT |       |     7 |  1092 |     1   (0)| 00:00:01 |
|   1 |  INDEX FULL SCAN | BAR_I |     7 |  1092 |     1   (0)| 00:00:01 |
--------------------------------------------------------------------------
*/

-編集

@Leighがコメントしたように、代替の、より簡潔なアプローチは、(変更された)正規表現を連結する単一の関数を持つことです: regexp_replace(lower(foo), '[^a-z]', '~')||regexp_replace(foo, '[^a-zA-Z0-9]', '~')||foo

||fooどちらの場合も最後にを含めると、順序が確定的(反復可能)になります。これは、質問で具体的に求められていない場合でも、良いことです。


1
このソリューションを(単一の関数)関数ベースのインデックスで使用できるようにする方法は、順序を連結することです。これは私たちに与えregexp_replace(lower(foo), '[^a-z]', '~') || regexp_replace(foo, '[^0-9]', '~') || fooます。問題は、これが元のソリューションとは異なる方法でソートされることです。したがって、この修正が必要なのはオリジナルではなく、変更されたバージョンです。並べ替え順序は、2番目の正規表現を変更することで修正できますregexp_replace(lower(foo), '[^a-z]', '~') || regexp_replace(foo, '[^0-9a-zA-Z]', '~') || foo
Leigh Riffel、2011
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.