排序规则和区分大小写

数据库中的文本处理可能会很复杂,需要更多的用户关注。 一方面,数据库处理文本的方式差异很大;例如,虽然一些数据库默认区分大小写(例如 Sqlite、PostgreSQL),但其他数据库不区分大小写(SQL Server、MySQL)。 此外,由于索引的使用,区分大小写和类似方面可能会对查询性能产生深远的影响:虽然在区分大小写的数据库中使用 string.ToLower 强制实施不区分大小写的比较很有诱惑力,但这样做可能会阻止应用程序使用索引。 本页详细说明了如何配置区分大小写或更为普遍的排序规则,以及如何在不影响查询性能的情况下有效地执行此操作。

排序规则简介

排序规则是文本处理中的一个基本概念,它是一组规则,用于确定文本值的排序和相等性比较方式。 例如,虽然不区分大小写的排序规则为了相等性比较会忽略大写和小写字母之间的差异,但区分大小写的排序规则不会。 但是,由于区分大小写是区分区域性的(例如,iI 在土耳其语中代表不同的字母),因此存在多个不区分大小写的排序规则,每个排序规则都有自己的一组规则。 排序规则的范围也超出了区分大小写的范围,扩展到字符数据的其他方面;例如,在德语中,有时(但并非总是)需要将 äae 视为相同。 最后,排序规则还定义了文本值的排序方式:德语将 ä 放在 a 之后,瑞典语将它放在字母表的末尾

数据库中的所有文本操作都使用排序规则(无论是显式还是隐式)来确定确定操作比较和排序字符串的方式。 可用排序规则及其命名方案的实际列表是特定于数据库的;有关指向各种数据库的相关文档页的链接,请参阅以下部分。 幸运的是,数据库通常允许在数据库或列级别定义默认排序规则,并显式指定应对查询中的特定操作使用哪种排序规则。

数据库排序规则

在大多数数据库系统中,默认排序规则是在数据库级别定义的;除非被覆盖,否则该排序规则将隐式应用于该数据库中发生的所有文本操作。 数据库排序规则通常在创建数据库时设置(通过 CREATE DATABASE DDL 语句),如果未指定,则默认为设置时确定的某个服务器级别值。 例如,SQL Server 中“英语(美国)”计算机区域设置的默认服务器级排序规则是 SQL_Latin1_General_CP1_CI_AS,这是一种不区分大小写、区分重音的排序规则。 尽管数据库系统通常允许更改现有数据库的排序规则,但这样做可能会使情况复杂化;建议在创建数据库之前选择排序规则。

使用 EF Core 迁移管理数据库架构时,模型的 OnModelCreating 方法中的以下内容将 SQL Server 数据库配置为使用区分大小写的排序规则:

modelBuilder.UseCollation("SQL_Latin1_General_CP1_CS_AS");

列排序规则

也可以在文本列上定义排序规则,从而覆盖数据库默认规则。 如果某些列需要不区分大小写,而数据库的其余部分需要区分大小写,这会十分适用。

使用 EF Core 迁移来管理数据库架构时,以下内容将 Name 属性的列配置为在数据库中不区分大小写,否则该数据库已配置为区分大小写:

modelBuilder.Entity<Customer>().Property(c => c.Name)
    .UseCollation("SQL_Latin1_General_CP1_CI_AS");

查询中的显式排序规则

在某些情况下,需要通过不同的查询使用不同的排序规则查询同一列。 例如,一个查询可能需要对列执行区分大小写的比较,而另一个查询可能需要对同一列执行不区分大小写的比较。 这可以通过在查询本身内显式指定排序规则来实现:

var customers = context.Customers
    .Where(c => EF.Functions.Collate(c.Name, "SQL_Latin1_General_CP1_CS_AS") == "John")
    .ToList();

这会在 SQL 查询中生成一个 COLLATE 子句,无论在列或数据库级别定义的排序规则如何,该子句都会应用区分大小写的排序规则:

SELECT [c].[Id], [c].[Name]
FROM [Customers] AS [c]
WHERE [c].[Name] COLLATE SQL_Latin1_General_CP1_CS_AS = N'John'

显式排序规则和索引

索引是数据库性能中最重要的因素之一,使用索引高效运行的查询在没有该索引的情况下可能会中断。 索引隐式继承其列的排序规则;这意味着该列上的所有查询都自动有资格使用该列上定义的索引,前提是该查询未指定其他排序规则。 在查询中指定显式排序规则通常会阻止该查询使用在该列上定义的索引,因为排序规则将不再匹配;因此,建议在使用此功能时务必小心。 最好在列(或数据库)级别定义排序规则,允许所有查询隐式使用该排序规则并从任何索引中受益。

请注意,某些数据库允许在创建索引时定义排序规则(例如 PostgreSQL、Sqlite)。 这允许在同一列上定义多个索引,从而加快使用不同排序规则的操作(例如区分大小写和不区分大小写的比较)。 有关详细信息,请查阅数据库提供程序文档。

警告

始终检查查询的查询计划,并确保在执行大量数据的性能关键型查询时使用了正确的索引。 通过 EF.Functions.Collate(或通过调用 string.ToLower)覆盖查询中的区分大小写会对应用程序的性能产生非常显着的影响。

内置 .NET 字符串操作的转换

在 .NET 中,字符串相等性默认区分大小写:s1 == s2 执行要求字符串相同的序号比较。 由于数据库的默认排序规则各不相同,而且简单等式需要使用索引,因此 EF Core 不会尝试将简单等式转换为数据库区分大小写的操作:C# 等式直接转换为 SQL 等式,它可能区分大小写,也可能不区分大小写,具体取决于使用的特定数据库及其排序规则配置。

此外,.NET 提供了接受 StringComparison 枚举的 string.Equals 重载,这允许为比较指定大小写敏感性和区域性。 根据设计,EF Core 避免将这些重载转换为 SQL,尝试使用它们将引发异常。 一方面,EF Core 不知道应该使用哪种区分大小写或不区分大小写的排序规则。 更重要的是,在大多数情况下应用排序规则会阻止索引的使用,从而显着影响非常基本和常用的 .NET 构造的性能。 若要强制查询使用区分大小写或不区分大小写的比较,请通过 EF.Functions.Collate 显式指定排序规则,如上面详述

其他资源

特定于数据库的信息

其他资源