MySQL:我应该更喜欢很多列还是很多行

时间:2016-06-02 13:46:20

标签: mysql sql

我的情况很难解释,但我会尝试。

我说,我有50米,每一分钟都输出一些值,我现在已经建了一个像这样的桌子,每分钟有50行添加到每个米的表中,如下所示:

每分钟50行:

id, datetime, meternumber, metervalue

然后我有另一张表:

id, meternumber, metername

这样我就可以为每个meternumber加入meternames。基本的东西。

然而,这很快成为一个问题,因为每分钟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

正如您所看到的那样,行数会减少很多,而且总和/平均值可以通过这种方式更轻松地完成。在这种情况下确定哪个值属于哪个仪表不会有问题。

编辑:row->列查询很糟糕,如下所示:

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

3 个答案:

答案 0 :(得分:1)

  

基本上将这50行转换为列是个好主意   桌子?

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

  

这基本上将行数除以50并进行求和   更容易,因为现在我可以将每一行加起来得到:

     

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

     

基本上将这50行转换为列是个好主意   桌子?我也尝试过做

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

使用Gordon建议的索引将帮助您获得单个仪表的总和和平均值,但如果您需要对所有仪表求和并求平均值,您仍将阅读全表。慢。

摘要表。

我建议的汇总表就是这样的

meter_number, num_records, summation.

您将使用触发器更新此表,以便计算是一项微不足道的补充。检索总和和平均值是一个简单的查询,您只需要读取50条记录。除了summation/num_records之外没有计算。

答案 1 :(得分:0)

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

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

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

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

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

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

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

(meternumber, datetime, value)上的索引应该非常快。

答案 2 :(得分:0)

如果将仪表转换为柱状格式对你来说效果更好,并为你提供所需的结果,那么一定要去吧!具有几百列的表对于任何RDBMS都没有问题,只有具有数百万行的表; MySQL可以在任何一个方向上扩展。关键是使用任何方法以最少的维护为您提供最佳结果,柱状方法看起来是一个很好的解决方案。