【问题标题】:When to use comma-separated values in a DB Column?何时在 DB 列中使用逗号分隔值?
【发布时间】:2011-11-23 00:44:54
【问题描述】:

好的,我知道技术答案是NEVER

但是,有时它似乎用更少的代码和看似很少的缺点让事情变得如此简单,所以请告诉我。

我需要构建一个名为 Restrictions 的表来跟踪人们希望联系的用户类型,并且该表将包含以下 3 列(为简单起见):

minAge
lookingFor
drugs

lookingFordrugs 可以包含多个值。

数据库理论告诉我应该使用连接表来跟踪用户可能为其中任一列选择的多个值。

但似乎使用逗号分隔值使事情更容易实现和执行。这是一个例子:

假设 用户 1 有以下限制:

minAge => 18
lookingFor => 'Hang Out','Friendship'
drugs => 'Marijuana','Acid'

现在假设 用户 2 想要联系 用户 1。好吧,首先我们需要看看他是否符合用户 1 的限制,但这很容易,即使使用逗号分隔的列,如下所示:

首先我会得到目标(用户 1)的限制:

SELECT * FROM Restrictions WHERE UserID = 1

现在我只是将它们放入各自的变量中按原样放入 PHP:

$targetMinAge = $row['minAge'];
$targetLookingFor = $row['lookingFor'];
$targetDrugs = $row['drugs'];

现在我们只检查 SENDER(用户 2)是否符合这个简单的条件:

COUNT (*) 
   FROM Users
WHERE 
   Users.UserID = 2 AND
   Users.minAge >= $targetMinAge AND
   Users.lookingFor IN ($targetLookingFor) AND
   Users.drugs IN ($targetDrugs)

最后,如果 COUNT == 1,用户 2 可以联系 用户 1,否则他们不能。

有多简单?它看起来真的很简单直接,那么只要每次用户更新他们的联系限制时我都会清理数据库的所有输入,那么这样做的真正问题是什么?能够使用 MySQL 的 IN 函数并且已经以它可以理解的格式存储多个值(例如逗号分隔值)似乎比必须为每个多选列创建连接表要容易得多。我举了一个简化的例子,但是如果有 10 个多项选择列呢?如此多的连接表导致事情开始变得混乱,而 CSV 方法则保持简单。

那么,在这种情况下,如果我使用逗号分隔值真的那么糟糕吗?

****鸭子****

【问题讨论】:

  • 似乎看起来非常简单明了,而且确实可能​​如此。另一种解决方案(有许多 JOIN)可能看起来很困难且不简单——实际上它可能很困难且不简单。但它具有链接(您已经阅读过)描述的所有其他优势,例如速度、完整性执行等。
  • 我的建议是:再次阅读该链接。
  • 基本经验法则:如果您需要访问逗号分隔列表的任何单个部分,请不要使用逗号分隔列表。这样做首先破坏了使用关系数据库的意义。您已将数据呈现为非关系型,因此现在数据库已缩减为一个非常大/昂贵的哑存储系统。
  • 我真的不明白Users.lookingFor IN ($targetLookingFor) 将如何匹配用户A 的'Hang Out,Friendship' 和用户B 的'Friendship,Football'
  • @Programmer:当然,你有一个选择——你可以跳过做正确的事情来完成工作,现在它可以工作了。但是,如果该站点值得该死的话,您稍后会投入时间,因为用户会想要更多的东西。比如说,推荐的联系人。为了实现它,您将不得不围绕数据库的限制编写一些代码。到最后,您将用 PHP 编写一个半笨的数据库引擎,并且只是将 MySQL 用作相对昂贵的原始数据存储。相信我,我去过那里。一点都不好玩。

标签: php mysql database database-design normalization


【解决方案1】:

你已经知道答案了。

