通过 timestampz 和 groupid 选择最新的记录

Selecting the latest records by timestampz and groupid

我正在尝试 return 基于导入时使用的 TIMESTAMPZ 的商店的最新记录。我在 Postgres 9.5 上,这是我在此处的某些线程上从 Whosebuging 获得的查询:

select p.*
from store_products p
inner join(
   select storeid, sku, max(lastupdated) AS lastupdated
   from store_products
   group by storeid, sku
)sp on p.storeid= sp.storeidand p.lastupdated = sp.lastupdated

这为我提供了每家商店(和 SKU)的最新 产品,这很好(我们有大约 30 家商店),但我注意到查询需要 (对于 6M 记录)大约 4 分钟来收集数据。

所以如果我们有这个作为我的数据:

PID | StoreID | SKU | lastupdated
1   | 1       | 1a1 | 2017-02-02 18:22:30
2   | 1       | 1b1 | 2017-02-02 18:21:30
3   | 1       | 1a1 | 2017-01-16 11:22:30
4   | 2       | 1a1 | 2017-02-02 18:21:30
5   | 2       | 1a1 | 2017-02-01 18:21:00
6   | 3       | 1a1 | 2017-02-02 18:21:30
7   | 3       | 1g1 | 2017-02-01 18:21:30

我明白了:

PID | StoreID | SKU | lastupdated
1   | 1       | 1a1 | 2017-02-02 18:22:30
2   | 1       | 1b1 | 2017-02-02 18:21:30
4   | 2       | 1a1 | 2017-02-02 18:21:30
6   | 3       | 1a1 | 2017-02-02 18:21:30

是否有更好的方法让我们能够导入这些商店快照,以便 Postgres 更容易理解上面的查询 - 更快?我们应该添加任何索引吗?这是解释:

Hash Join  (cost=2358424.92..2715814.08 rows=311 width=371)
  Hash Cond: ((lp.storeid = p.storeid) AND (lp.lastupdated = p.lastupdated))
  ->  Subquery Scan on lp  (cost=1676046.30..1737513.85 rows=62125 width=12)
        ->  GroupAggregate  (cost=1676046.30..1736892.60 rows=62125 width=108)
              Group Key: store_products.storeid, store_products.sku
              ->  Sort  (cost=1676046.30..1691102.56 rows=6022505 width=108)
                    Sort Key: store_products.storeid, store_products.sku
                    ->  Seq Scan on store_products  (cost=0.00..297973.05 rows=6022505 width=108)
  ->  Hash  (cost=297973.05..297973.05 rows=6022505 width=371)
        ->  Seq Scan on store_products p  (cost=0.00..297973.05 rows=6022505 width=371)

我们的 Postgres DBA 正在休假,我们中的大多数人都不知道在这里做什么。

背景故事...

我们每天从 JSON 的多家商店获取商店产品转储。每个商店都由 storeid 确定,它们被导入为一个包含所有商店及其产品的大块 JSON 文件。每个条目都有自己的 lastupdated |时间戳字段。如果有人决定稍后更新该字段(出于审计目的),则由触发器自动更新该字段。每天,大约有 2-3K 的 store_products 被插入到这个 table 中,我们目前没有删除这些数据(所以价格可能已经改变,它可能没有,我们没有似乎还不关心,我们只是插入)。我想我们很快就会删除重复数据。

让我给你一个基本的模式:

CREATE TABLE store_products
(
    id BIGINT DEFAULT PRIMARY KEY NOT NULL,
    storeid INTEGER,
    ...etc etc...
    lastupdated TIMESTAMP WITH TIME ZONE DEFAULT now()
);

商店的 storeid 有一个 FK table 等等

尝试使用 ROW number -over partition by 子句并使用 temp table,如下所示

select *
from (
    select p.*
    from store_products p
    inner join (
        select
            storeid,
            max(lastupdated) AS lastupdated,
            ROW_NUMBER() OVER (PARTITION BY storedid ORDER BY lastupdated DESC) AS RowNo
        from store_products
        group by storeid
    ) sp on p.storeid= sp.storeidand p.lastupdated = sp.lastupdated
) temp
where
order by temp.RowNo 

distinct on 会更简单:

select distinct on (storeid, sku) *
from store_products
order by storeid, sku, lastupdated desc

请注意,必须使用 order by 子句来确定要返回的行。

在 (storeid, sku, lastupdated) 上创建索引,如果没有足够的时间戳值得额外大小的索引,则仅在 (storeid, sku) 上创建索引。