我如何使用下面描述的原点 table 和预期 table 分析 PostgreSQL 上的数据?

How can I analyse data on PostgreSQL using the origin table and expectant table I have described below?

我的数据是这样的:

Item Year Price
Hat 2020 15
Hat 2021 17
Hat 2022 19
Pen 2020 3
Pen 2021 2
Pen 2022 3.3

我想使用 PostgreSQL 实现一个简单的数据分析任务,将提供以下结果:

Item Year Price Previous Year Previous price higher_than_previous_year
Hat 2020 15
Hat 2021 17 2020 15 yes
Hat 2022 19 2021 17 yes
Pen 2020 3
Pen 2021 2 2020 3 no
Pen 2022 3.3 2021 2 yes

上一年: 获取小于年份列中指定的当前年份的最近年份。 上一价格: 获取与上一年对应的商品价格。 higher_than_previous_year:将价格栏与上一个价格栏进行比较,当价格高于前一价格时计算是,当前一价格高于当年价格时计算否。

如果有任何 PostgreSQL 专家能对此进行破解,我将不胜感激。谢谢!

您可以使用简单的 JOINSUNION 来实现此目的(多种方法之一):

查询:

SELECT 
    a.item, 
    a.year, 
    a.price, 
    NULL AS prev_year, 
    NULL AS prev_price,
    NULL AS higher_than_previous_year
FROM
    (
    Select *, ROW_NUMBER() OVER (PARTITION BY Item ORDER BY Year) as rn
    from t
    ) as a
WHERE rn=1

UNION

SELECT 
    a.item, 
    a.year, 
    a.price, 
    b.year as prev_year, 
    b.price as prev_price,
  CASE
    WHEN a.price > b.price THEN 'yes'
    ELSE 'No'
  END AS higher_than_previous_year
FROM 
    (
    Select *, ROW_NUMBER() OVER (PARTITION BY Item ORDER BY Year) as rn
    from t
    ) as a
LEFT JOIN
    (
    Select *, ROW_NUMBER() OVER (PARTITION BY Item ORDER BY Year) as rn
    from t
    ) as b
  ON a.item=b.item and a.rn=b.rn+1
WHERE a.rn>1

解释:

  1. 首先创建一个 ROW_NUM 列来查找前几行 - 在所有使用 FROMJOIN 的地方使用相同的 table
  2. 以上 UNION: 获取带有 1 的行,因为它们很容易继续
  3. 低于 UNION: 自加入 table 以获取其上一个值

参见db<>fiddle

中的fiddle

如果您有访问权限,我建议创建 intermediate tables 以便查询看起来更简单。请参阅此 fiddle(创建了中间 tables,因此查询易于理解和调试)

只需使用 lag window function, which allows you to look back the previous row(s). Since this requires several look back operation, you define and use a WINDOW definition in the query. (see demo)

select item, year, price 
     , lag(year)  over w  previous_year
     , lag(price) over w  previous_price
     , case when price > lag(price) over w  then 'Yes' 
            when lag(price) over w   is null then null::text  
            else 'No'
       end higher_than_previous_year
  from origin
window w as (partition by item order by year);  

我之前在对@arun 的解决方案的评论之一中指出,有一个名为 'category' 的额外列。我发现这个查询效果最好。

SELECT *, 
CASE WHEN price > previous_price THEN 'yes'
     WHEN price <= previous_price THEN 'no'
     ELSE null
END AS is_current_year_price_higher
FROM
    (
SELECT *,
LAG(year,1) OVER (PARTITION BY item, category ORDER BY item, year) AS previous_year,
LAG(price,1) OVER (PARTITION BY item, category ORDER BY item, year) AS previous_price
FROM table_name
    ) AS main