差不多 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 数据库是否使用排序规则?
查看设置表。如果未设置排序规则,则表中将没有条目。