【问题标题】:Is there anyway to improve this query computational cost?有没有办法改善这个查询计算成本?
【发布时间】:2017-06-26 08:27:27
【问题描述】:

我在我自己的机器上为这个数据库使用 PostgreSQL 9.6.1。

我有这个交易数据库。整个数据库大约有 1 亿行 x 30 列。交易跨越过去四年。

对于这个查询,有三个相关的列:

  • 交易时间戳,四舍五入到最近的 15 分钟
  • 供应商 ID
  • 交易金额(收入)

我有兴趣返回四列的输出,例如下图(抱歉链接 - 还没有足够的代表来嵌入图像:

输出是该特定时间戳期间的交易计数、过去 60 分钟内唯一活跃供应商的计数以及过去 60 分钟内的每小时收入。

以下是我用来尝试完成此操作的代码。

SELECT transaction_timestamp,
   COUNT(transaction_timestamp) AS "transaction_timestamp",
   (SELECT COUNT(DISTINCT vendor_id)
    FROM transactions_db
    WHERE transaction_timestamp BETWEEN t.transaction_timestamp - INTERVAL '60 MINUTES' AND t.transaction_timestamp
   ) AS "lag_60_transaction_count",
   (SELECT SUM(revenue) / COUNT(DISTINCT vendor_id)
    FROM transactions_db
    WHERE transaction_timestamp BETWEEN t.transaction_timestamp - INTERVAL '60 MINUTES' AND t.transaction_timestamp
   ) AS "rolling_hourly_rate"
FROM transactions_db t
GROUP BY transaction_timestamp
ORDER BY transaction_timestamp;

这里是解释输出:

 GroupAggregate  (cost=21989857.85..681893649752.90 rows=108423 width=56)
   Group Key: t.transaction_timestamp
   ->  Sort  (cost=21989857.85..22252785.49 rows=105171056 width=8)
         Sort Key: t.transaction_timestamp
         ->  Index Only Scan using timestamp_vendor_revenue_idx on transactions_db t  (cost=0.57..3663118.41 rows=105171056 width=8)
   SubPlan 1
     ->  Aggregate  (cost=3143836.32..3143836.33 rows=1 width=8)
           ->  Index Only Scan using timestamp_vendor_revenue_idx on transactions_db  (cost=0.57..3142521.68 rows=525855 width=4)
                 Index Cond: ((transaction_timestamp >= (t.transaction_timestamp - '01:00:00'::interval)) AND (transaction_timestamp <= t.transaction_timestamp))
   SubPlan 2
     ->  Aggregate  (cost=3145150.96..3145150.97 rows=1 width=32)
           ->  Index Only Scan using timestamp_vendor_revenue_idx on transactions_db transactions_db_1  (cost=0.57..3142521.68 rows=525855 width=10)
                 Index Cond: ((transaction_timestamp >= (t.transaction_timestamp - '01:00:00'::interval)) AND (transaction_timestamp <= t.transaction_timestamp))

话虽如此,这个查询运行时间非常长(8 多个小时 - 运行了一夜,今天早上仍在运行)。

我在 transaction_timestamp、vendor_id 和收入上创建了一个复合索引,但运行时间仍然非常高。

当我对数据子集(我有一个包含一天数据的示例表)运行此查询时,查询会在 2.1 秒内返回。

我对优化数据库和查询非常熟悉,所以我可以在 2.1 秒内返回一天的数据,这一事实让我相信我可以做一些事情来让这个查询在主数据库的合理时间。

如果我遗漏了任何其他信息,请告诉我。

这里的示例数据、查询和输出:http://rextester.com/AOKNT5900

【问题讨论】:

  • 能否提供您的CREATE TABLE 以查看索引?
  • @JuanCarlosOropeza 您是否想过将所有数据发送到 GPU,以便在计算密集型的情况下它可以更快地完成任务?
  • @huseyintugrulbuyukisik 不,我说 1 亿个表查询不需要 8 小时。
  • @JuanCarlosOropeza 100mm 是数百万。索引是在创建表之后创建的 - 就是这样:CREATE INDEX timestamp_vendor_revenue_idx ON transactions_db (vendor_id, transaction_timestamp, revenue)
  • 即使索引是在表之后创建的,也可以从pgAdmin获取create table脚本。

标签: sql database postgresql


【解决方案1】:

试试这样的:

select t1.transaction_timestamp
, count (t1.*) transactions
, count(distinct t1.vendor_id) vendors
, sum(t1.revenue) / count(distinct t1.vendor_id) hourly_rate

from transactions_db t1 join transactions_db t2 
    on t1.transaction_timestamp > t2.transaction_timestamp
    and t1.transaction_timestamp < t2.transaction_timestamp + INTERVAL '61 MINUTES' 

group by t1.transaction_timestamp

另外,除非你真的需要整个数据库,否则过滤 transaction_timestamp 和/或 vendor_id

【讨论】:

  • 我也认为我做到了。
  • 每个人都太专注于逻辑而忘记了语法?我愿意。
  • 对不起,如果我太打扰了。我认为 group by 应该超过 t2.transaction_timestamp 我希望有一个样本数据来更好地测试它:/
  • @DanBracuk Hi Dan - 我想要整个数据库 - 试图查看所有交易和供应商随时间推移的趋势。我对您的 join 语句试图完成的工作感到困惑 - 看起来您正试图加入 t1 > t2 但随后 t2+1​​ 小时 > t1 的时间戳,我不明白。我想要完成的是说你有一个给定的时间戳 - 比如说 01:00:00。我想要计算 01:00:00 的所有交易、00:00:00 到 01:00:00 之间进行交易的唯一供应商的数量以及 00:00:00 到 01 之间每个供应商的平均收入: 00:00
  • 联接应该为您提供前 60 分钟的所有 t1 数据,这就是您所说的。
【解决方案2】:

此版本提供的结果与您当前的查询相同。我必须将计算分成两部分,然后在最后加入。检查两者的解释并告诉我。

第二个查询的关键是创建一个子查询,将每个时间戳作为一个组,然后加入以获取该组中的每个收入。

FROM ( SELECT DISTINCT transaction_timestamp 
       FROM transactions_db) t1 

DEMO

WITH transaction_total as (    
    SELECT transaction_timestamp,
           COUNT (transaction_timestamp)  AS "total"
    FROM transactions_db t
    GROUP BY transaction_timestamp
), lag_60 as (
    SELECT  t1.transaction_timestamp, 
            COUNT(DISTINCT t2.vendor_id) as lag_60_transaction_count,
            SUM(revenue) / COUNT(DISTINCT t2.vendor_id) AS "rolling_hourly_rate"
    FROM ( SELECT DISTINCT transaction_timestamp 
           FROM transactions_db) t1 
    join transactions_db t2 
      on t1.transaction_timestamp <= t2.transaction_timestamp + INTERVAL '60 MINUTES'
     and t1.transaction_timestamp >= t2.transaction_timestamp
    GROUP BY t1.transaction_timestamp
)    
SELECT T1.transaction_timestamp,
       T1.total,
       T2.lag_60_transaction_count,
       T2.rolling_hourly_rate
FROM transaction_total T1
JOIN lag_60 T2
USING (transaction_timestamp)
ORDER BY T1.transaction_timestamp;
;

输出:

【讨论】:

  • 谢谢胡安,干得好 - 看起来总成本减少了一半。我会尝试运行它,但仍然担心总时间 - 由于数据的大小,这可能是我无法避免的问题。
  • 好吧,你可以每年批量运行它。您还需要再次检查explain analyze。您必须检查选项explain (analyze, buffers) 以查看内存使用情况。你在你的 postgresql 服务器中增加了work_mem 吗?
  • 我几天前遇到了类似的problem,也有数百万条记录。我没有计算查询1 to 10.000.000,而是在 C# 中创建了一个应用程序,并将查询分批发送到 postgress ... 就像{1,10000}, {10001,20000}{20001,30000} postgres 可以一次运行多个小查询比一个大查询快(我说快 20 倍) . 9.6 也有 Parallel Query 所以也许你不需要它,但你可以再次查看解释计划。
  • 感谢您提供的信息 - 我需要出去一个小时左右,等我回来再看看这个。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2012-12-12
  • 1970-01-01
  • 1970-01-01
  • 2012-11-09
相关资源
最近更新 更多