postgresql 从表中获取非重复值,但在出现重复值时选择最新值?

lf3rwulv  于 2023-03-29  发布在  PostgreSQL
关注(0)|答案(5)|浏览(207)

在PostgreSQL中,表Sales包含以下数据:

| id(PK) | eva_dts_id(FK) | realm_name | device_id | timestamp | product_id | product_name | quantity | product_price | revenue |

realm_namedevice_id常量,即使它们在将来发生变化,它们也会在此表中相应地更新。
我尝试使用以下模式在其上创建视图Products

| realm_name | device_id | product_id | product_name | product_price |

到目前为止,我使用了以下查询:

SELECT DISTINCT realm_name, device_id, product_id, product_name, product_price
FROM public.Sales;

假设设备的产品列表是不可变的,所以将来不会改变。我想释放这个假设,并为设备创建一个产品列表数据,始终考虑最近的记录(基于Sales表中找到的timestamp)。例如,如果我有一个产品“Chocomilk”,它有记录:

| id(PK) | eva_dts_id(FK) | realm_name | device_id | timestamp | product_id | product_name | quantity | product_price | revenue |
"03ef91f6-bb24-4c8e-90ef-366cc4dee5a6"  "e853dcec-c369-4111-816d-1645067df8e1"  "tenant"    "RbbMIyemWTOI99N6XZx1hA"    "2023-03-26 22:43:31.454734"    "10"    "Chocomilk" 1   0.38    0
"03ef91f6-bb24-4c8e-90ef-366cc4dee5a6"  "e853dcec-c369-4111-816d-1645067df8e1"  "tenant"    "RbbMIyemWTOI99N6XZx1hA"    "2023-04-12 22:43:31.454734"    "10"    "Chocomilk" 1   2.3 0
"03ef91f6-bb24-4c8e-90ef-366cc4dee5a6"  "e853dcec-c369-4111-816d-1645067df8e1"  "tenant"    "RbbMIyemWTOI99N6XZx1hA"    "2023-05-18 22:43:31.454734"    "10"    "Chocomilk" 1   1.5 0

我只考虑最后一个记录。
如何重写视图查询以实现此目的?

xoefb8l8

xoefb8l81#

使用ROW_NUMBER()按时间戳排序你的物品信息,只获取最新的。

select
    realm_name,
    device_id,
    product_id,
    product_name,
    product_price
from
    (
        SELECT
            realm_name,
            device_id,
            product_id,
            product_name,
            product_price,
            row_number() OVER (
                PARTITION BY
                    realm_name,
                    device_id,
                    product_id
                ORDER BY
                    timestamp desc
            ) rn
        FROM
            public.Sales
    ) as most_recents
where
    rn = 1;

仔细检查ROW_NUMBER()PARTITION BY子句,以包含项目的唯一标识符,该标识符不会随时间而更改。

ldfqzlk8

ldfqzlk82#

对@markalex的答案提出了另一种解决方案,并遵循@tinazmu的建议:

SELECT DISTINCT realm_name,
                device_id,
                product_id,
                product_name,
                product_price
FROM public.Sales AS ext
WHERE timestamp =
    (
      SELECT MAX(int.timestamp)
      FROM public.Sales AS int
      WHERE int.realm_name = ext.realm_name
       AND int.device_id = ext.device_id
       AND int.product_id = ext.product_id
    );
hgb9j2n6

hgb9j2n63#

Postgres提供了另一种选择:select语句的Distinct on子句。该子句的结果是保持在第一行匹配distinct on表达式(根据短语顺序)。您要查找的是:(参见demo

select distinct on(product_name) *
  from sales 
 order by product_name, sale_ts desc;

注意:恕我直言,命名一个列timestamp是一个糟糕的做法,因为它是一个SQL保留字和一个Postgres数据类型(尽管不是Postgres保留的)。我在上面和演示中已经替换了sale_ts

fgw7neuy

fgw7neuy4#

对于这样的查询,我更喜欢使用公共表表达式,它可以更容易地识别数据是什么以及如何过滤它。
Row_number和partition,给予查询引擎更好地了解你实际上在寻找什么。Sql是一种声明性语言,只要有可能,你就应该描述你想要的东西,而不是做一些技巧来引导它找到正确的答案。

;with cte as (
select  realm_name
      , device_id
      , product_id
      , product_name
      , product_price
      , row_number() OVER (PARTITION BY realm_name, device_id, product_id
                           ORDER BY timestamp desc
                          ) rw
from public.Sales
)
select realm_name
      , device_id
      , product_id
      , product_name
      , product_price
from cte 
where rw=1
krcsximq

krcsximq5#

另一个选项(可能更直观)是使用EXISTS函数过滤掉较旧的记录:

SELECT  T.realm_name,
        T.device_id,
        T.product_id,
        T.product_name,
        T.product_price
FROM    Sales       AS  T
WHERE   NOT EXISTS
        (
            SELECT  1
            FROM    Sales               AS  newer
            WHERE   newer.product_id    =   T.product_id
            AND     newer.[timestamp]   >   T.[timestamp]
        )

此查询将确保同一product_id没有任何更新的记录

相关问题