我相信这个问题并不能专门针对MySQL-我正在使用的数据库-,而这与最佳实践有关。
到目前为止,我的问题可以通过创建表并查询它们来解决(有时在这里和那里联接)。但是我正在做的事情感觉不对劲,每当我需要“ strong”查询旁边的非规范化数据时,就会触发我。
为了让我更好地表达自己,让我们创建一个肤浅的场景,其中:
user
可以购买product
,从而生成purchase
(让我们忽略purchase
只能有一个product
的事实); < / li>
product
d的总次数查询purchase
; 要解决用例,我们可以定义一个简单结构:
product
表:
product_id
[INT PK ] user
表:
user_id
[INT PK ] purchase
表:
purchase_id
[INT PK ] product_id
[INT FK NOT NULL] user_id
[INT FK NOT NULL] 这是感觉不对的地方:当我们需要检索product
列表及其购买总次数时,我将创建查询:< / p>
# 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_id.product_id)
FROM
product
我担心的原因是,我已经了解到COUNT会进行全表扫描,并且当我缩放到要购买的数千种产品时,即使在{{ 1}}在product_id
上按FK键(默认情况下,MySQL会这样做)。
我对关系数据库的了解还很浅,所以在比较这些问题的替代方案(合理的替代方案)时,我有点迷失了。不用说我还没有做完作业(在询问之前先搜索),我发现可以这样做:
插入新的purchase
时,它必须始终位于事务中,该事务还必须用purchase
更新product
表。
可能的问题:人为错误。有人可能会手动插入purchase.product_id
,而无需执行交易和BAM-我们存在不一致之处。
每当我插入,删除或更新某些特定表中的某行时,我都会使用新值(purchase
)更新我的product
s表。因此表将变为:
bought_amount
表:
product
[INT PK] product_id
[INT NOT NULL]; 可能的问题:触发器是否昂贵?有没有一种方法可以成功插入但触发器不会成功-从而使我前后不一致?
更新某些表以存储不断变化的数据是RDBMS的一种可行方法吗?长期加入并计数/求和其他事件是否更安全,并且从长远来看是否更有益?
我找到了关于此问题的两个有用的问题/答案,但是没有一个能从广泛的角度解决这个问题。 请考虑我对RDBMS的无知,因为我可能会建议废话可能的解决方案。
答案 0 :(得分:1)
此查询:
SELECT p.product_id,
(SELECT COUNT(*)
FROM purchase pu
WHERE pu.product_id = p.product_id
)
FROM product p;
必须同时扫描product
和purchase
。我不确定为什么您会为一个表扫描而不是另一个表感到激动。
关于性能,这可以利用purchase(product_id)
上的索引。在MySQL中,这可能比等效的(左)连接版本要快。
除非成为问题,否则您不必担心此类查询的性能。如果您需要提高这种查询的性能,首先我会问:为什么?这将返回很多信息-一直以来关于所有产品的信息。通常,我希望有人关心一种产品或一段时间,或两者兼而有之。而且,这些担忧将暗示数据集市的发展。
如果性能是一个问题,则有许多替代方法,例如:
实际上,关系数据库(具有合理的数据模型)的巨大优势对您而言并不“合适”。您可以使其保持最新状态。而且,您可以使用满足业务需求的简洁语言进行查询。
答案 1 :(得分:0)
获取每个键计数的通常方法是
SELECT product_id, COUNT(*)
FROM purchase
GROUP BY product_id
您不需要提及product
表,因为它包含的只是键列。现在,尽管它使用COUNT(*)
,但是它不需要为每个product_id
进行全表扫描,因为SQL引擎足够聪明,可以看到GROUP BY
。
但这会产生与您的查询不同的结果:对于从未购买过的product
个广告,我的查询不会显示它们;您的查询将显示计数为零的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引擎现在更加智能,因此没有必要。您可能会在较旧的教科书中看到这种过时的做法。我会忽略它,并设计您的架构尽可能标准化。
答案 2 :(得分:0)
可能的问题:人为错误。有人可能会在不进行交易和BAM的情况下手动插入购买商品-我们存在不一致之处。
->构建一个存储过程,该过程在事务中同时执行两个步骤,然后强制用户执行该过程。
可能的问题:触发器是否昂贵?有没有一种方法可以成功插入但触发器不会成功-从而使我前后不一致?
触发器不是太糟糕。但是,再次,我建议强迫用户执行执行所有所需步骤的存储过程。
注意:您可以拥有一个执行必要步骤的应用程序,而不是存储过程。然后强迫用户浏览该应用,并赋予他们 no 直接访问数据库的权限。
数据库是数据的“真理之源”。它是此类的“持久”存储库。不应将其视为构建应用程序的整个引擎。
关于性能: