【问题标题】:How to calculate power consumption from power records?如何根据电量记录计算耗电量?
【发布时间】:2022-03-04 21:03:07
【问题描述】:

我有一个表格,其中包含设备的功率值 (kW)。每分钟从每个设备读取一次值,并插入带有时间戳的表中。我需要做的是计算给定时间跨度的功耗(kWh)并返回 10 个最耗电的设备。现在我查询给定时间跨度的结果,并在后端循环所有记录中进行计算。这适用于少量设备和短时间跨度,但在实际用例中我可以拥有数千台设备和长时间跨度。

所以我的问题是如何在 PostgreSQL 9.4.4 中完成所有这些操作,以便我的查询仅返回 10 个最耗电的(device_id、power_consumption)对?

示例表:

CREATE TABLE measurements (
  id         serial primary key,
  device_id  integer,
  power      real,
  created_at timestamp
);

简单数据示例:

| id | device_id | power |               created_at |
|----|-----------|-------|--------------------------|
|  1 |         1 |    10 | August, 26 2015 08:23:25 |
|  2 |         1 |    13 | August, 26 2015 08:24:25 |
|  3 |         1 |    12 | August, 26 2015 08:25:25 |
|  4 |         2 |   103 | August, 26 2015 08:23:25 |
|  5 |         2 |   134 | August, 26 2015 08:24:25 |
|  6 |         2 |     2 | August, 26 2015 08:25:25 |
|  7 |         3 |    10 | August, 26 2015 08:23:25 |
|  8 |         3 |    13 | August, 26 2015 08:24:25 |
|  9 |         3 |    20 | August, 26 2015 08:25:25 |

想要查询的结果:

| id | device_id | power_consumption |
|----|-----------|-------------------|
|  1 |         1 |              24.0 |
|  2 |         2 |             186.5 |
|  3 |         3 |              28.0 |

我如何计算 kWh 值的简化示例(created_at in hours):

data = [
    [
        { 'id': 1, 'device_id': 1, 'power': 10.0, 'created_at': 0 },
        { 'id': 2, 'device_id': 1, 'power': 13.0, 'created_at': 1 },
        { 'id': 3, 'device_id': 1, 'power': 12.0, 'created_at': 2 }
    ],
    [
        { 'id': 4, 'device_id': 2, 'power': 103.0, 'created_at': 0 },
        { 'id': 5, 'device_id': 2, 'power': 134.0, 'created_at': 1 },
        { 'id': 6, 'device_id': 2, 'power': 2.0, 'created_at': 2 }
    ],
    [
        { 'id': 7, 'device_id': 3, 'power': 10.0, 'created_at': 0 },
        { 'id': 8, 'device_id': 3, 'power': 13.0, 'created_at': 1 },
        { 'id': 9, 'device_id': 3, 'power': 20.0, 'created_at': 2 }
    ]
]

# device_id: power_consumption
results = { 1: 0, 2: 0, 3: 0 }

for d in data:
    for i in range(0, len(d)):
        if i < len(d)-1:
            # Area between two records gives us kWh
            # X-axis is time(h)
            # Y-axis is power(kW)
            x1 = d[i]['created_at']
            x2 = d[i+1]['created_at']
            y1 = d[i]['power']
            y2 = d[i+1]['power']
            # Area between two records gives us kWh
            # X-axis is time(h)
            # Y-axis is power(kW)
            x1 = d[i]['created_at']
            x2 = d[i+1]['created_at']
            y1 = d[i]['power']
            y2 = d[i+1]['power']

            results[d[i]['device_id']] += ((x2-x1)*(y2+y1))/2

print results

编辑:检查this 看看我最终是如何解决这个问题的。

【问题讨论】:

  • 我认为你的消费可以更简单地计算为 (x2 - x1) * (y1 + y2) / 2
  • @DavidAldridge 真的谢谢!
  • 您知道抽样方法永远不会非常准确,对吧?因为它会错过尖峰负载,受到采样时间引起的错误等影响?

标签: sql postgresql


【解决方案1】:

为了做到这一点,您需要的一些元素是:

  1. Sum() 聚合,用于计算记录总数
  2. Lag()/Lead() 函数,用于计算给定记录的“上一个”或“下一个”记录的值。

因此,对于给定的行,您可以在哪里获取当前 created_at 和 power 记录,在 SQL 中,您可能会使用 Lead() 窗口函数来获取 created_at 和 power 记录,以获取具有相同设备 ID 的记录created_at 的下一个最高值。

Lead() 的文档在这里:http://www.postgresql.org/docs/9.4/static/functions-window.html

当您通过引用“下一个”记录计算每一行的功耗时,您可以使用 Sum() 来汇总该设备的所有计算功率。

当您计算出每台设备的功率后,您可以使用 ORDER BY 和 LIMIT 来选择耗电前 n 的设备。

要遵循的步骤,如果您没有信心投入并只编写最终的 SQL - 在每个步骤之后确保您有您理解的 SQL,并且它只返回您需要的数据:

  1. 从小处着手,选择所需的数据行。
  2. 计算 Lead() 函数,定义适当的分区和顺序子句以获取下一行。
  3. 添加每行功率的计算。
  4. 定义 Sum() 函数,并按设备 ID 分组。
  5. 添加 ORDER BY 和 LIMIT 子句。

如果您在这些步骤中的任何一个步骤中遇到问题,他们都会提出一个不错的 StackOverflow 问题。

【讨论】:

  • 是的,这也是我通过阅读 postgresql 文档得出的结论,但我现在知道如何在一个查询中实现这一切。你能举个例子吗?
【解决方案2】:

如果有人碰巧想知道同样的事情,我是如何解决这个问题的。
我按照大卫的指示做了这个:

SELECT
  t.device_id,
  sum(len_y*(extract(epoch from date_trunc('milliseconds', len_x)))/7200) AS total
FROM (
    SELECT
      m.id,
      m.device_id,
      m.power,
      m.created_at,
      m.power+lag(m.power) OVER (
        PARTITION BY device_id
        ORDER BY m.created_at
      ) AS len_y,
      m.created_at-lag(m.created_at) OVER (
        PARTITION BY device_id
        ORDER BY m.created_at
      ) AS len_x
    FROM
      mes AS m
  WHERE m.created_at BETWEEN '2015-08-26 13:39:57.834674'::timestamp
    AND '2015-08-26 13:43:57.834674'::timestamp
) AS t
GROUP BY t.device_id
ORDER BY total
DESC LIMIT 10;

【讨论】:

  • 虽然“.../60/60)/2”对程序员来说更具描述性,但您可能希望在生产中简单地使用“.../7200”,因为每次可以节省 2 次操作线并且可能对非常大的数据集产生显着影响。当然,您应该在评论中注明 7200 的含义。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2012-09-16
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多