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#
交流'
BZ
Oracleバイナリ順序
AB1 $
BZ
交流'
acc#
指定するソート順がOracleですでにサポートされている場合は、NLSSORT関数で並べ替えることでこれを行うことができます。
ORDER BY NLSSORT(sorted_column, 'NLS_SORT = XDanish') -- Replace XDanish as appropriate
サポートされているソート順のリスト ここ を見つけることができます。
いくつかのオプション:
ソートされたバージョンのデータをトリガーを介してテーブルに永続化し、それを使用します。
Oracle Locale Builder を使用して、カスタムの並べ替え順序を作成します。 (注意:私はこれを使用したことがないので、そこに何が存在するかわからない。)次に、そのカスタムソート順でNLSSORT関数を使用できます。
あなたの説明に基づいて、それは[〜#〜] 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)));
別のアプローチは、FN_SPECIAL_SORT_KEY(sorted_column,'asc')
に関数ベースのインデックスを追加することです。追加の列とトリガーの必要性を回避し、クエリを変更する必要はありません。
_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
_を含めると、順序が確定的(反復可能)になり、質問で明確に要求されなくても、これは良いことかもしれません。