【问题标题】:Oracle equivalent sort in JavaJava中的Oracle等效排序
【发布时间】:2014-12-16 14:47:04
【问题描述】:

我想按字母顺序对 Java 字符串进行排序。排序应该类似于 Oracle SQL order by。我尝试使用 Java Collat​​or,但它优先考虑小写字母而不是大写字母。非英文字母也有问题...

例如:

select * from TABLE1 order by COLUMN1;

按以下顺序返回字符串:A、a、Á、á、Ä、ä、B、b、C、C(对我来说是正确的)

Collections.sort(strings, Collator.getInstance());

按如下顺序排列字符串:a, A, á, ä, Ä, Á, b, B, C, C(á, ä, Ä, Á 顺序有问题)

(两种情况下的语言环境相同)

我不想输入整个字母表,因为我可能会忘记一些特殊的字母。 Out 应用程序将被来自许多欧洲国家的许多不同的人使用。

【问题讨论】:

  • 好吧,预言机的ORDER BYgives a different output than the one you provided
  • 可能有点重量级,但icu可能有你想要的?否则,呃...制作您自己的基于规则的整理器? :p
  • 另外,为什么你希望它们以完全相同的顺序排列?为什么这是一个要求?
  • @BackSlash 如果我将 NLS_SORT 设置为 GERMAN_CI 或 GENERIC_M_CI,我可以重现此问题。

标签: java sql oracle sorting sql-order-by


【解决方案1】:

排序很复杂。 Oracle documentation 提供了不同方面的完整概述。

很高兴知道您尝试重现的确切排序,即NLS_SORT 的确切值。你可以通过执行来发现

SELECT SYS_CONTEXT ('USERENV', 'NLS_SORT') from SYS.DUAL;

您使用的排序产生

A, a, Á, á, Ä, ä, B, b, C, c

不清楚输入的顺序是什么。

  • 它将A 放在a 之前。这很奇怪。我推断它实际上并不是更喜欢 A 而不是 a 而是认为它们相等,即不区分大小写。
  • 它将不带重音的字母放在带重音的字母之前,所以我推断它对重音敏感。

GENERIC_M_CI 中的 NLS_SORT 符合要求。您可以通过在 oracle 中运行它来检查:

[...] ORDER BY NLSSORT(<colname>, 'NLS_SORT=GENERIC_M_CI');

Java Collat​​or 有一个setStrength() 方法,它接受值PRIMARYSECONDARYTERTIARYIDENTICAL

确切的解释取决于语言环境,但javadocs 是一个例子

  • 主要强度仅区分 ab
  • 二级强度也区分aá
  • 三级强度还区分aA
  • 只有当字符绝对相同时,才能满足相同的强度。

因此,具有 SECONDARY 实力的 Collat​​or 应该可以很好地为您服务。

在我的机器上,使用 en_US 默认语言环境,我尝试了这个:

List<String> strings = Arrays.asList("A", "Ä", "Á", "B", "C", "a", "á", "ä", "b", "c");
Collator collator = Collator.getInstance();
collator.setStrength(Collator.SECONDARY);
Collections.sort(strings, collator);
System.out.println(strings);

打印

[A, a, Á, á, Ä, ä, B, b, C, c]

(但如果您将a 放在A 之前,则该顺序不会受到影响。)

【讨论】:

  • 太好了,'NLSSORT(, 'NLS_SORT=GENERIC_M_CI');'
【解决方案2】:

据我了解,这对你有帮助

  Collator coll = Collator.getInstance(locale);
  coll.setStrength(Collator.PRIMARY) 
  Collections.sort(words, coll);

或者你可以这样尝试

 List<String> words = Arrays.asList(
      "Äbc", "äbc", "Àbc", "àbc", "Abc", "abc", "ABC"
    );

    log("Different 'Collation Strength' values give different sort results: ");
    log(words + " - Original Data");
    sort(words, Strength.Primary);
    sort(words, Strength.Secondary);
    sort(words, Strength.Tertiary);

    private enum Strength {
    Primary(Collator.PRIMARY), //base char
    Secondary(Collator.SECONDARY), //base char + accent
    Tertiary(Collator.TERTIARY), // base char + accent + case
    Identical(Collator.IDENTICAL); //base char + accent + case + bits

    int getStrength() { return fStrength; }

    private int fStrength;
    private Strength(int aStrength){
      fStrength = aStrength;
    }
  }

  private static void sort(List<String> aWords, Strength aStrength){
    Collator collator = Collator.getInstance(TEST_LOCALE);
    collator.setStrength(aStrength.getStrength());
    Collections.sort(aWords, collator);
    log(aWords.toString() + " " + aStrength);
  }

该类输出以下内容:

Different 'Collation Strength' values give different sort results: 
[Äbc, äbc, Àbc, àbc, Abc, abc, ABC] - Original Data
[Äbc, äbc, Àbc, àbc, Abc, abc, ABC] Primary
[Abc, abc, ABC, Àbc, àbc, Äbc, äbc] Secondary
[abc, Abc, ABC, àbc, Àbc, äbc, Äbc] Tertiary

参考来自

Comparator

Localized ordering

【讨论】:

    【解决方案3】:

    1)您需要一个代表Oracle 排序顺序的字符串。我称这个字符串为 oracleSort。 您可以尝试在互联网上搜索此内容或 您可以将每个字母的一行插入数据库,查询该列并返回结果。从结果中组装您的排序字符串。这听起来很费力,但您也可以使用 Java 程序来填充数据库。

    oracleSort = "AaÁáÄäBbCc..."

    2)我认为您需要实现一个比较两个字符串的比较器。 http://docs.oracle.com/javase/1.5.0/docs/api/java/util/Comparator.html 逐个字符地遍历字符串并比较在 oracleSort 中找到的字符索引。索引小于其对应位置的第一个字符是较小的字符串。

    oracleSort.indexOf("a") 是 1。

    oracleSort.indexOf("Á") 是 2。

    “a”小于“Á”

    3)后来我想可能有一种替代模式。看起来顺序是按不带重音符号的字母分组,然后按 ASCII 大写分组,然后是大写在小写之前。

    因此,您可以使用 Apache commons-lang StringUtils.stripAccents 来制作不带重音符号和大写字母的字符串副本。如果它们相等,则比较副本带有重音但大写。如果相等,则检查每个字符,看一个是大写还是一个是小写。

    public static int compare(String one, String two)
    {
        String oneNoAccent = StringUtils.stripAccents(one).toUpperCase();
        String twoNoAccent = StringUtils.stripAccents(two).toUpperCase();
        int compare = oneNoAccent.compareTo(twoNoAccent);
        if(compare == 0)
        {
            String oneU = one.toUpperCase();
            String twoU = two.toUpperCase();
            compare = oneU.compareTo(twoU);
            if(compare == 0)
            {
                //TODO:
            }
        }
        return compare;
    }
    

    【讨论】:

      猜你喜欢
      • 2013-05-08
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2021-01-09
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2011-07-25
      相关资源
      最近更新 更多