我怎样才能加快这个查询?

时间:2021-05-10 18:51:22

标签: sql postgresql performance query-optimization

我有这个查询:

select 
    a.*, 
    trunc(MAX(rebase.circulating_supply) * a.supply_percentage, 9) as ampl_balance, 
    trunc(
        MAX(rebase.circulating_supply) *
        (coalesce(SUM(t.supply_percentage) FILTER (WHERE t.to = a.contract_address), 0) +
        (coalesce(SUM(t.supply_percentage) FILTER (WHERE t.from = a.contract_address), 0) * -1)),
        9
    ) as net_ampl_moved
from addresses as a
cross join (select r.circulating_supply from rebases r order by r.timestamp desc limit 1) as rebase
left join transfers as t
on (t.from = a.contract_address or t.to = a.contract_address) and t.timestamp >= ?
group by a.contract_address;

此处的目标是汇总某个时间段内账户的净余额,例如从过去 24 小时开始,所有时间等等。我通过总结每笔交易和每笔交易来做到这一点。

转移索引:

 CREATE INDEX transfers_from_to_index ON public.transfers USING btree ("from", "to")
 CREATE INDEX transfers_timestamp_index ON public.transfers USING btree ("timestamp")
 CREATE INDEX transfers_action_index ON public.transfers USING btree (action)
 CREATE UNIQUE INDEX transfers_pkey ON public.transfers USING btree (transaction_hash, log_index)
 CREATE INDEX transfers_supply_percentage_index ON public.transfers USING btree (supply_percentage)
 CREATE INDEX transfers_amount_index ON public.transfers USING btree (amount)
 CREATE INDEX transfers_supply_percentage_timestamp_log_index_index ON public.transfers USING btree (supply_percentage, "timestamp", log_index)
 CREATE INDEX transfers_from_index ON public.transfers USING btree ("from")
 CREATE INDEX transfers_to_index ON public.transfers USING btree ("to")

地址索引:

 CREATE UNIQUE INDEX addresses_pkey ON public.addresses USING btree (contract_address)
 CREATE INDEX addresses_supply_percentage_index ON public.addresses USING btree (supply_percentage)

rebase 上的索引:

 CREATE UNIQUE INDEX rebases_pkey ON public.rebases USING btree (epoch)
 CREATE INDEX rebases_timestamp_index ON public.rebases USING btree ("timestamp")

解释计划:

https://explain.depesz.com/s/1Zy

这里的任何帮助将不胜感激。

1 个答案:

答案 0 :(得分:1)

transfers 上的左连接是成本最高的部分,主要是由于 OR 并且没有好的索引来解决它。

Mybe 试试这个?

select 
    a.*, 
    trunc(
        MAX(rebase.circulating_supply)
          * a.supply_percentage,
        9
    ) as ampl_balance, 
    trunc(
        MAX(rebase.circulating_supply)
          * coalesce(SUM(t.supply_percentage), 0),
        9
    ) as net_ampl_moved
from addresses as a
cross join (select r.circulating_supply from rebases r order by r.timestamp desc limit 1) as rebase
left join (
  select tt.to   as contract_address, tt.timestamp,  tt.supply_percentage from transfers tt
  union all
  select tf.from as contract_address, tf.timestamp, -tf.supply_percentage from transfers tf
) t
on t.contract_address = a.contract_address and t.timestamp >= ?
group by a.contract_address;

然后在transfers中有两个新索引...

CREATE INDEX transfers_to_timestamp_index
  ON public.transfers
  USING btree ("to", "timestamp")
  INCLUDE ("supply_percentage");

CREATE INDEX transfers_from_timestamp_index
  ON public.transfers
  USING btree ("from", "timestamp")
  INCLUDE ("supply_percentage");

这应该会使连接和聚合都更便宜。


编辑:另一种选择...

使用与上面提到的相同的索引,但将所有聚合折叠到仅传输表上的子查询中。

(并将子查询移至 CTE 以提高可读性...)

WITH
  t_normalised
AS
(
  select tt.to   as contract_address, tt.timestamp,  tt.supply_percentage from transfers tt
  union all
  select tf.from as contract_address, tf.timestamp, -tf.supply_percentage from transfers tf
),
  t_sum
AS
(
  SELECT
    contract_address,
    SUM(supply_percentage)  AS supply_percentage
  FROM
    t_normalised
  WHERE
    timestamp >= ?
  GROUP BY
    contract_address
),
  rebase
AS
(
  select r.circulating_supply
    from rebases r
order by r.timestamp desc
   limit 1
) 
select 
    a.*, 
    trunc(
        rebase.circulating_supply
          * a.supply_percentage,
        9
    ) as ampl_balance, 
    trunc(
        rebase.circulating_supply
          * coalesce(t_sum.supply_percentage, 0),
        9
    ) as net_ampl_moved
from
  addresses as a
cross join
  rebase
left join
  t_sum
    on t_sum.contract_address = a.contract_address