【问题标题】:Keep strings case-insensitively unique in a database在数据库中保持字符串不区分大小写的唯一性
【发布时间】:2010-12-19 21:31:36
【问题描述】:

我想确保当用户想要在我的系统(网络应用程序)中注册用户名时,即使不考虑大小写,用户名也是唯一的。因此,如果一个名为“SuperMan”的用户已经注册,则不得允许其他用户注册为“superman”或“SUPERman”。这必须在数据库级别进行检查。

在我当前的实现中,我执行以下操作:

select count(*) from user where lower(name) = lower(?) 进行更新; -- 如果计数大于 0,则错误中止 -- 为用户确定一个新的ID 插入用户(id,name,...)值(?,?,...);

我不确定“for update”是否会将数据库锁定得足够远,以致其他用户无法在上述两个 SQL 语句之间使用无效名称进行注册。可能这不是 100% 安全的解决方案。不幸的是,我不能在 SQL 中使用唯一键,因为它们只会区分大小写。

还有其他解决方案吗?以下怎么样,以增加安全性?

select count(*) from user where lower(name) = lower(?) 进行更新; -- 如果计数大于 0,则错误中止 -- 为用户确定一个新的ID 插入用户(id,name,...)值(?,?,...); -- 现在再数一数 select count(*) from user where lower(name) = lower(?); -- 如果计数现在大于 1,请执行以下操作: 从 id = ?; 的用户中删除 -- 或者回滚事务

【问题讨论】:

  • 说到什么DBMS支持什么,我目前的要求是依次支持MySQL、SQLite和PostgreSQL。不过,其他系统对未来会很好。由于 PostgreSQL 尚不支持这种排序规则(在他们的 TODO 中)并且 SQLite 不能很好地支持 Unicode(A = a,但 Ä != ä),我可能需要另一种解决方案。

标签: sql database unique case-insensitive


【解决方案1】:

我这样做的方法是为用户名创建第二列,该用户名要么全部转换为大写字母,要么全部小写。我在上面放了一个唯一的索引。我使用触发器在另一列中生成值,因此代码不必担心它。

想一想,您可以使用基于函数的索引来获得相同的结果:

CREATE UNIQUE INDEX user_name_ui1 ON user (lower(name))

【讨论】:

  • 单独的列似乎是最便携和最容易处理/理解的解决方案。我会看看我是否将它留给应用程序来为附加列创建“标准化”值,或者我是否会为此使用触发器。关于 SQLite 糟糕的 Unicode 支持(开箱即用),我可能会让 iconv(在 PHP 中)完成这项工作。
【解决方案2】:

您认为唯一约束会区分大小写的假设是不正确的。他们将根据列的排序规则进行比较。所有值得讨论的商业数据库支持排序规则。只需为您的列选择不区分大小写的排序规则,并在其上声明唯一约束。见:

在插入之前通过查找执行执行不仅效率极低,在并发下也是不正确的。

【讨论】:

  • 如果您使用的是 JDBC 之类的东西,您经常不知道您使用的是哪个数据库。任何需要在客户端使用 DBMS 特定解决方案的东西都会破坏这种可移植性。
  • 声明列排序规则并在其上放置唯一索引显然是 DDL 部署时间操作。没有跨平台 DDL,任何不仅仅是简单演示软件的应用程序如果尝试进行声称跨平台工作的任何部署,都会遇到无法克服的问题。对于 DDL 操作,基本上没有没有标准。例如,CREATE UNIQUE INDEX user_name_ui1 ON user (lower(name)) 这样的语法在 MySQL 或 SQL Server 上是白日梦,因为它们不支持 Oracle 支持的基于函数的索引。
【解决方案3】:

您还可以通过更改该列的排序规则来更改唯一约束的工作方式:Unique constraint on table column

【讨论】:

  • 我认为这将是特定于 DBMS 的。使用生成的列几乎可以处理任何事情。我还将该列用于代码不知道它正在使用哪个 DBMS 的排序。
  • @JOTN,我完全同意。虽然许多 SQL 服务器包支持排序规则并不是所有的都支持(MSSQL、MySql、Sqlite 支持,但 PostgreSQL 不支持),并且它们支持的排序规则集可能会有所不同。因此,当您可能想要切换到不同的 DBMS 时,使用排序规则肯定会限制您。我只是认为它应该作为一个选项放在桌面上。不过,我 +1 了你的答案。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2013-05-11
  • 1970-01-01
  • 2012-01-31
  • 2017-04-15
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多