如果按名称搜索,如果列上有索引,是不是需要使列(字符串)对 EF Core 查询区分大小写?

Posted

技术标签:

【中文标题】如果按名称搜索,如果列上有索引,是不是需要使列(字符串)对 EF Core 查询区分大小写?【英文标题】:If searching by name, do I need to make the column (string) case sensitive for EF Core query if I have an index on the column?如果按名称搜索,如果列上有索引,是否需要使列(字符串)对 EF Core 查询区分大小写? 【发布时间】:2021-11-08 00:04:04 【问题描述】:

如果我通过用户名 ex 进行简单搜索。迈克。

var poses = await _dbContext.Users
    .FirstOrDefaultAsync(p => p.UserName == userName);

并且 UserName 列已编入索引,如果我收到不同情况下的值(例如 MIKE、MIke、mike)是否有关系?

不区分大小写会影响性能吗?

或者我应该创建一个规范化列(例如 MIKE、PAUL、BOB),然后接受我的输入并执行 userName.ToUpper()?

例如

var poses = await _dbContext.Users
    .FirstOrDefaultAsync(p => p.NormalizedUserName == userName.ToUpper());

【问题讨论】:

Collations and Case Sensitivity 从这个链接的内容来看,我应该像我的示例一样创建一个标准化列并使用它来执行搜索? 但是等等,链接说 Sql Server 不区分大小写。所以我不需要规范化列? 取决于排序规则,可以在列级别设置 出于同样的原因,MS 已将 Normalized Email and UserName 字段添加到基本 IdentityUser 模型中。 【参考方案1】:

在我见过的大多数应用程序中,用户名通常不区分大小写,只有密码。

如果你有一个非常大的用户表,这样的查询

var user = await _dbContext.Users
    .FirstOrDefaultAsync(p => p.UserName.ToLower == userName.ToLower());

会影响性能,因为您需要将每个用户名转换为较低的。

所以恕我直言,当您保存新用户或更新现有用户时,将用户名转换为小写字母是个好主意,但您不需要为此保留特殊列。然后你可以使用这样的代码

var user = await _dbContext.Users
    .FirstOrDefaultAsync(p => p.UserName == userName.ToLower());

【讨论】:

【参考方案2】:

行为将完全取决于为数据库选择的排序规则选项。默认情况下,SQL Server 将使用不区分大小写 (CI) 排序规则,因此当您针对数据库编写查询时,无论是 EF 还是手动,字符串比较都将不区分大小写。但是,当您针对区分大小写的排序规则 (CS) 数据库运行应用程序时,您的应用程序将无法匹配字符串。 (即 PostgreSQL 我相信默认使用 CS,或者针对 _CS Collat​​ion SQL Server 数据库运行。)在一个客户端站点上,开发数据库是 _CI 而生产服务器使用 _CS 排序规则,这有点令人惊讶。

只要您确保记录行为并标记不区分大小写排序规则是系统的要求,就可以让 SQL Server(或类似数据库)处理不区分大小写的搜索。性能方面,我不知道让 SQL Server 进行不区分大小写比较的任何隐含成本。我希望编写您的所有查询,例如:

.FirstOrDefaultAsync(p => p.UserName.ToLower() == userName.ToLower());

...实际上会产生从小到大的性能成本,并可能抵消索引使用和可能在数据库中执行查询的其他内置性能度量。

否则,如果要求不区分大小写,那么您的应用程序应确保不区分大小写的字段始终以大写或小写形式存储,并相应地使用相同的强制大小写进​​行搜索。如果外部系统/查询可能会改变数据,那么这里的问题是您的代码会期望数据库本身没有强制执行的一致情况。检查约束可以捕获插入/更新混合大小写的尝试,或者您可以使用而不是插入触发器来替换混合大小写 /w 大写或小写。当然,这里的选项完全取决于您的 RDBMS。

【讨论】:

还有列排序规则...【参考方案3】:

Entity Framework默认does not make any collation specification在生成的SQL中。因此,查询如

var poses = await _dbContext.Users
    .FirstOrDefaultAsync(p => p.UserName == userName);

只会被翻译成类似的东西

SELECT TOP 1 u.*
FROM Users u
WHERE u.UserName = @userName;

有区分大小写和不区分大小写的排序规则。可以在每一列上设置不同的排序规则,尽管在创建时没有明确指定,它将采用数据库默认排序规则。

同时,变量的排序规则是当前数据库的排序规则。但与列引用相比,它的优先级较低。 所以这一切都取决于列的排序规则。


另一方面,查询如

var poses = await _dbContext.Users
    .FirstOrDefaultAsync(p => p.NormalizedUserName == userName.ToUpper());

变成

SELECT TOP 1 u.*
FROM Users u
WHERE u.NormalizedUserName = UPPER(@userName);

因此排序规则的大小写敏感性没有区别。 但仍有其他排序规则属性可能会受到影响。

请注意,您还会通过像这样的规范化来丢失信息,这可能是也可能不是您想要的。单独的整理规则通常不会剥离案例信息。这就是为什么通常最好只为此类列设置不区分大小写的排序规则,然后完成。


无论你做什么,不要这样做

var poses = await _dbContext.Users
    .FirstOrDefaultAsync(p => p.UserName.ToUpper() == userName.ToUpper());

这会导致

SELECT TOP 1 u.*
FROM Users u
WHERE UPPER(u.UserName) = UPPER(@userName);

不能使用索引,并且是一个全面的坏主意。

【讨论】:

好的,感谢您的帮助。我问的原因是因为我现在必须搜索 2 列(例如 UserName、IsActive),所以我创建了一个多列索引,所以我现在担心如果我不使用它可能会影响 Sql Server 的性能标准化列。但我想我只是坚持第一个例子,我应该没问题。 排序规则的全部意义在于它们定义了数据的排序顺序(例如,AaBb 之前排序)。因此,当您查询该排序规则下的列时(如果您没有特别指定,您将这样做),那么您可以在该排序顺序下查找索引。然而,如果您强制对列进行排序规则更改(最后一个示例),那么您将无法使用索引,因为它的顺序错误。

以上是关于如果按名称搜索,如果列上有索引,是不是需要使列(字符串)对 EF Core 查询区分大小写?的主要内容,如果未能解决你的问题,请参考以下文章

您是不是需要在 Hibernate 表的 @id 列上创建索引

具有重复值的列上的数据库索引

MySQL索引优化系列:索引失效

Oracle 索引

MYSQL

MYSQL