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

如何编写SQL Server查询按账户统计2020年月度余额

SQL Server 账户月度余额行列转换查询实现

需求说明

需要编写SQL Server查询,获取所有账户在2020年1月至12月的月度余额,输出为按账户、年份展示各月余额的行列转换表格。余额计算逻辑:

每月余额 = (Transfer_ins.amount总和 + pix_movements中in_or_out为'pix_in'的pix_amount总和) - (Transfer_outs.amount总和 + pix_movements中in_or_out为'pix_out'的pix_amount总和)

预期输出示例:

Account_id| Year  | Jan |Feb| Mar| Apr|May | Jun| Jul| Aug|Sep| Oct| Nov|Dec
---------  ----  --- --- --- --- --- --- --- --- --- --- --- ---
        1 | 2020  | 200 | 0 |  0 |  0 |  0 |  0 |  0 |  0 | 0 |-500| 400| 0
        2 | 2020  |  0  | 0 |  0 | 500|  0 |  0 | 900|  0 | 0 | 100| 0  | 0
        3 | 2020  | 100 | 0 |  0 |  0 |  0 |  0 | 0  |  0 | 0 | 200| 500| 0

原查询的问题

  1. 表连接错误:使用INNER JOIN会过滤掉没有转账或Pix交易的账户,应改为LEFT JOIN;同时存在表名拼写错误:pix_movemets应为pix_movements。
  2. 语法错误:SELECT列之间缺少逗号分隔;SQL Server中字符串常量需用单引号(原查询用了双引号);Dec列误写为Dic,且Nov的CASE语句中重复使用了月份11(应为12)。
  3. 逻辑错误:多表直接连接会产生笛卡尔积,导致金额重复计算;GROUP BY包含action_month但SELECT中未按月份分组汇总,行列转换逻辑混乱。

正确查询实现

方案一:先汇总月度数据,再用CASE语句行列转换

WITH MonthlyBalances AS (
    SELECT
        a.account_id,
        YEAR(t.transaction_date) AS balance_year,
        MONTH(t.transaction_date) AS balance_month,
        SUM(CASE WHEN t.transaction_type = 'transfer_in' THEN t.amount ELSE 0 END) +
        SUM(CASE WHEN t.transaction_type = 'pix_in' THEN t.amount ELSE 0 END) AS total_in,
        SUM(CASE WHEN t.transaction_type = 'transfer_out' THEN t.amount ELSE 0 END) +
        SUM(CASE WHEN t.transaction_type = 'pix_out' THEN t.amount ELSE 0 END) AS total_out
    FROM accounts a
    LEFT JOIN (
        -- 合并转入交易
        SELECT account_id, transfer_date AS transaction_date, amount, 'transfer_in' AS transaction_type
        FROM transfer_ins
        WHERE YEAR(transfer_date) = 2020
        UNION ALL
        -- 合并转出交易
        SELECT account_id, transfer_date AS transaction_date, amount, 'transfer_out' AS transaction_type
        FROM transfer_outs
        WHERE YEAR(transfer_date) = 2020
        UNION ALL
        -- 合并Pix转入交易
        SELECT account_id, movement_date AS transaction_date, pix_amount AS amount, 'pix_in' AS transaction_type
        FROM pix_movements
        WHERE in_or_out = 'pix_in' AND YEAR(movement_date) = 2020
        UNION ALL
        -- 合并Pix转出交易
        SELECT account_id, movement_date AS transaction_date, pix_amount AS amount, 'pix_out' AS transaction_type
        FROM pix_movements
        WHERE in_or_out = 'pix_out' AND YEAR(movement_date) = 2020
    ) t ON a.account_id = t.account_id
    GROUP BY a.account_id, YEAR(t.transaction_date), MONTH(t.transaction_date)
)
SELECT
    account_id,
    2020 AS Year,
    ISNULL(SUM(CASE WHEN balance_month = 1 THEN (total_in - total_out) END), 0) AS Jan,
    ISNULL(SUM(CASE WHEN balance_month = 2 THEN (total_in - total_out) END), 0) AS Feb,
    ISNULL(SUM(CASE WHEN balance_month = 3 THEN (total_in - total_out) END), 0) AS Mar,
    ISNULL(SUM(CASE WHEN balance_month = 4 THEN (total_in - total_out) END), 0) AS Apr,
    ISNULL(SUM(CASE WHEN balance_month = 5 THEN (total_in - total_out) END), 0) AS May,
    ISNULL(SUM(CASE WHEN balance_month = 6 THEN (total_in - total_out) END), 0) AS Jun,
    ISNULL(SUM(CASE WHEN balance_month = 7 THEN (total_in - total_out) END), 0) AS Jul,
    ISNULL(SUM(CASE WHEN balance_month = 8 THEN (total_in - total_out) END), 0) AS Aug,
    ISNULL(SUM(CASE WHEN balance_month = 9 THEN (total_in - total_out) END), 0) AS Sep,
    ISNULL(SUM(CASE WHEN balance_month = 10 THEN (total_in - total_out) END), 0) AS Oct,
    ISNULL(SUM(CASE WHEN balance_month = 11 THEN (total_in - total_out) END), 0) AS Nov,
    ISNULL(SUM(CASE WHEN balance_month = 12 THEN (total_in - total_out) END), 0) AS Dec
