You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何用单条SQL查询获取每个会员每年的期初与期末余额

单条SQL实现按会员、年份统计期初/期末余额的最优方案

方案1:ROW_NUMBER + 条件聚合(兼容性最优)

这种方法仅需一次表扫描,在子查询中同时计算升序、降序行号,再通过条件聚合提取每年的期初(最早记录)和期末(最晚记录)余额,几乎兼容所有支持窗口函数的数据库(MySQL 8.0+、PostgreSQL、SQL Server等)。

SELECT
    YEAR(DateCol) AS YEAR,
    MemberID AS MEMBERID,
    MAX(CASE WHEN seqnum_asc = 1 THEN Amount END) AS STARTBALANCE,
    MAX(CASE WHEN seqnum_desc = 1 THEN Amount END) AS ENDBALANCE
FROM (
    SELECT
        MemberID,
        DateCol,
        Amount,
        -- 按日期升序排,标记每年第一条记录(期初)
        ROW_NUMBER() OVER (PARTITION BY MemberID, YEAR(DateCol) ORDER BY DateCol) AS seqnum_asc,
        -- 按日期降序排,标记每年第一条记录(期末)
        ROW_NUMBER() OVER (PARTITION BY MemberID, YEAR(DateCol) ORDER BY DateCol DESC) AS seqnum_desc
    FROM Tablet
) t
WHERE seqnum_asc = 1 OR seqnum_desc = 1
GROUP BY YEAR(DateCol), MemberID
-- 如需筛选特定会员,添加 WHERE MemberID = '1000009'(可放在子查询或外层)

方案2:FIRST_VALUE/LAST_VALUE + DISTINCT(代码最简洁)

利用窗口函数直接提取分组内的第一条和最后一条金额,再通过DISTINCT去重,代码更简洁,但需注意LAST_VALUE的窗口范围设置(默认窗口仅包含当前行及之前的数据,必须显式指定覆盖整组)。

SELECT DISTINCT
    YEAR(DateCol) AS YEAR,
    MemberID AS MEMBERID,
    -- 取每年最早日期的金额(期初)
    FIRST_VALUE(Amount) OVER (
        PARTITION BY MemberID, YEAR(DateCol) 
        ORDER BY DateCol
    ) AS STARTBALANCE,
    -- 取每年最晚日期的金额(期末),指定窗口范围覆盖整组数据
    LAST_VALUE(Amount) OVER (
        PARTITION BY MemberID, YEAR(DateCol) 
        ORDER BY DateCol 
        RANGE BETWEEN UNBOUNDED PRECEDING AND UNBOUNDED FOLLOWING
    ) AS ENDBALANCE
FROM Tablet
-- 如需筛选特定会员,添加 WHERE MemberID = '1000009'

注意事项:处理同一日期多条记录的情况

如果同一会员在同一天有多条金额记录,建议先按MemberID和DateCol聚合金额(比如求和、取平均),再计算期初期末,避免随机取数:

SELECT
    YEAR(DateCol) AS YEAR,
    MemberID AS MEMBERID,
    MAX(CASE WHEN seqnum_asc = 1 THEN TotalAmount END) AS STARTBALANCE,
    MAX(CASE WHEN seqnum_desc = 1 THEN TotalAmount END) AS ENDBALANCE
FROM (
    SELECT
        MemberID,
        DateCol,
        SUM(Amount) AS TotalAmount, -- 按日期聚合金额,可根据需求改为AVG/MAX等
        ROW_NUMBER() OVER (PARTITION BY MemberID, YEAR(DateCol) ORDER BY DateCol) AS seqnum_asc,
        ROW_NUMBER() OVER (PARTITION BY MemberID, YEAR(DateCol) ORDER BY DateCol DESC) AS seqnum_desc
    FROM Tablet
    GROUP BY MemberID, DateCol
) t
WHERE seqnum_asc = 1 OR seqnum_desc = 1
GROUP BY YEAR(DateCol), MemberID

内容的提问来源于stack exchange,提问作者bigquestiontime

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.13 11:10:35