我认为这个问题并没有专门针对 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/