按字母顺序对varchar2中的字符进行排序

时间:2010-05-14 16:05:08

标签: oracle

我正在寻找一个按字母顺序对varchar2中的字符进行排序的函数。

我可以使用oracle中内置的东西,还是需要在PL / SQL中创建自定义?

6 个答案:

答案 0 :(得分:1)

http://forums.oracle.com/forums/thread.jspa?messageID=1791550的答案可能会有效,但是没有10g可以测试......

SELECT MIN(permutations)
FROM (SELECT REPLACE (SYS_CONNECT_BY_PATH (n, ','), ',') permutations
    FROM (SELECT LEVEL l, SUBSTR ('&col', LEVEL, 1) n
        FROM DUAL
        CONNECT BY LEVEL <= LENGTH ('&col')) yourtable
    CONNECT BY NOCYCLE l != PRIOR l)
WHERE LENGTH (permutations) = LENGTH ('&col')

在示例中col是在SQL * Plus中定义的,但是如果你将它作为一个函数你可以传入它,或者可以重新编写它以直接取一个表列。我想。

我认为这是一个起点,而不是解决方案;最初的问题是关于字谜,因此它旨在找到所有排列,所以类似但简化的东西可能是可能的。我怀疑对于较大的值,这不能很好地扩展。

答案 1 :(得分:1)

你应该记住,没有“按字母顺序”表示什么的共同协议。这一切都取决于它是哪个国家,谁在查看您的数据以及它所处的背景。

例如在DK中,有大量不同的分类a,aa,b,c,æ,ø,å

  • 按字母顺序排列:a,aa,b,c,æ,ø,å
  • 对于某些词典:a,aa,å,b,c,æ,ø
  • 其他词典:a,b,c,æ,ø,aa,å
  • 根据Microsoft标准:a,b,c,æ,ø,aa,å

查看http://www.siao2.com/2006/04/27/584439.aspx了解详情。对于这些问题,这也恰好是一个很棒的博客。

答案 2 :(得分:1)

所以我最终选择了PL / SQL路由,因为在搜索了一段时间之后我意识到没有可以使用的内置函数。

这是我想出的。它基于关联数组的未来,即Oracle按键排序顺序。

create or replace function sort_chars(p_string in varchar2) return varchar deterministic
as
     rv varchar2(4000);
     ch  varchar2(1);
     type vcArray is table of varchar(4000) index by varchar2(1);
     sorted vcArray;

     key varchar2(1);

begin
     for i in 1 .. length(p_string)
     loop
        ch := substr(p_string, i, 1);

        if (sorted.exists(ch))
        then 
            sorted(ch) := sorted(ch) || ch;
        else
            sorted(ch) := ch;
        end if;
     end loop;


    rv := '';
    key  := sorted.FIRST;
    WHILE key IS NOT NULL LOOP
        rv := rv || sorted(key);
        key := sorted.NEXT(key);
    END LOOP;

     return rv;
end;

简单的性能测试:

set timing on;

create table test_sort_fn as 
select t1.object_name || rownum as test from user_objects t1, user_objects t2;

select count(distinct test) from  test_sort_fn;

select count (*)  from (select sort_chars(test)  from test_sort_fn);


Table created.
Elapsed: 00:00:01.32

COUNT(DISTINCTTEST)
-------------------
             384400
1 row selected.
Elapsed: 00:00:00.57

  COUNT(*)
----------
    384400
1 row selected.
Elapsed: 00:00:00.06

答案 3 :(得分:0)

假设你不介意每行返回1个字符:

select substr(str, r, 1) X from (
select 'CAB' str,
       rownum r
from dual connect by level <= 4000
) where r <= length(str) order by X;

X
=
A
B
C

答案 4 :(得分:0)

您可以使用以下查询:

select listagg(letter) 
    within group (order by UPPER(letter), ASCII(letter) DESC) 
from
(
select regexp_substr('gfedcbaGFEDCBA', '.', level) as letter from dual
connect by regexp_substr('gfedcbaGFEDCBA', '.', level) is not null
);

子查询使用 regexp_substr 将字符串拆分为记录(每个字符为单个字符),外部查询在对它们进行排序后,使用 listagg 将记录合并为一个字符串。

在这里你应该小心,因为按字母顺序排序取决于你的数据库配置,如Cine指出的那样。

在上面的例子中,字母按升序排序&#34;字母顺序排列&#34;并且通过ascii代码降序,在我的例子中,结果是&#34; aAbBcCdDeEfFgG&#34;。 您的案例结果可能会有所不同。

您也可以使用 nlssort 对字母进行排序 - 它可以让您更好地控制排序顺序,因为您可以独立于数据库配置。

select listagg(letter) 
    within group (order by nlssort(letter, 'nls_sort=german') 
from
(
select regexp_substr('gfedcbaGFEDCBA', '.', level) as letter from dual
connect by regexp_substr('gfedcbaGFEDCBA', '.', level) is not null
);

上面的查询也会给你&#34; aAbBcCdDeEfFgG&#34;但如果你改变了&#34;德国&#34;西班牙语&#34;你会得到&#34; AaBbCcDdEeFfGg&#34;代替。

答案 5 :(得分:0)

对于使用Oracle 10g的人import optuna def objective(trial: optuna.Trial): # Sample parameters. x = trial.suggest_int('x', 0, 10) y = trial.suggest_categorical('y', [-10, -5, 0, 5, 10]) # Check duplication and skip if it's detected. for t in trial.study.trials: if t.state != optuna.structs.TrialState.COMPLETE: continue if t.params == trial.params: return t.value # Return the previous value without re-evaluating it. # # Note that if duplicate parameter sets are suggested too frequently, # # you can use the pruning mechanism of Optuna to mitigate the problem. # # By raising `TrialPruned` instead of just returning the previous value, # # the sampler is more likely to avoid sampling the parameters in the succeeding trials. # # raise optuna.structs.TrialPruned('Duplicate parameter set') # Evaluate parameters. return x + y # Start study. study = optuna.create_study() unique_trials = 20 while unique_trials > len(set(str(t.params) for t in study.trials)): study.optimize(objective, n_trials=1) 不起作用。可接受的答案确实有效,但是它会生成输入字符串的所有可能排列,从而导致糟糕的性能-我的Oracle数据库在输入字符串只有10个字符的情况下苦苦挣扎。

这是适用于Oracle 10g的另一种替代方法。类似于Jeffrey Kemp's answer,只是结果不拆分成行:

select listagg within group

select replace(wm_concat(ch), ',', '') from ( select substr('CAB', level, 1) ch from dual connect by level <= length('CAB') order by ch ); -- output: 'ABC' 只需将逗号之间的分隔符将不同行中的记录连接成一个字符串(这就是我们稍后还要进行替换的原因)。

请注意,如果您输入的字符串中包含逗号,则它们将丢失。此外,wm_concat是一个未记录的功能,according to this answer在Oracle 12c中已被删除。仅当您坚持使用10g且没有更好的选择时才使用它(例如wm_concat,如果可以使用11g,则可以使用它。)