获取前一行数据会减慢查询速度

3qpi33ja  于 2021-06-20  发布在  Mysql
关注(0)|答案(2)|浏览(328)

我有这张table

Id | ApplianceId | Energy
1  | 101         | 0.6
2  | 103         | 1.5
3  | 107         | 1.3
4  | 101         | 1.0
5  | 101         | 1.5
6  | 103         | 2.1

我想在查询中显示的是(通过applianceid过滤),[previous]是前一个[present]值,[present]是能量值,[consumption]是present-previous:

ApplianceId | Previous | Present | Consumption
101         | 0        | 0.6     | 0.6
101         | 0.6      | 1.0     | 0.4
101         | 1.0      | 1.5     | 0.5

我已经有一个工作查询,但它太慢了(原因是在获取上一个值时)。

SELECT 
    ApplianceId, 
    COALESCE((SELECT MAX(Energy) FROM table WHERE 
    id < t.id AND ApplicationId = '101' ORDER BY id DESC LIMIT 1),0) As Previous,
    Energy AS Present,
    ROUND(MAX(m.wh_total) - (SELECT Previous),2) AS Consumption
FROM
    table t
WHERE ApplicationId = '101'
ORDER BY Id

我在10万条记录上查询这个,运行大约需要30秒。如果删除获取前一行的部分,只需要0.4秒。是否有任何方法可以优化或有其他方法获得前一行的值?

huwehgph

huwehgph1#

你可以试试下面的查询-

SELECT ApplianceId
      ,COALESCE(LAG(Energy) OVER (ORDER BY ApplianceId),0) PREVIOUS
      ,Energy PRESENT
      ,Energy - COALESCE(LAG(Energy) OVER (ORDER BY ApplianceId),0) FUTURE 
FROM T
WHERE ApplicationId = '101'
GROUP BY ApplianceId, Energy
kkih6yb8

kkih6yb82#

由于您使用的mysql没有内置的分析函数(从v5.6开始),因此您必须在查询中使用用户定义的变量来模拟它们的行为,并分阶段进行构建。
第一步是模拟由applianceid划分并按id排序的解析滞后函数,该函数包含三个变量。一个用来跟踪当前分区,一个用来跟踪先前的能量,最后一个用来存储下一个先前的能量(即当前能量)。

select ID
     , case when @pa != ApplianceID
            then @PriorEnergy := 0.0
            else @PriorEnergy := @ce
       end PriorEnergy
     , @pa := ApplianceID ApplianceID
     , @ce := energy Energy
  from table1
 order by ApplianceID, id;

在上面的查询中,case语句处理分区,这样当@pa与当前applianceid不匹配时,@priorenergy变量被设置为0.0,否则,@priorenergy变量被设置为@ce的当前值,该变量保存来自上一条记录的能量值。在case语句之后,@pa和@ce从当前记录更新为保持状态。上述查询中的order by对于确保分区正常工作非常重要。
下一步是将上述查询用作更大查询的一部分,以生成最终输出:

select ApplianceID
     , PriorEnergy Previous
     , Energy Present
     , Energy - PriorEnergy Consumption
from (
select ID
     , case when @pa != ApplianceID
            then @PriorEnergy := 0.0
            else @PriorEnergy := @ce
       end PriorEnergy
     , @pa := ApplianceID ApplianceID
     , @ce := Energy Energy
  from table1
  order by ApplianceID, ID) aq1
order by ApplianceID, id;

您可以在这个sql fiddle中看到上面两个查询的结果。此外,fiddle中还有第三个查询,显示如何作为第一个分析查询的一部分来计算消耗量,但是如果不考虑使分析处理工作所需的列顺序约束,则投影不满足您的要求。

相关问题