当前位置: 首页 > 面试题库 >

如何使用窗口函数优化SQL查询

张锐藻
2023-03-14
问题内容

。我有包含设备功耗值的表,我需要计算给定时间跨度的功耗并返回10个最耗电的设备。我已生成192个设备和7742208个测量记录(每个记录为40324)。这大约是设备在一个月内会产生多少记录。

对于如此大量的数据,我当前的查询要花40多个秒才能执行,这太长了,因为时间跨度,设备和测量的数量可能更高。我是否应该尝试使用不同于lag()OVER
PARTITION的方法来解决此问题,还可以进行其他哪些优化?我非常感谢代码示例的建议。

PostgreSQL 9.4版

用示例值查询:

SELECT
  t.device_id,
  sum(len_y*(extract(epoch from len_x))) AS total_consumption
FROM (
    SELECT
      m.id,
      m.device_id,
      m.power_total,
      m.created_at,
      m.power_total+lag(m.power_total) 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
      measurements AS m
  WHERE m.created_at BETWEEN '2015-07-30 13:05:24.403552+00'::timestamp
    AND '2015-08-27 12:34:59.826837+00'::timestamp
) AS t
GROUP BY t.device_id
ORDER BY total_consumption
DESC LIMIT 10;

表信息:

    Column    |           Type           |                         Modifiers
--------------+--------------------------+----------------------------------------------------------
 id           | integer                  | not null default nextval('measurements_id_seq'::regclass)
 created_at   | timestamp with time zone | default timezone('utc'::text, now())
 power_total  | real                     |
 device_id    | integer                  | not null
Indexes:
    "measurements_pkey" PRIMARY KEY, btree (id)
    "measurements_device_id_idx" btree (device_id)
    "measurements_created_at_idx" btree (created_at)
Foreign-key constraints:
    "measurements_device_id_fkey" FOREIGN KEY (device_id) REFERENCES devices(id)

查询计划:

Limit  (cost=1317403.25..1317403.27 rows=10 width=24) (actual time=41077.091..41077.094 rows=10 loops=1)
->  Sort  (cost=1317403.25..1317403.73 rows=192 width=24) (actual time=41077.089..41077.092 rows=10 loops=1)
Sort Key: (sum((((m.power_total + lag(m.power_total) OVER (?))) * date_part('epoch'::text, ((m.created_at - lag(m.created_at) OVER (?)))))))
Sort Method: top-N heapsort  Memory: 25kB
->  GroupAggregate  (cost=1041700.67..1317399.10 rows=192 width=24) (actual time=25361.013..41076.562 rows=192 loops=1)
Group Key: m.device_id
->  WindowAgg  (cost=1041700.67..1201314.44 rows=5804137 width=20) (actual time=25291.797..37839.727 rows=7742208 loops=1)
->  Sort  (cost=1041700.67..1056211.02 rows=5804137 width=20) (actual time=25291.746..30699.993 rows=7742208 loops=1)
Sort Key: m.device_id, m.created_at
Sort Method: external merge  Disk: 257344kB
->  Seq Scan on measurements m  (cost=0.00..151582.05 rows=5804137 width=20) (actual time=0.333..5112.851 rows=7742208 loops=1)
Filter: ((created_at >= '2015-07-30 13:05:24.403552'::timestamp without time zone) AND (created_at <= '2015-08-27 12:34:59.826837'::timestamp without time zone))

Planning time: 0.351 ms
Execution time: 41114.883 ms

查询以生成测试表和数据:

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

INSERT INTO measurements(
    device_id,
    created_at,
    power_total
  )
SELECT
  device_id,
  now() + (i * interval '1 minute'),
  random()*(50-1)+1
FROM (
  SELECT
    DISTINCT(device_id),
    generate_series(0,10) AS i
 FROM (
  SELECT
    generate_series(1,5) AS device_id
  ) AS dev_ids
) AS gen_table;

问题答案:

我将尝试将部分计算移至行插入阶段。

添加新列:

alter table measurements add consumption real;

更新列:

with m1 as (
    select
        id, power_total, created_at,
        lag(power_total) over (partition by device_id order by created_at) prev_power_total,
        lag(created_at) over (partition by device_id order by created_at) prev_created_at
    from measurements
    )
