【问题标题】:MySQL - Select substring from a column, without catching similar substrings from same columnMySQL - 从列中选择子字符串,而不从同一列中捕获类似的子字符串
【发布时间】:2019-02-02 02:31:14
【问题描述】:

在一个 MySQL 表中,我有一个名为 ShareID 的 VARCHAR 列。

  • 如果第 1 行的 ShareID 值包含 1 形式的字符串
  • 并且第 2 行的 ShareID 值包含 10, 1
  • 形式的字符串
  • 第 3 行的 ShareID 值包含 111, 12 形式的字符串。

我想获取 ShareID 为 1 的所有行。即这里只有第一行和第二行。

我尝试过使用 LIKE 命令,如下所示:

SELECT * FROM tablename WHERE ShareWithID LIKE '1%';

但这会捕获其中包含数字 1 的所有行,即第 3 行,这不是我想要的。

我想运行一个只返回上面第 1 行和第 2 行的命令,因为它们的 ShareID 为 1

我尝试了各种命令(包括 REGEXP 和 IN)并管理了一个“frig”解决方案,我会在 ShareID 列中的每个数字之后放置一个逗号,包括最后一个(即 10, 1,),然后执行这个命令:

SELECT * FROM tablename WHERE ShareWithID LIKE '%1,%';

但我宁愿使用适当的解决方案,而不是一个被操纵的解决方案。

欢迎任何指导。

【问题讨论】:

    标签: mysql sql select substring


    【解决方案1】:

    您不应该将数字列表存储在逗号分隔的字符串中。这是一个非常糟糕的主意:

    • 数字应存储为数字,而不是字符串。
    • 您的号码似乎是 id。 ID 应明确定义外键。
    • SQL——一般来说——具有糟糕的字符串处理功能。
    • SQL 无法优化带有字符串操作的查询。
    • SQL 有一种存储列表的好方法。它被称为

    但有时,我们在设计数据库时遇到了非常、非常、非常、非常糟糕的决定。 MySQL 针对这种情况有一个方便的功能:

    where find_in_set(1, ShareWithID) > 0
    

    如果字符串中有空格,则需要将其删除:

    where find_in_set(1, replace(ShareWithID, ' ', '')) > 0
    

    【讨论】:

    • 是的,我知道 MySQL 缺乏字符串处理能力,但有时字符串操作是更好的方法。当原始“表”中的一列也可以完成这项工作时,它不必浪费时间构建一个 250,000 行的“表”(是吗??)来处理几列 ID。只要使用正确的“内置”MySQL 命令来操作字符串。所以感谢您引导我使用我不知道的“find_in_set”命令。这就是为什么像 stackoverflow 这样的门户网站如此出色的原因,但是,有一个基本的细节需要解决 - 如下所述。
    【解决方案2】:

    ...内置功能可以使用

    FIND_IN_SET() 实际上并不打算用于包含逗号分隔列表的字符串。它旨在与 MySQL 的 SET data type 一起使用。因此名称为FIND_IN_SET(),而不是FIND_IN_COMMA_SEPARATED_LIST()

    当原始“表”中的一列也可以完成这项工作时,它节省了构建 250,000 行“表”(是吗??)来处理几列 ID 的时间。

    250k 行对 MySQL 来说不是问题。我在给定表中管理具有 十亿 行的数据库。如果您使用索引进行基本查询优化,那么对 250k 行的表的大多数查询都可以。

    使用逗号分隔的列表会破坏优化查询的机会。索引无助于搜索可能不是字符串最左边前缀的子字符串,在逗号分隔的列表中搜索数字基本上是在搜索子字符串。

    使用逗号分隔的列表使您的查询无法优化。使用FIND_IN_SET() 的每个查询都将是一次表扫描,它与表中的行数呈线性关系会变慢。

    除了索引之外,使用逗号分隔的列表还有其他缺点,我在对这篇旧帖子的回答中写道:Is storing a delimited list in a database column really that bad?

    我宁愿使用适当的解决方案,而不是使用冷门的解决方案。

    然后每行存储一个 id。在关系数据库中,这是正确的解决方案。

    【讨论】:

    • 感谢比尔的解释和旧帖子的链接。我确实很欣赏在字符串中存储数字通常是一个很大的禁忌,但在我的情况下,它非常适合我打算使用它的有限数量的 I/O。我从来没有在我的数据库中使用过索引,只是因为我还不知道如何使用。似乎正确的方法是如果所有 250,000 行都需要 SharedWithID 值的关联列,我将需要一个具有相关列数的新的 250,000 行表。我现在知道了两种解决问题的方法,尽管其中一种似乎比另一种更被专业接受。
    • 您可能会喜欢我的演示文稿How to Design Indexes, Really,或video
    • 感谢 Bill 的演示链接。它们在我的 MySQL 教程文件夹中。另外,我决定放弃“字符串”存储方法,走“整数”路线。我会在需要时动态地将额外的列添加到主表中。不应有超过 5 个附加列。但是,如果这个数字大幅上升,我将创建新表。
    • 所以当你需要找到一个特定的数字时,你必须搜索5列,因为你不知道你搜索的数字存储在哪一列。这意味着你仍然可以' t 使用索引。我告诉你这是一个已解决的问题 — 创建一个子表并将每行的一个 id 存储在 一列中。 然后你可以索引该一列并搜索任何有效地创造价值。
    • 我想起了温斯顿·丘吉尔的名言:“你总是可以指望美国人做正确的事——在他们尝试了其他一切之后。”
    【解决方案3】:

    解决这个问题的方法是使用 Gordon Linoff 对 FIND_IN_SET 命令的建议,并结合问题表列的正确配置,像这样:

    SELECT * FROM tablename WHERE FIND_IN_SET('1', ShareWithID);
    

    但是,由于 FIND_IN_SET 命令允许您在 逗号分隔 字符串列表中查找字符串的位置,因此您必须确保列的内容每个项目后包含一个逗号,并且逗号后不包含空格。

    所以与上述命令结合使用的这一列内容将返回 '0' 行:111, 1

    虽然此列内容将返回“1”行:111,1

    这个也一样:33,1

    还有这个:44,1,415

    【讨论】:

    • 听起来FIND_IN_SET()毕竟不是一个好的解决方案。
    • 为什么是比尔·卡尔文?结合正确的列配置,它对我来说效果很好。它可能不是经典的编码,但内置的功能是可以使用的。
    • 评论太多,无法回答。我会写一个答案。
    猜你喜欢
    • 2021-12-10
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2012-01-12
    • 2014-10-24
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多