首先,您的 PHP 代码甚至无法正常工作,因为它仅在用户 2 在 LookFor 或 Drugs 中只有一个值时才有效。如果这些列中的任何一列包含多个以逗号分隔的值,则 IN 将不起作用即使这些值与用户 1 的值的顺序完全相同。如果 右侧 有一个或多个逗号,那么 IN 会做什么?

因此,在 PHP 中做你想做的事情并不“容易”。这实际上很痛苦,需要将用户 2 的字段拆分为单个值,编写具有许多 OR 的动态 SQL 来进行比较,然后执行极其低效的查询来获取结果。

此外,您甚至需要编写 PHP 代码 来回答这样一个关于两组交集的相对简单的问题,这意味着您的设计存在严重缺陷。这正是 SQL 用来解决的那种问题(关系代数)。一个正确的设计可以让您解决数据库中的问题,然后简单地在 PHP 或其他技术中实现一个表示层。

做对了,你会轻松很多。

【讨论】:

  • 假设我将始终使用 PHP,当它只是几行代码同时使开发方面的事情变得如此容易时,这仍然是一个大问题吗?毕竟,这些列只会用于单向检查(使用 MySQL 的 IN 函数检查 SINGLE 值和 MULTIPLE 值)。
  • 即使您给出的示例也不是这样,除非用户 1 永远不允许尝试联系任何人。该记录已经在两列中都有多个值
  • 我觉得我表达的不是很清楚,每个用户在主“用户”表上都有自己的“寻找”和“药物”列。因此,如果用户 1 要联系某人,我只需检查用户 1 在主用户表中的列(仅包含 SINGLE 值),对照“限制”表中的目标多值列,这可以使用 MySQL 轻松完成IN 函数。
  • 如果您愿意将自己限制在您提出的单一要求中,您将能够以合理的性能方式解决您的问题(性能不如正确的方式,但您赢了不要大受打击)。但是您将无法回答您最终想要查看的任何其他问题(例如,“为用户推荐合适的联系人”)。做出一个糟糕的设计决定,因为它会让你的应用程序超过 0.1 版,这将花费你大量的时间和金钱来修复。
【解决方案2】:

假设用户 1 正在寻找“Hang Out”、“Friendship”,而用户 2 正在寻找“Friendship”、“Hang Out”

您的代码无法匹配它们,因为 'Friendship','Hang Out' 不在 ('Hang Out','Friendship')

这才是真正的问题。

【讨论】:

  • 我真的不认为这是一个问题,因为我从不检查多个值与多个值,我总是检查一个值与多个值,这可以通过 MySQL 的 IN 轻松完成功能。我应该考虑其他任何主要缺点吗?
  • 事实是,你不能这么说。您自己说过,lookingFor 列可以包含多个值。如果您的变量是单个值,则意味着 IN 被反转。如果你把它翻转到$targetLookingFor IN Users.lookingFor,那么你只是强制进行表扫描(阅读:杀死性能),因为你不能再使用索引了。
  • @cHao:我觉得我表达的不是很清楚,每个用户在主“用户”表上都有自己的“寻找”和“药物”列。因此,如果用户要联系某人,我只需检查主用户表中用户 1 的列(仅包含单个值),对照“限制”表中的目标多值列,这可以使用 MySQL 轻松完成IN 函数。
  • 所以,等等...您正在阅读用户 1 的限制,然后查询他是否与用户 2 匹配?即使用户 2 是登录的人,因此您已经拥有谁的信息?哦……这整件事对我来说听起来倒退了。
  • @cHao:我需要以一种或另一种方式阅读目标限制,这不会改变。我想知道的是,考虑到查询将始终是单向检查单个值是否在 CSV 中,将多值限制作为 CSV 列存储在单个限制数据库中是否真的很糟糕......我仍然没有得到直接的答案,因为每个人都认为我会针对 CSV 检查进行 CSV,但事实并非如此。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2015-08-24
  • 2019-01-11
  • 1970-01-01
  • 1970-01-01
  • 2013-07-03
相关资源
最近更新 更多