什么是更高效(就查询性能而言)的数据库表设计 - 长表还是宽表?
即,这个
id size price
1 S 12.4
1 M 23.1
1 L 33.3
2 S 3.3
2 M 5.3
2 L 11.0
与此相对
id S M L
1 12.4 23.1 33.3
2 3.3 5.3 11.0
通常(我认为)它归结为 GROUP BY
和直接选择列之间的性能比较:
SELECT AVG(price) FROM table GROUP BY size
或
SELECT AVG(S), AVG(M), AVG(L) FROM table
第二个写的有点长(就很多栏目而言),但是两者的性能如何呢?如果可能,每种表格格式的一般优点/缺点是什么?
最佳答案
首先,这是两种适用于不同目的的不同数据模型。
话虽如此,我预计1第二个模型的聚合速度会更快,因为数据打包得更紧凑,因此需要更少的 I/O:
- 第一个模型中的 GROUP BY 可以通过对索引
{size, price}
进行完整 扫描来满足。当数据太大而无法放入 RAM 时,替代索引的速度太慢。 - 第二个模型中的查询可以通过全表扫描来满足。不需要索引2。
由于第一种方法需要表+索引,而第二种方法只需要表,因此第二种情况下缓存利用率更高。即使我们忽略缓存并将第一个模型中的索引(没有表)与第二个模型中的表进行比较,我怀疑索引会比表大,仅仅是因为它物理上记录了 size
并且具有 B 树典型的未使用“孔”(尽管对于表也是如此,如果它是 clustered )。
最后,第二种模型没有索引维护开销,这可能会影响 INSERT/UPDATE/DELETE 性能。
除此之外,您可以考虑将 SUM 和 COUNT 缓存在仅包含一行的单独表中。每当在主表中插入、更新或删除行时,都会通过触发器更新 SUM 和 COUNT。然后,您只需将 SUM 和 COUNT 相除即可轻松获得当前的 AVG。
1 但您确实应该测量有代表性的数据量才能确定。
2 由于您的查询中没有 WHERE 子句,因此将扫描所有行。索引仅对获取相对较小的表行子集有用(有时用于 index-only scans )。作为一个粗略的经验法则,如果需要表中超过 10% 的行,索引将无济于事,即使索引可用,DBMS 通常也会选择全表扫描。
关于MySQL:长表与宽表,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/16026226/