SQL(分析)查询以检索分组依据的每个值的第一行和第二行

SQL (analytical) query to retrieve first and second rows for each value of a group by

我有一个情况想在 SQL 中使用尽可能少的内部联接来解决,所以我正在考虑使用 window 函数。

我正在使用 Snowflake,但我也很高兴在其他数据库引擎中看到答案。

Table 销售额有 sale_id 和 seller_id,还有 sale_datetime,金额... Table 卖家有 seller_id

我想获得每个卖家的第一次和第二次销售,最好是列(这样我就可以加减,例如金额总和)。如果只有一个销售,理想情况下我不会检索它。

我正在尝试使用这样的东西:

select se.seller_id,sa.*, first_value(sale_datetime) over (ORDER BY
sale_datetime ),  LAG(sale_datetime) OVER (ORDER BY sale_datetime)
from seller se inner join sales sa  on sa.seller_id = se.seller_id
order by se.seller_id;

但我需要添加一个分组依据,因为 window 功能需要在卖家级别应用,而不是 table 的总和。无论是在查询末尾还是在 window 中添加它,我都会得到一个错误。

我在没有 windows 的情况下执行此操作的另一种方法是使用两个 with...as 子句分别检索每个卖家的第一次和第二次销售以及它们之间的连接,但我正在尝试出于性能原因避免这种情况。

谢谢!

ROW_NUMBER 和 QUALIFY 可以使用:

select se.seller_id,sa.*
from seller se 
join sales sa  
  on sa.seller_id = se.seller_id
qualify row_number() over(partition by sa.seller_id order by sale_datetime) <= 2
order by se.seller_id;

你的答案已经基本存在了。您只需要使用 LAG 并使用 ROW_NUMBER/QUALIFY

数据的 CTE:

with sales(sale_id, seller_id, sale_datetime, amount) as (
    select * from values
    (4, 10, '2022-04-24'::date, 100),
    (3, 10, '2022-04-23'::date, 200),
    (2, 10, '2022-04-22'::date, 400),
    (1, 11, '2022-04-22'::date, 300)
)

工作SQL:

select sa.*,
    LAG(sale_datetime) OVER (partition by sa.seller_id ORDER BY sa.sale_datetime) as prior_sale_date,
    LAG(amount) OVER (partition by sa.seller_id ORDER BY sa.sale_datetime) as prior_amount  
FROM sales AS sa  
qualify row_number() over(partition by sa.seller_id order by sale_datetime) = 2
order by sa.seller_id;

给出:

SALE_ID SELLER_ID SALE_DATETIME AMOUNT PRIOR_SALE_DATE PRIOR_AMOUNT
2 10 2022-04-23 200 2022-04-22 400

这是可行的,因为每一行都通过两个 LAG 获得先验值,但我们只保留第二行。

我还删除了 seller se table 因为它对流程没有任何价值,因为它只是一个过滤器..,但可以通过以下方式存在:

更复杂的数据 CTE:

    select * from values
    (4, 10, '2022-04-24'::date, 100),
    (3, 10, '2022-04-23'::date, 200),
    (2, 10, '2022-04-22'::date, 400),
    (1, 11, '2022-04-22'::date, 300)
), seller(seller_id) as (
    select * from values (10),(11),(12),(13)
)

与 JOIN 一起工作 SQL。

select sa.*,
    LAG(sale_datetime) OVER (partition by sa.seller_id ORDER BY sa.sale_datetime) as prior_sale_date,
    LAG(amount) OVER (partition by sa.seller_id ORDER BY sa.sale_datetime) as prior_amount  
FROM seller as se
JOIN sales AS sa
    ON se.seller_id = sa.seller_id
qualify row_number() over(partition by sa.seller_id order by sale_datetime) = 2
order by sa.seller_id;

给出:

SALE_ID SELLER_ID SALE_DATETIME AMOUNT PRIOR_SALE_DATE PRIOR_AMOUNT
3 10 2022-04-23 200 2022-04-22 400