update measurements m2
set consumption = 
    (m1.power_total+ m1.prev_power_total)*
    extract(epoch from m1.created_at- m1.prev_created_at)
from m1
where m2.id = m1.id;

创建触发器:

create or replace function before_insert_on_measurements()
returns trigger language plpgsql
as $$
declare
    rec record;
begin
    select power_total, created_at into rec
    from measurements
    where device_id = new.device_id
    order by created_at desc
    limit 1;
    new.consumption:= 
        (new.power_total+ rec.power_total)*
        extract(epoch from new.created_at- rec.created_at);
    return new;
end $$;

create trigger before_insert_on_measurements
before insert on measurements
for each row execute procedure before_insert_on_measurements();

查询:

select device_id, sum(consumption) total_consumption
from measurements
-- where conditions
group by 1
order by 1


 类似资料:
  • 我有一份flink工作,需要在1小时内重复删除收到的记录。重复数据消除后,我需要收集所有这些重复数据消除的文档,并进行一些聚合,如计数,然后生成目标主题。 现在,由于我只需要收集那些重复数据消除的文档,所以可能不需要等待1小时。我如何避免仅为收集这些文档而设置1个小时的窗口,但一旦收集到这些文档,就继续进行聚合。 因此,资源会占用内存,检查点大小也在增加,这是我想要避免的。 水印策略: 如有任何建

  • 问题内容: 我有一个查询,使用带通配符的“ like”来搜索客户端。例如: 它还可以在“ where”子句中使用较少的参数,例如: 谁能说出优化这种查询性能的最佳方法是什么?也许我需要创建一个索引?该表在生产中最多可以有1000K条记录。 问题答案: 要在模式具有表单的位置上做很多事情,您需要查找SQL Server的全文本索引功能,并使用代替。照原样,您正在执行全表扫描,因为普通索引对搜索以通配

  • 问题内容: 我有一个涉及Postgresql数据库的任务。我对SQL不太有经验。 我有一张桌子,上面有每周贸易产品的营业额。 对于每周,提供以下信息:产品,周数,每周营业额(可能是正数,也可能是负数,具体取决于天气是购买还是出售了更多产品)。我已经添加了一个列,每个星期都有期末余额。我在表中的第一周所有产品的期末余额(week_number= 0),而其他所有周均为“ null”。下面提供了一些示

  • 问题内容: 在这里很难说出要问什么。这个问题是模棱两可,含糊不清,不完整,过于宽泛或夸张的,不能以目前的形式合理地回答。如需帮助澄清此问题以便可以重新打开, 请访问帮助中心。 8年前关闭。 有一个查询运行得比我想要的慢,但是我无法共享详细信息。该查询已经返回了正确的结果,并且已经对其进行了重构,但是我无法使其足够快地运行。谓词在可能的情况下已经是可Sarg- able 的。它已经正确地使用了联接,

  • 问题内容: 每当用户接近文档顶部时,我都会尝试向下滚动100px。 当用户接近文档顶部时,我执行了该函数,但是.scrollTo函数不起作用。 我在之前和之后放置了一个警报,以检查它实际上是否是阻止该行的线路,并且只有第一个警报响起,这是代码: 我知道我的jquery页面链接正确,因为我在整个过程中都使用了许多其他jquery函数,而且它们都可以正常工作。我也尝试过从上方删除“ px”,但似乎没有

  • 我在Scala中查看幻灯片函数中的Spark。

  • 我有一个这样的查询,其中join~6000个值 多值查询的查询计划: 我使用PosgreSQL 10.8.0。有没有可能加快这个查询的速度? 我尝试用递归替换DISTINCT: 但结果更慢。 哈希连接(成本=418.67..21807.22行=3000宽度=24)(实际时间=16.804..10843.174行=5991循环=1)哈希Cond:(t.user_id="VALUES"。专栏1)缓冲区

  • 问题内容: 从EMPLOYEE表中,我想对记录的数量(雇用的雇员)进行分组,并且还需要每天运行的TOTAL。输入的格式如下: 所需的输出: 每天的分组没有问题: 问题 :如何使用窗口功能获得总计(在最后一栏中) 问题答案: http://sqlfiddle.com/#!4/4bd36/9