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#

Answers:


5

如果Oracle已支持您要指定的排序顺序,则可以通过NLSSORT函数的排序来完成此操作-像这样:

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

您可以在此处找到支持的排序顺序列表。


既然这些涉及案例和变音符号,那么在这种情况下真的有一个可行的方法吗?
Leigh Riffel

5

一些选项:

  1. 通过触发器将数据的排序版本坚持到表中,然后使用它。

  2. 使用Oracle Locale Builder构建自定义排序顺序。(注意:我从未使用过,所以我不知道那里可能存在什么陷阱。)然后,您可以将NLSSORT函数与该自定义排序顺序一起使用。


4

另一种方法是在上添加基于函数的索引FN_SPECIAL_SORT_KEY(sorted_column,'asc')。避免了额外的column + trigger,并且您无需修改​​查询。


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)));

正如杰克·道格拉斯(Jack Douglas)所指出的,就翻译的特殊字符而言,这些结果的顺序是不可预测的。因此,尽管此答案解决了OP的问题,但如果您需要一致的符号顺序,则可能没有用。
Leigh Riffel

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。问题在于,这种排序方式与原始解决方案不同。因此,需要您更正的是更改后的版本,而不是您的原始版本。可以通过更改第二个正则表达式来固定排序顺序,第二个正则表达式的顺序为regexp_replace(lower(foo), '[^a-z]', '~') || regexp_replace(foo, '[^0-9a-zA-Z]', '~') || foo
Leigh Riffel
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.