【问题标题】:H2 database collation: what to choose?H2数据库整理:选择什么?
【发布时间】:2012-11-30 17:12:32
【问题描述】:

经过大量阅读和实验,似乎我想要 PRIMARY 强度进行搜索,但 TERTIARY 或 IDENTICAL 进行排序。 主要问题:是否可以使用 H2(或任何其他数据库)来实现?

第二个问题:我是这里唯一的一个,还是你们中的任何人也喜欢上述组合?一些确认将有助于我的理智。

背景: 似乎排序规则只能在创建数据库时一开始就设置。所以我想确保选择正确的。我主要考虑这些用例(目前):

  1. 用户可以开始输入以过滤表的搜索字段:这里 PRIMARY 似乎是最合适的,以避免丢失任何结果(用户习惯于 Google...)。不过,如果能够让用户选择启用二级或三级排序规则以进行更精确的搜索,那就太好了。

  2. 排序:当用户单击表列对内容进行排序时,TERTIARY/IDENTICAL 排序似乎是合适的。这是我从日常经验中习惯的。

我在这里阅读了官方 H2 文档:http://www.h2database.com/html/commands.html#set_collation。 在这里:http://www.h2database.com/html/datatypes.html#varchar_ignorecase_type 更多相关信息: Collation STRENGTH and local language relation

