mysql - 关系数据库 (RDBMS) 非规范化数据

标签 mysql database-design relational-database query-optimization

我认为这个问题并没有专门针对 MySQL(我正在使用的数据库),而是一个关于最佳实践的问题。

到目前为止,我的问题可以通过创建表并查询它们来解决(有时在这里和那里加入)。但是我正在做的一些事情感觉不对,每当我需要一个非规范化数据以及我的“常见”查询时,它就会触发我。

示例用例

为了更好地表达自己,让我们创建一个肤浅的场景:

  • 一个 user 可以买一个 product ,生成一个 purchase (让我们忽略 purchase 只能有一个 product 的事实);
  • 并且我们需要查询 product s 以及它已经出现 purchase d 的总次数;

为了解决我们的用例,我们可以定义一个简单的结构,由:

  • product 表:

    • product_id [INT PK]
  • user 表:

    • user_id [INT PK]
  • purchase 表:

    • purchase_id [INT PK]
    • product_id [INT FK 不为空]
    • user_id [INT FK 不为空]

这里感觉不对:当我们需要检索 product 的列表及其已购买的总次数时,我会创建查询:

# There are probably faster queries than this to reach the same output
SELECT
    product.product_id,
    (SELECT COUNT(*) FROM purchase
      WHERE purchase.product_id = product.product_id)
FROM
    product

我的担心来源是我读到 COUNT 执行全表扫描,当扩展到数千种正在购买的产品时,它让我害怕执行上面的查询 - 即使我已经用 product_id FK 创建了一个 INDEX在 purchase 上(MySQL 默认执行此操作)。


可能的解决方案

我对关系数据库的了解很浅,所以在比较这些问题的替代方案(可能的方案)时,我有点不知所措。并不是说我没有做功课(问之前先搜索),我发现有理由:

创建交易:

INSERT插入新的 purchase 时,它必须始终在事务内,该事务还使用 product 更新 purchase.product_id 表。

可能的问题:人为错误。有人可能会在不执行事务和 BAM 的情况下手动插入 purchase - 我们存在不一致。

创建触发器:

每当我在某个特定表中插入、删除或更新某些行时,我都会用新值 (product) 更新我的 bought_amount 表。所以表格会变成:

  • product 表:
    • product_id [INT PK]
    • bought_amount [INT NOT NULL];

可能出现的问题:触发器是不是很贵?有没有一种方法可以使插入成功但触发器不会 - 从而使我感到不一致?


问题

更新某些表以存储不断变化的数据是 RDBMS 的合理方法吗?是否更安全并且 - 从长远来看 - 只是继续加入并计算/总结其他事件是否更有益?

我发现了几个关于这个问题的有用问题/答案,但没有一个从广泛的角度解决这个问题。 请考虑到我对 RDBMS 的无知,因为我可能会提出一些废话可能的解决方案

最佳答案

获取每个键的计数的常用方法是

SELECT product_id, COUNT(*)
FROM purchase
GROUP BY product_id

您无需提及product 表,因为它只包含键列。现在虽然它使用了 COUNT(*),但它不需要对每个 product_id 进行全表扫描,因为 SQL 引擎足够智能,可以看到 GROUP BY

但这会产生与您的查询不同的结果:对于从未购买过的产品,我的查询根本不会显示它们;您的查询将显示计数为零的 product_id

那么在您开始担心实现和效率之前,您想要回答什么问题?如果您想查看所有 product 是否已购买,则必须扫描整个 product 表并从中查找 purchase。我会去

SELECT product_id, count
FROM product
OUTER JOIN (SELECT product_id, COUNT(*) AS count
            FROM purchase
            GROUP BY product_id) AS purch
ON product.product_id = purch.product_id

关于您的更广泛的问题(不确定我是否完全理解它们),在早期,SQL 在这种连接和聚合方面效率很低,并且模式通常在多个表中使用重复的列进行非规范化。 SQL 引擎现在更加智能,因此没有必要。您可能会在较旧的教科书中看到这种老式的做法。我会忽略它并尽可能规范地设计您的架构。

关于mysql - 关系数据库 (RDBMS) 非规范化数据,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/56411374/

相关文章:

database - Hibernate,将多个列分配为主键

database - 当多值主键的成员属性是函数依赖的决定因素时,多值主键本身是决定因素吗?

php - CodeIgniter:将数据从模型传递到 Controller

mysql - 我想选择批处理 ID,其中包含从 SQL 表中提供的两个选定类(class)

mysql - 在整个数据库而不是仅在一个表中拥有唯一行 ID 的最佳方法是什么?

php - 在数据库中添加类别和子类别

java - spring hibernate实现一对一关系

mysql - 检查两个时间是否重叠(如果它们在同一天)

php - 随机结果。 SQL 查询和平面文件哪种方式更快?

php - 如何在 php 中获取超过 x 行的特定列值?