FROM MonthlyBalances
GROUP BY account_id
UNION ALL
-- 补充没有任何交易的账户,确保所有账户都出现在结果中
SELECT
    account_id,
    2020 AS Year,
    0 AS Jan, 0 AS Feb, 0 AS Mar, 0 AS Apr, 0 AS May, 0 AS Jun,
    0 AS Jul, 0 AS Aug, 0 AS Sep, 0 AS Oct, 0 AS Nov, 0 AS Dec
FROM accounts
WHERE account_id NOT IN (SELECT DISTINCT account_id FROM MonthlyBalances)
ORDER BY account_id;

方案二:使用PIVOT运算符实现行列转换

WITH MonthlyBalances AS (
    SELECT
        a.account_id,
        YEAR(t.transaction_date) AS balance_year,
        DATENAME(MONTH, t.transaction_date) AS balance_month_name,
        (SUM(CASE WHEN t.transaction_type IN ('transfer_in', 'pix_in') THEN t.amount ELSE 0 END) -
         SUM(CASE WHEN t.transaction_type IN ('transfer_out', 'pix_out') THEN t.amount ELSE 0 END)) AS monthly_balance
    FROM accounts a
    LEFT JOIN (
        SELECT account_id, transfer_date AS transaction_date, amount, 'transfer_in' AS transaction_type FROM transfer_ins WHERE YEAR(transfer_date)=2020
        UNION ALL
        SELECT account_id, transfer_date AS transaction_date, amount, 'transfer_out' AS transaction_type FROM transfer_outs WHERE YEAR(transfer_date)=2020
        UNION ALL
        SELECT account_id, movement_date AS transaction_date, pix_amount AS amount, 'pix_in' AS transaction_type FROM pix_movements WHERE in_or_out='pix_in' AND YEAR(movement_date)=2020
        UNION ALL
        SELECT account_id, movement_date AS transaction_date, pix_amount AS amount, 'pix_out' AS transaction_type FROM pix_movements WHERE in_or_out='pix_out' AND YEAR(movement_date)=2020
    ) t ON a.account_id = t.account_id
    GROUP BY a.account_id, YEAR(t.transaction_date), DATENAME(MONTH, t.transaction_date)
)
SELECT
    account_id,
    2020 AS Year,
    ISNULL(Jan, 0) AS Jan,
    ISNULL(Feb, 0) AS Feb,
    ISNULL(Mar, 0) AS Mar,
    ISNULL(Apr, 0) AS Apr,
    ISNULL(May, 0) AS May,
    ISNULL(Jun, 0) AS Jun,
    ISNULL(Jul, 0) AS Jul,
    ISNULL(Aug, 0) AS Aug,
    ISNULL(Sep, 0) AS Sep,
    ISNULL(Oct, 0) AS Oct,
    ISNULL(Nov, 0) AS Nov,
    ISNULL(Dec, 0) AS Dec
FROM MonthlyBalances
PIVOT (
    SUM(monthly_balance)
    FOR balance_month_name IN (Jan, Feb, Mar, Apr, May, Jun, Jul, Aug, Sep, Oct, Nov, Dec)
) AS PivotTable
UNION ALL
SELECT
    account_id,
    2020 AS Year,
    0 AS Jan, 0 AS Feb, 0 AS Mar, 0 AS Apr, 0 AS May, 0 AS Jun,
    0 AS Jul, 0 AS Aug, 0 AS Sep, 0 AS Oct, 0 AS Nov, 0 AS Dec
FROM accounts
WHERE account_id NOT IN (SELECT DISTINCT account_id FROM MonthlyBalances)
ORDER BY account_id;

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.19 02:25:41