测试sql(来自https://groups.google.com/forum/?fromgroups=#!topic/h2-database/lBksrrcuGdY):

drop all objects;
set collation english STRENGTH PRIMARY;
create table test(name varchar);
insert into test values ('À'), ('Ä'), ('Â'), ('A'), ('à'), ('ä'), ('â'), ('a'), ('àa'), ('äa'), ('âa'), ('aa'), ('B'), ('b');
select * from test where name like 'a' order by name;
select * from test order by name;

【问题讨论】:

    标签: search sql-order-by collation h2


    【解决方案1】:

    差不多 8 年后,我自己的建议是基于一些艰苦的学习:

    完全不使用排序规则(H2 数据库的默认设置)。

    理由:使用排序规则会产生一些真正意想不到的结果和错误。

    陷阱:独特的约束

    到目前为止,我在日常业务中看到的最常见的唯一约束是强制唯一(名字、姓氏)。通常情况下,应忽略大小写(禁止同时使用 'thomas müller' 和 'Thomas Müller'),但不能忽略变音符号(允许同时使用 'Thomas Müller' 和 'Thomas Muller')。

    使用排序规则强度SECONDARY 设置来实现此目的可能很诱人(不区分大小写但区分变音符号)。别。 改用VARCHAR_IGNORECASE 列。

    {
      // NOT recommended: using SECONDARY collation
      Statement s = DriverManager.getConnection("jdbc:h2:mem:", "test", "test").createStatement();
      s.execute("SET COLLATION ENGLISH STRENGTH SECONDARY");
      s.execute("CREATE TABLE test ( name VARCHAR )");
      s.execute("ALTER  TABLE test ADD CONSTRAINT unique_name UNIQUE(name)");
      s.execute("INSERT INTO  test (name) VALUES ('Müller')");
      s.execute("INSERT INTO  test (name) VALUES ('Muller')");
      // s.execute("INSERT INTO  test (name) VALUES ('muller')" /* will fail */);
    }
    {
      // recommended: no collation, using VARCHAR_IGNORECASE instead of VARCHAR column
      Statement s = DriverManager.getConnection("jdbc:h2:mem:", "test", "test").createStatement();
      s.execute("CREATE TABLE test ( name VARCHAR_IGNORECASE )");
      s.execute("ALTER  TABLE test ADD CONSTRAINT unique_name UNIQUE(name)");
      s.execute("INSERT INTO  test (name) VALUES ('Müller')");
      s.execute("INSERT INTO  test (name) VALUES ('Muller')");
      // s.execute("INSERT INTO test (name) VALUES ('muller')" /* will fail */);
    }
    

    陷阱:搜索/WHERE 子句

    建议:没有排序规则的默认行为很好,并且行为符合预期。要进行更模糊的搜索,请使用您自己的代码搜索或类似 Lucene 的库。

    SECONDARY 排序规则强度将匹配,即使大小写不同。使用 SELECT WHERE name = '...' 时不会出现这种行为,因为您会忘记所有关于排序规则的设置。

    {
      Statement s = DriverManager.getConnection("jdbc:h2:mem:", "test", "test").createStatement();
      s.execute("SET COLLATION ENGLISH STRENGTH SECONDARY");
      s.execute("CREATE TABLE test ( name VARCHAR )");
      s.execute("INSERT INTO  test (name) VALUES ('Thomas Müller')");
      ResultSet rs = s.executeQuery("SELECT count(*) FROM test WHERE name = 'Thomas müller'" /* different case */);
      rs.next();
      /* prints 1 (!) */ System.out.println(rs.getLong(1));
    }
    

    PRIMARY 排序规则强度将匹配,即使 SPACES 不同。您会相信英语主要排序规则会忽略空格吗?看看这个金块:https://stackoverflow.com/a/16567963/1124509

    {      
      Statement s = DriverManager.getConnection("jdbc:h2:mem:", "test", "test").createStatement();
      s.execute("SET COLLATION ENGLISH STRENGTH PRIMARY");
      s.execute("CREATE TABLE test ( name VARCHAR )");
      s.execute("INSERT INTO  test (name) VALUES ('Thomas Müller')");
      ResultSet rs = s.executeQuery("SELECT count(*) FROM test WHERE name = 'ThomasMüller'" /* no space! */);
      rs.next();
      /* prints 1 (!) */ System.out.println(rs.getLong(1));
    }
    

    排序 / ORDER BY 子句

    没有排序规则的默认排序在实际场景中并不是很有用,因为它会根据严格的字符串比较进行排序。通过首先从数据库中加载数据,然后使用代码对其进行排序/排序来解决此问题。

    就我个人而言,我主要使用固定spaces problem 的英语初级强度整理器。即使是非英文文本列也能正常工作。

    但您可能还需要使用自定义比较器来满足更困难的要求,例如自然或直观的排序顺序,例如sort like windows explorer,或semantic versioning

    {
      Statement s = DriverManager.getConnection("jdbc:h2:mem:", "test", "test").createStatement();
      s.execute("CREATE TABLE test ( name VARCHAR )");
      s.execute("INSERT INTO  test (name) VALUES ('é6')");
      s.execute("INSERT INTO  test (name) VALUES ('e5')");
      s.execute("INSERT INTO  test (name) VALUES ('E4')");
      s.execute("INSERT INTO  test (name) VALUES ('ä3')");
      s.execute("INSERT INTO  test (name) VALUES ('a2')");
      s.execute("INSERT INTO  test (name) VALUES ('A1')");
      ResultSet rs = s.executeQuery("SELECT name FROM test ORDER BY name");
      List<String> names = new ArrayList<>();
      while(rs.next()) {
        names.add(rs.getString(1));
      }
      // not very useful strict String.compareTo() result: [A1, E4, a2, e5, ä3, é6]
      System.out.print(names);
    
      String rules = ((RuleBasedCollator) Collator.getInstance(new Locale("en", "US"))).getRules();
      Collator collator = new RuleBasedCollator(rules.replaceAll("<'\u005f'", "<' '<'\u005f'"));
      collator.setStrength(Collator.PRIMARY);
      names.sort((a, b) -> collator.compare(a, b));
      // as humans usually expect it in a name list / table: [A1, a2, ä3, E4, e5, é6]
      System.out.print(names);
    }
    

    如何检查您的 H2 数据库是否使用排序规则?

    查看设置表。如果未设置排序规则,则表中将没有条目。

    【讨论】:

      【解决方案2】:

      如果您想对单个数据有两种行为,您必须:

      • 将数据拆分为两列,
      • 或使用两个运算符集。

      出于您的目的,通常会存储原始数据的“规范”表示形式,以便在规范形式上进行搜索,然后对原始数据进行排序/显示。可能你应该使用一些“文本搜索引擎”,例如Apache Lucene

      对于纯 H2 解决方案,您可以将 H2 aliasComputed columns 或与查询条件一起使用。第一个解决方案允许建立索引来加快查询速度。

      【讨论】:

        猜你喜欢
        • 2013-12-30
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2012-06-13
        • 1970-01-01
        • 2015-01-08
        • 2014-07-05
        • 1970-01-01
        相关资源
        最近更新 更多