Cột sắp xếp Oracle varchar2 với các ký tự đặc biệt cuối cùng


8

Làm cách nào tôi có thể sắp xếp trong Oracle một cột Varchar2 hoặc NVarchar2 theo thứ tự được xác định tùy chỉnh của riêng tôi. Hoặc là bất kỳ tùy chọn hiện có nào sẽ đặt chữ cái đầu tiên, sau đó là số, sau đó là tất cả các ký tự đặc biệt.

Cách tiếp cận đầu tiên của chúng tôi là sử dụng một hàm thực hiện một số thủ công các ký tự thành số.

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

Hàm sắp xếp đặc biệt ánh xạ mỗi ký tự thành một số có 2 chữ số và giá trị trả về được sử dụng để sắp xếp. Điều này dường như chỉ là sự kết hợp thực sự tốn kém, và nó cảm thấy sai.

        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;

Tôi đang có một thời gian khó khăn để đưa ra một phương pháp thay thế. Tôi muốn biết những vấn đề tồn tại với phương pháp này. Có lẽ chúng ta không có lựa chọn thay thế.

Phụ lục 1:

Thêm ví dụ về dữ liệu được sắp xếp. Nói chung, tất cả các ký tự alpha không nhạy, sau đó là các số 0-9, sau đó là các ký tự đặc biệt theo bất kỳ thứ tự nào.

Dưới đây là một mẫu sắp xếp danh sách tăng dần. Hãy nhớ rằng các ký tự đặc biệt có thể hoán đổi cho nhau, tất cả chúng nên nằm sau các chữ cái và số. Trong sắp xếp nhị phân, một số ký tự đặc biệt nằm trước các chữ cái (tức là ')

Đơn hàng mong muốn của tôi,

AB1 $
aCC #
ac '
BZ

Thứ tự nhị phân của Oracle

AB1 $
BZ
ac '
acc #

Câu trả lời:


5

Nếu thứ tự sắp xếp mà bạn muốn chỉ định đã được Oracle hỗ trợ, bạn có thể thực hiện việc này bằng cách đặt hàng theo hàm NLSSORT - như vậy:

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

Bạn có thể tìm thấy một danh sách các đơn đặt hàng sắp xếp được hỗ trợ ở đây .


Vì những thỏa thuận với trường hợp và dấu phụ có thực sự có tác dụng trong trường hợp này không?
Leigh Riffel

5

Một số tùy chọn:

  1. Duy trì phiên bản đã sắp xếp của dữ liệu của bạn vào bảng thông qua trình kích hoạt và sử dụng nó.

  2. Sử dụng Oracle Locale Builder để xây dựng một thứ tự sắp xếp tùy chỉnh. (Hãy cẩn thận: Tôi chưa bao giờ sử dụng cái này, vì vậy tôi không biết những gì gotchas có thể tồn tại ở đó.) Sau đó, bạn có thể sử dụng chức năng NLSSORT với thứ tự sắp xếp tùy chỉnh đó.


4

Một cách tiếp cận khác là thêm một chỉ mục dựa trên chức năng FN_SPECIAL_SORT_KEY(sorted_column,'asc'). Tránh sự cần thiết phải có thêm cột + kích hoạt và bạn sẽ không cần phải sửa đổi các truy vấn của mình.


4

Dựa trên mô tả của bạn, có vẻ như TRANSLATE có thể thực hiện công việc cho bạn. Như Jeffrey Kemp gợi ý, một chỉ số dựa trên chức năng có thể được tạo ra cho việc này.

Thiết lập:

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

Trình diễn:

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

Đầu ra:

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 

Kiểm tra thứ tự của tất cả các ký tự:

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

Như Jack Douglas đã chỉ ra, thứ tự của các kết quả này là không thể dự đoán được đối với các ký tự đặc biệt được dịch. Vì vậy, mặc dù câu trả lời này giải quyết được câu hỏi của OP nhưng nó có thể không hữu ích nếu bạn yêu cầu thứ tự biểu tượng nhất quán.
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    
!    
*/

Nếu bạn muốn lập chỉ mục dữ liệu để tránh sắp xếp một truy vấn với một order by, bạn có thể làm như vậy:

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 |
--------------------------------------------------------------------------
*/

-- biên tập

Như @Leigh đã nhận xét, một cách tiếp cận khác, gọn gàng hơn là có một chức năng duy nhất nối các biểu thức (đã sửa đổi): regexp_replace(lower(foo), '[^a-z]', '~')||regexp_replace(foo, '[^a-zA-Z0-9]', '~')||foo

bao gồm cả ||fookết thúc trong cả hai trường hợp làm cho việc xác định thứ tự (có thể lặp lại) có thể là một điều tốt mặc dù câu hỏi không yêu cầu cụ thể.


1
Một cách để làm cho giải pháp này có thể sử dụng được với chỉ mục dựa trên chức năng (một chức năng) là nối thứ tự theo. Điều này cho chúng ta regexp_replace(lower(foo), '[^a-z]', '~') || regexp_replace(foo, '[^0-9]', '~') || foo. Vấn đề là điều này sắp xếp khác với giải pháp ban đầu của bạn. Vì vậy, đây là phiên bản thay đổi cần chỉnh sửa này, không phải bản gốc của bạn. Thứ tự sắp xếp có thể được sửa bằng cách thay đổi regex thứ hai, cung cấp một thứ tự theo regexp_replace(lower(foo), '[^a-z]', '~') || regexp_replace(foo, '[^0-9a-zA-Z]', '~') || foo.
Leigh Riffel
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.