【问题标题】:How do I correctly compare a MySQL text field to a string of arbitrary length?如何正确地将 MySQL 文本字段与任意长度的字符串进行比较?
【发布时间】:2019-12-21 20:25:56
【问题描述】:

假设我有一个使用字符串作为主键的表。例如,一个会话表(我会将其保留为 4 个字符,以便让我的示例更清晰)。

我还将添加一个 Auto Increment 列,以使我的示例更接近现实(以防它产生影响)。

CREATE TABLE sessions (
  `id` INT AUTO INCREMENT,
  `key` CHAR(4) NOT NULL,
  PRIMARY KEY(`key`),
  UNIQUE KEY `idx_key` ( `key` )
);

我可以在表格中插入数据。

INSERT INTO sessions (`key`) VALUES ('abcd');

因为这是来自浏览器的会话 ID,所以我不信任它并在处理请求的代码中使用正确的绑定值。恶意用户可以在这里发送各种字符串,但它们总是以字符串的形式结束,而不是注入攻击。没关系。 MySQL 会很高兴地截断这些数据并发出警告。

INSERT INTO sessions(`key`) VALUES ('abcdefg');
Warning (Code 1265): Data truncated for column 'key' at row 1

但是,这只是用于创建行的“好”(不是很好,但无论如何)。相同的操作首先查找一行,然后再尝试插入。你知道的 - upsert。 MySQL 不会费心截断 this 数据,并确定它不在表中。

SELECT * FROM sessions WHERE `key` = 'abcdefg';
Empty set (0.00 sec)

这意味着我之前的插入更加糟糕,因为当我插入数据时,MySQL 会将其截断为一个确实存在的值。

INSERT INTO sessions (`key`) VALUES ('abcdefg');
Warning (Code 1265): Data truncated for column 'key' at row 1
Error (Code 1062): Duplicate entry 'abcd' for key 'idx_key'

网站代码不知道此列的约束,我无意提供此信息。

如何让 MySQL 只比较字符串的前 N ​​个字符,其中 N 是文本字段的(最大)长度?

【问题讨论】:

  • 根据您的实际问题,我将您的关键示例更新为字母而不是数字。
  • AUTO_INCREMENT 列必须是主键。
  • @Barmar - AUTO_INCREMENT 的唯一索引要求是它是 some 索引中的第一列。它不一定是 PK。

标签: mysql indexing


【解决方案1】:

您可以使用SUBSTRING 方法。

给定 N 是文本字段的(最大)长度,你可以说

SELECT * FROM sessions WHERE `key` = SUBSTRING('abcdefg', 1, N)

如果 N = 4 那么我们有

SELECT * FROM sessions WHERE `key` = SUBSTRING('abcdefg', 1, 4)

【讨论】:

  • 只有 MySQL 知道 4,这意味着我不能直接将它放在查询中...我可以创建一个包含 4 的存储过程,因此它与 CREATE 一起受版本控制TABLE 和我可以使用单个变量吗?我应该吗?
  • 是的@Altreus,你应该这样做。这应该可以工作,并且对于 CREATE 和 SELECT 查询的预期字符长度的可重用性和一致性也更有意义。
【解决方案2】:

various MySQL functions 绕道而行。

最容易使用的是LEFTManual,Q&A

SELECT COUNT(*) as numb FROM sessions WHERE `key` = LEFT(:stringVar,4)

其中:stringVar 是给定会话值的 PDO 准备语句占位符(因此在 MySQL 中完全安全)。

因此

如果:stringVar = 'abcdefg' 则上面的SQL 将检查key = 'abcd' 并忽略字符串的其余部分。

为了灵活性,如果您愿意,也可以使用RIGHT(在这种情况下会返回“defg”)。

警告:

对于 MySQL 5.7 及更高版本,插入长度超过字段长度的数据(如您的问题中所引用),默认情况下现在将中止插入/更新并返回错误。请注意这一点并进行查找。

所以;为避免这种情况,请在插入时也应用LEFT 功能:

INSERT INTO sessions (`key`) VALUES ( LEFT('abcdefg',4));

(未经测试)

如果您的key 列长度是可变的;您可以使用 MySQL INFORMATION_SCHEMA 动态挖掘它们:

SELECT `CHARACTER_MAXIMUM_LENGTH` as ColumnLength FROM `INFORMATION_SCHEMA`.`COLUMNS`
WHERE `COLUMNS`.`DATA_TYPE` = 'char' AND `COLUMNS`.`TABLE_SCHEMA` = '<db_name>' AND `COLUMNS`.`TABLE_NAME` = '<table_name>'

全文:

INSERT INTO sessions (`key`) VALUES ( 
    LEFT(:stringVar, 
        (SELECT `CHARACTER_MAXIMUM_LENGTH`  
         FROM `INFORMATION_SCHEMA`.`COLUMNS`
         WHERE `COLUMNS`.`DATA_TYPE` = 'char' AND `COLUMNS`.`TABLE_SCHEMA` = '<db_name>' AND `COLUMNS`.`TABLE_NAME` = '<table_name>') ));

【讨论】:

    【解决方案3】:

    通过切割成 4 个字符,您是说 字符串的其余部分不用于测试相等性

    网站代码不知道此列的约束,我无意提供此信息。

    存储到CHAR(4) 意味着截断。如果截断字符串,则会丢失信息。所以无法进行测试。

    如何让 MySQL 只比较字符串的前 N ​​个字符,其中 N 是文本字段的(最大)长度?

    LEFT(input_string, 4) 会这样做。那个“4”可以隐藏在位于用户和数据库之间的 API 中。你不应该让用户直接执行INSERTs

    其他注意事项...

    注意CHAR——它用空格填充到指定的长度。然后在比较时忽略尾随空格。因此'abc ''abc' 将比较相等,'abc def' 的截断也是如此。

    INSERT .. ON DUPLICATE KEY .. 是一种插入或更新的单语句方式。

    也许您会对此感兴趣:您可以使用一些“散列”函数(例如 MD5 或 SHA256)“消化”文本。摘要是固定长度。它可用于测试相等性。 (两个不同的文本哈希到相同值的可能性极低。)

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2011-02-15
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2022-11-22
      相关资源
      最近更新 更多