面试题:如何在列级别获得最近3个月的聚合?

Posted

技术标签:

【中文标题】面试题:如何在列级别获得最近3个月的聚合?【英文标题】:Interview question:How to get last 3 month aggregation at column level? 【发布时间】:2019-08-07 05:01:41 【问题描述】:

这是我在 Apple 现场采访中被问到的问题,让我大吃一惊。数据是这样的:

orderdate,unit_of_phone_sale

20190806,3000

20190704,3789

20190627,789

20190503,666

20190402,765

我必须编写一个查询来获取每个月销售的结果,我们应该有最近 3 个月的销售值。让我把预期的输出放在这里。

order_monnth,M-1_Sale, M-2_Sale, M-3_Sale

201908,3000,3789,789,666

201907,3789,789,666,765

201906,789,666,765,0

201905,666,765,0,0

201904,765,0,0

我只能通过硬编码月份获得明智的销售和使用案例陈述,这是错误的。我敲了敲头想写这个sql,但我写不出来。

任何人都可以帮助解决这个问题。对我准备sql面试真的很有帮助

更新:这是我尝试过的

with abc as(
select to_char(order_date,'YYYYMM') as yearmonth,to_char(order_date,'YYYY') as year,to_char(order_date,'MM') as moth, sum(unit_of_phone_sale) as unit_sale
from t1 group by to_char(order_date,'YYYYMM'),to_char(order_date,'YYYY'),to_char(order_date,'MM'))
select yearmonth, year, case when month=01 then unit_sale else 0 end as M1_Sale,
case when month=02 then unit_sale else 0 end as M2_Sale...
case when month=12 then unit_sale else 0 end as M12_Sale
from abc

【问题讨论】:

在 Apple 的一次采访中,您被问到一个 Oracle 问题?这真的是预期的输出吗?我认为这太复杂了,无法在一分钟内快速回答。 第一个回复应该是:“永远不要将 date 值存储为字符串或数字 - 使用正确的 DATETIMESTAMP 数据类型。” 【参考方案1】:

您首先需要对当月的数据求和,然后使用 LAG 函数获取前几个月的数据,如下所示:

SELECT
    ORDER_MONTH,
    LAG(UNIT_OF_PHONE_SALE, 1) OVER(
        ORDER BY
            ORDER_MONTH
    ) AS "M-1_Sale",
    LAG(UNIT_OF_PHONE_SALE, 2) OVER(
        ORDER BY
            ORDER_MONTH
    ) AS "M-2_Sale",
    LAG(UNIT_OF_PHONE_SALE, 3) OVER(
        ORDER BY
            ORDER_MONTH
    ) AS "M-3_Sale"
FROM
    (
        SELECT
            TO_CHAR(ORDERDATE, 'YYYYMM') AS ORDER_MONTH,
            SUM(UNIT_OF_PHONE_SALE) AS UNIT_OF_PHONE_SALE
        FROM
            DATAA
        GROUP BY
            TO_CHAR(ORDERDATE, 'YYYYMM')
    )
ORDER BY
    ORDER_MONTH DESC;

输出:

ORDER_   M-1_Sale   M-2_Sale   M-3_Sale
------ ---------- ---------- ----------
201908       3789        789        666
201907        789        666        765
201906        666        765
201905        765
201904

db<>fiddle demo

干杯!!

-- 更新--

对于 cmets 中提到的要求,以下查询将适用。

CTE AS (
    SELECT
        TRUNC(ORDERDATE, 'MONTH') AS ORDER_MONTH,
        SUM(UNIT_OF_PHONE_SALE) AS UNIT_OF_PHONE_SALE
    FROM
        DATAA
    GROUP BY
        TRUNC(ORDERDATE, 'MONTH')
)

SELECT
    TO_CHAR(C.ORDER_MONTH,'YYYYMM') as ORDER_MONTH,
    NVL(C1.UNIT_OF_PHONE_SALE, 0) AS "M-1_Sale",
    NVL(C2.UNIT_OF_PHONE_SALE, 0) AS "M-2_Sale",
    NVL(C3.UNIT_OF_PHONE_SALE, 0) AS "M-3_Sale"
FROM
    CTE C
    LEFT JOIN CTE C1 ON ( C1.ORDER_MONTH = ADD_MONTHS(C.ORDER_MONTH, - 1) )
    LEFT JOIN CTE C2 ON ( C2.ORDER_MONTH = ADD_MONTHS(C.ORDER_MONTH, - 2) )
    LEFT JOIN CTE C3 ON ( C3.ORDER_MONTH = ADD_MONTHS(C.ORDER_MONTH, - 3) )
ORDER BY
    C.ORDER_MONTH DESC

输出:

db<>fiddle demo 的更新答案。

干杯!!

【讨论】:

这是完美的 如果 201907 数据丢失怎么办,第一个记录将在 6 月作为 M-2。知道如何解决它 您是否在上述情况下尝试过此查询?它的工作方式与您的预期完全相同? 对不起,误会了。您的确切期望是什么? 所以如果数据丢失一个月,它应该是 null 。例如,对于 201907 没有数据,那么 201908 行 m_1_sale 应该为 0。请您帮忙【参考方案2】:

我认为 LEAD 功能在这里可以提供帮助 -

SELECT TO_CHAR(orderdate, 'YYYYMM') "DATE"
  ,unit_of_phone_sale M_1_Sale
  ,LEAD(unit_of_phone_sale,1,0) OVER(ORDER BY TO_CHAR(orderdate, 'YYYYMM') DESC) M_2_Sale
  ,LEAD(unit_of_phone_sale,2,0) OVER(ORDER BY TO_CHAR(orderdate, 'YYYYMM') DESC) M_3_Sale
  ,LEAD(unit_of_phone_sale,3,0) OVER(ORDER BY TO_CHAR(orderdate, 'YYYYMM') DESC) M_4_Sale
FROM table_sales

这里是DB Fiddle

【讨论】:

【参考方案3】:

你可以使用这个查询:

select a.order_month, a.unit_of_phone_sale, 
LEAD(unit_of_phone_sale, 1, 0) OVER (ORDER BY rownum) AS M_1,
LEAD(unit_of_phone_sale, 2, 0) OVER (ORDER BY rownum) AS M_2,
LEAD(unit_of_phone_sale, 3, 0) OVER (ORDER BY rownum) AS M_3
from (
select TO_CHAR(orderdate, 'YYYYMM') order_month,
unit_of_phone_sale,
rownum
from Y
order by order_month desc) a

【讨论】:

以上是关于面试题:如何在列级别获得最近3个月的聚合?的主要内容,如果未能解决你的问题,请参考以下文章

如何使用月份名称获取最近 3 个月的计数,如果该月份没有记录需要使用月份名称获取 0 [重复]

如何在 sql server 中获取最近 3 个月的名称

如何从当前日期 PHP 获取最近 7 周、7 个月的日期范围?

火了!北大学霸爆肝3个月的算法小抄完整笔记,GitHub疯狂转发

如何找到滚动的 3 个月方差?

在 BigQuery 中减去最近 3 个月的数据