4

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

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

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

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

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

我在这里阅读了官方的 H2 文档:http ://www.h2database.com/html/commands.html#set_collat​​ion 。在这里:http ://www.h2database.com/html/datatypes.html#varchar_ignorecase_type 更多相关信息: 排序规则和本地语言关系

测试 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;

在此处输入图像描述

4

2 回答 2

2

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

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

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

对于纯 H2 解决方案,您可以将H2 别名计算列或查询条件一起使用。第一个解决方案允许索引来加快您的查询。

于 2014-12-19T10:58:55.323 回答
0

差不多 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 子句

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

就个人而言,我主要使用英语主要力量整理器来解决空格问题。即使对于非英文文本列也能正常工作。

但是您可能还需要使用自定义比较器来满足更困难的要求,例如自然或直观的排序顺序,例如Windows 资源管理器语义版本控制等排序。

{
  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 数据库是否使用排序规则?

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

整理

于 2020-06-10T10:32:32.160 回答