【问题标题】:MySQL: Should I prefer a lot of columns or a lot of rowsMySQL:我应该喜欢很多列还是很多行
【发布时间】:2016-06-02 13:46:20
【问题描述】:

我的情况有点难以解释,但我会努力的。

比如说,我有 50 米,每个米每分钟输出一些值,我目前已经建立了一个表格,这样每分钟就有 50 行添加到每个米的表格中,如下所示:

每分钟 50 行这样:

id, datetime, meternumber, metervalue

然后我有另一个表:

id, meternumber, metername

这样我就可以加入每个仪表编号的仪表名称。基本的东西。

但是,这很快就会成为一个问题,因为每分钟 50 行是一个巨大的行数。我还必须对仪表值进行一些平均和求和,这并不容易也不是有效的,因为当我必须对 50 行中的每一行的每 50 行求和时,求和和平均值似乎不能很好地工作,因为我想对每个仪表的仪表值求和/平均。

所以我在想,我是否应该像这样制作 50 列:

id, datetime, meter_1_value, meter_2_value, ..., meter_n_value

这基本上将行数除以 50 并使求和变得容易得多,因为现在我可以对每一行求和:

id, datetime, meter_1_value_summed, meter_2_value_summed, ..., meter_n_value_summed

那么基本上将这 50 行转换为表格的列是个好主意吗?我还尝试在查询时执行此操作,首先创建一个将行转换为列的临时表,然后查询该表,但由于已经有 1500 万行,它变得越来越慢。

索引、分区和增加服务器规格并没有多大作用,所以我开始没有想法了。

我知道你们出于某种原因想查看实际的表格数据,所以这里是一个示例:

DateTime TagName Value
-------- ------- -----
2016-06-02 16:24:51 meter_1_name 66.232818603515625
2016-06-02 16:24:51 meter_2_name 42.3612060546875
2016-06-02 16:24:51 meter_3_name 25.111988067626953
2016-06-02 16:24:51 meter_4_name 4.296875
2016-06-02 16:24:51 meter_5_name NULL
2016-06-02 16:24:51 meter_6_name 3.5083911418914795
2016-06-02 16:24:51 meter_7_name 46.137149810791016
2016-06-02 16:24:51 meter_8_name 71.419265747070312
2016-06-02 16:24:51 meter_9_name 68.337669372558594
2016-06-02 16:24:51 meter_10_name 3.1090855598449707
2016-06-02 16:24:51 meter_11_name 3.0222799777984619
2016-06-02 16:24:51 meter_12_name 2.3900461196899414
2016-06-02 16:24:51 meter_13_name 44.856769561767578
2016-06-02 16:24:51 meter_14_name 64.431419372558594
2016-06-02 16:24:51 meter_15_name 34.657115936279297
2016-06-02 16:24:52 meter_1_name 66.232818603515625
2016-06-02 16:24:52 meter_2_name 42.3612060546875
2016-06-02 16:24:52 meter_3_name 25.111988067626953
2016-06-02 16:24:52 meter_4_name 4.296875
2016-06-02 16:24:52 meter_5_name NULL
2016-06-02 16:24:52 meter_6_name 3.5083911418914795
2016-06-02 16:24:52 meter_7_name 46.137149810791016
2016-06-02 16:24:52 meter_8_name 71.419265747070312
2016-06-02 16:24:52 meter_9_name 68.337669372558594
2016-06-02 16:24:52 meter_10_name 3.1090855598449707
2016-06-02 16:24:52 meter_11_name 3.0222799777984619
2016-06-02 16:24:52 meter_12_name 2.3900461196899414
2016-06-02 16:24:52 meter_13_name 44.856769561767578
2016-06-02 16:24:52 meter_14_name 64.431419372558594
2016-06-02 16:24:52 meter_15_name 34.657115936279297
2016-06-02 16:24:53 meter_1_name 66.232818603515625
2016-06-02 16:24:53 meter_2_name 42.3612060546875
2016-06-02 16:24:53 meter_3_name 25.111988067626953
2016-06-02 16:24:53 meter_4_name 4.296875
2016-06-02 16:24:53 meter_5_name NULL
2016-06-02 16:24:53 meter_6_name 3.5083911418914795
2016-06-02 16:24:53 meter_7_name 46.137149810791016
2016-06-02 16:24:53 meter_8_name 71.419265747070312
2016-06-02 16:24:53 meter_9_name 68.337669372558594
2016-06-02 16:24:53 meter_10_name 3.1090855598449707
2016-06-02 16:24:53 meter_11_name 3.0222799777984619
2016-06-02 16:24:53 meter_12_name 2.3900461196899414
2016-06-02 16:24:53 meter_13_name 44.856769561767578
2016-06-02 16:24:53 meter_14_name 64.431419372558594
2016-06-02 16:24:53 meter_15_name 34.657115936279297

我正在考虑把它变成这样:

DateTime meter_1_value meter_2_value meter_3_value meter_4_value
-------- ------------- ------------- ------------- -------------
2016-06-02 16:24:51 66.2328186035 42.36146875 21.111986762693 5.29687
2016-06-02 16:24:52 70.2328186035 43.36146875 22.111988062695 2.29685
2016-06-02 16:24:53 80.2328186035 40.36120465 23.111988762653 8.29675
2016-06-02 16:24:54 90.2328186035 49.36120685 24.111986762693 5.29875

如您所见,这样会减少很多行,并且可以更轻松地完成求和/平均值。在这种情况下,确定哪个值属于哪个仪表将不是问题。

编辑:行->列查询是hacky,看起来像这样:

DROP VIEW IF EXISTS v_temp;

CREATE OR REPLACE VIEW v_temp AS
(
    SELECT m.datatime, 

    MAX(IF(metername = 1, metervaluevalue, null)) as "meter1",
    MAX(IF(metername = 2, metervaluevalue, null)) as "meter2",
    MAX(IF(metername = 3, metervaluevalue, null)) as "meter3"

    FROM meters m

    WHERE m.datatime >= CAST("2016-05-09 00:00:00" AS DATETIME)
    AND m.datatime <= CAST("2016-05-11 23:59:00" AS DATETIME)

    GROUP BY datatime
);

SELECT datatime, 

ROUND(AVG(meter1), 0) as meter1_avg, 
ROUND(AVG(meter2), 0) as meter2_avg,
ROUND(AVG(meter3), 0) as meter3_avg

FROM v_temp

GROUP BY DATE(datatime), HOUR(datatime), MINUTE(datatime)
ORDER BY datatime ASC

【问题讨论】:

  • 我可能会保留当前的设计。
  • 在我看来,存储为更多行(即以规范化形式)的更多优势取决于设计。 stackoverflow.com/questions/9774715/…
  • 2700 万行每年听起来并不是不合理的数据量。这要运行多少年?除以 50 是对增长的微小线性变化,与系统中增加的复杂性相比,它几乎没有什么区别。我会更关注性能调优,而不是单数量级的数据缩减。
  • @David 再说一遍,2700 万而不是 2700 万 / 50 = 540k / 年?那我可以运行 27 年没有问题吗?我认为添加新仪表不是问题。我可以编写一些工具来向表中添加新列或其他东西。真正成为问题的是求和和平均。如果我把每一米都排成一排,这很难做到。我不能按米分组,因为我必须按日期时间分组才能获得例如每小时平均值。
  • 关系数据库的重点是以normalized形式存储数据。通过将行转为列,您正在对布局进行非规范化。

标签: mysql sql


【解决方案1】:

因此,基本上将这 50 行转换为 桌子?

在您的位置上,我将保留现有结构并添加一个汇总表,该表维护每个仪表的记录数以及总和。我不会费心保持平均值,因为可以很容易地从计数和总和中计算出来。

这基本上会将行数除以 50 并求和 容易多了,因为现在我可以对每一行求和:

id、日期时间、meter_1_value_summed、meter_2_value_summed、...、 meter_n_value_summed

因此,基本上将这 50 行转换为 桌子?我也试过做

我相信这不会给您带来明确的优势,因为您将进行全表扫描并计算所有 50 列。这可能会比当前的总和慢得多。

使用 Gordon 建议的索引将帮助您获得单个仪表的总和和平均值,但如果您需要对所有仪表进行求和和平均,您仍然会读取整个表格。慢。

汇总表。

我建议的汇总表是这样的

meter_number, num_records, summation.

您将在触发器的帮助下更新此表,以便计算是一个微不足道的加法。总和和平均值的检索是一个微不足道的查询,您只需要读取 50 条记录。除了summation/num_records,没有计算。

【讨论】:

  • 这确实是一个非常好看的解决方案!因此,当添加 50 个新行时,我会查询汇总表,并将每个仪表的 num_records 增加 1 并将新仪表值添加到每个仪表的总和中?而当我想要每米的平均值时,那就是summation/num_records,对吧?
  • 是的,就是这样。事实证明,如果您完全控制数据的插入方式,您甚至可能不需要触发器。
  • 完美!我可能仍然更喜欢触发器,所以我不必关心这个,无论插入可能发生在哪里。眼不见,心不烦:)我认为这个解决了问题,并且是将来要记住的一件好事-这比正常的数据库使用要先进一些。
【解决方案2】:

这可能是一个棘手的设计问题。目前的设计有一定的优势:

  • 很容易插入或删除新仪表。
  • 如果其中一个仪表不可用(由于某种原因),则数据库中缺少一行。
  • 聚合一组选定的仪表非常简单。

您提出的设计涉及对数据进行非规范化。这也有一些优点:

  • 数据的行数和大小通常较小。
  • 将数据提供给外部分析工具更容易。

1500 万行并不是特别大。而且,每秒少于一行的负载并不是数据库的大负载。您应该能够使当前版本工作——使用适当的索引和分区。

特别是,对特定仪表的值求和应该很快。所以一个查询如:

select sum(value), avg(value)
from t
where meternumber = 1;

使用(meternumber, datetime, value) 上的索引应该很快。

【讨论】:

  • 我是否应该进行多次查询以求和并平均每个仪表?目前,我有一个大规模的 hack,它首先将行转换为列,然后对行进行汇总和平均。速度很慢。
  • 我们可以看到您的查询
  • 我稍后会回答。我首先创建一个包含一些行的大测试表
  • @Piwwoli 。 . .如果你想聚合多个仪表,你只需使用group by
【解决方案3】:

如果将仪表转换为柱状格式更适合您并为您提供所需的结果,那么请务必使用它!对于任何 RDBMS 来说,拥有几百列的表都没有问题,就像拥有数百万行的表一样; MySQL 可以在任一方向扩展。关键是要使用任何方法,以最少的维护为您提供最佳结果,而柱状方法看起来是 tis 应用的一个很好的解决方案。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2010-10-16
    • 1970-01-01
    • 2012-11-06
    • 2013-12-13
    • 1970-01-01
    • 2015-06-13
    • 1970-01-01
    • 2017-04-05
    相关资源
    最近更新 更多