SQL sum函数一对多场景,消除笛卡尔积

场景:借还款统计数据,一笔贷款可以对应多笔还款。

贷款表

DROP TABLE IF EXISTS `load_contract`;
CREATE TABLE `load_contract`  (
  `load_id` bigint(0) UNSIGNED NOT NULL AUTO_INCREMENT COMMENT '贷款ID',
  `load_amt` decimal(10, 2) UNSIGNED NULL DEFAULT NULL COMMENT '贷款金额',
  `persion` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NULL DEFAULT NULL COMMENT '贷款人',
  PRIMARY KEY (`load_id`) USING BTREE
) ENGINE = InnoDB AUTO_INCREMENT = 6 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_0900_ai_ci ROW_FORMAT = Dynamic;

-- ----------------------------
-- Records of load_contract
-- ----------------------------
INSERT INTO `load_contract` VALUES (5, 50.00, 'zzl');
INSERT INTO `load_contract` VALUES (8, 60.00, 'zzl');
INSERT INTO `load_contract` VALUES (10, 20.00, 'zzy');

还款表

DROP TABLE IF EXISTS `repayment_contract`;
CREATE TABLE `repayment_contract`  (
  `repay_id` bigint(0) NOT NULL AUTO_INCREMENT COMMENT '还款ID',
  `repay_amt` decimal(10, 2) UNSIGNED NULL DEFAULT NULL COMMENT '还款金额',
  `load_id` bigint(0) UNSIGNED NOT NULL COMMENT '贷款ID',
  PRIMARY KEY (`repay_id`) USING BTREE
) ENGINE = InnoDB AUTO_INCREMENT = 4 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_0900_ai_ci ROW_FORMAT = Dynamic;

-- ----------------------------
-- Records of repayment_contract
-- ----------------------------
INSERT INTO `repayment_contract` VALUES (1, 10.00, 5);
INSERT INTO `repayment_contract` VALUES (2, 10.00, 5);
INSERT INTO `repayment_contract` VALUES (3, 10.00, 5);
INSERT INTO `repayment_contract` VALUES (4, 15.00, 8);
INSERT INTO `repayment_contract` VALUES (5, 20.00, 8);
INSERT INTO `repayment_contract` VALUES (6, 5.00, 10);

其中还款表使用load_id作为外键与贷款表关联。

贷款表数据

SELECT * FROM load_contract lc 
load_id load_amt persion
5 50.00 zzl
8 60.00 zzl
10 20.00 zzy

还款表数据

SELECT * FROM repayment_contract rc;
repay_id repay_amt load_id
1 10.00 5
2 10.00 5
3 10.00 5
4 15.00 8
5 20.00 8
6 5.00 10
  • 场景一:统计某人的总共贷款金额。
SELECT SUM(load_amt) FROM load_contract WHERE persion='zzl';

结果为 110.00

  • 场景二:统计某人已还款金额
SELECT
    SUM( rc.repay_amt ) 
FROM
    repayment_contract rc,
    load_contract lc 
WHERE
    lc.load_id = rc.load_id 
    AND lc.persion = 'zzl';

结果为 65.00

  • 场景三:关联还款表,统计贷款金额(这种情况先不考虑具体的使用场景)。
    SELECT
    SUM( lc.load_amt ) 
FROM
    repayment_contract rc,
    load_contract lc 
WHERE
    lc.load_id = rc.load_id 
    AND lc.persion = 'zzl';

结果为 270.00
关联情况下,贷款表查询出的实际数据如下,可以看出这种情况下出现了笛卡尔积。

SELECT
     lc.*
FROM
    repayment_contract rc,
    load_contract lc 
WHERE
    lc.load_id = rc.load_id 
    AND lc.persion = 'zzl';
load_id load_amt persion
5 50.00 zzl
5 50.00 zzl
5 50.00 zzl
8 60.00 zzl
8 60.00 zzl

load_id为5的贷款对应3条还款,load_id为8的贷款对应2条还款。所以sum(lc.load_amt)的结果270 = 50 * 3 + 60*2。

解决方案,使用 DISTINCT 关键字。

SELECT
    SUM( load_amt ) 
FROM
    ( SELECT DISTINCT lc.* FROM 
    repayment_contract rc, load_contract lc 
    WHERE lc.load_id = rc.load_id AND lc.persion = 'zzl' ) t;
    

结果 110.00

  • 场景四:同时统计贷款和还款总额。
SELECT
    SUM( lc.load_amt ),
    SUM( rc.repay_amt ) 
FROM
    repayment_contract rc,
    load_contract lc 
WHERE
    lc.load_id = rc.load_id 
    AND lc.persion = 'zzl';

结果为 270.00 , 65.00。
跟场景三一样,贷款金额出现了笛卡尔积

这种情况可以分两步走

  1. 第一步,使用子查询,查询出每笔贷款对应的还款金额
SELECT
    lc.load_amt ,
    ( SELECT SUM( rc.repay_amt ) FROM repayment_contract rc WHERE
    lc.load_id = rc.load_id )  totalRepayAmt
FROM
    load_contract lc 
WHERE
    lc.persion = 'zzl';

load_amt totalRepayAmt
50.00 30.00
60.00 35.00
  1. 第二步,重新汇总数据
SELECT
    SUM( lc.load_amt ) totalLoadAmt,
    SUM( ( SELECT SUM( rc.repay_amt ) FROM 
    repayment_contract rc WHERE lc.load_id = rc.load_id ) ) totalRepayAmt 
FROM
    load_contract lc 
WHERE
    lc.persion = 'zzl';
totalLoadAmt totalRepayAmt
110.00 65.00

这种方式可能不是很好理解,换成子查询方式。

SELECT
    SUM( totalLoadAmt ),
    SUM( totalRepayAmt ) 
FROM
    (
    SELECT
        lc.load_amt totalLoadAmt,
        ( SELECT SUM( rc.repay_amt ) FROM repayment_contract rc
         WHERE lc.load_id = rc.load_id ) totalRepayAmt 
    FROM
        load_contract lc 
    WHERE
        lc.persion = 'zzl' 
    ) t;
  • 场景五:统计某人贷款余额(还没还的贷款)
SELECT SUM(balanceAmt) totalBalanceAmt FROM (SELECT
 load_amt - ( SELECT SUM( rc.repay_amt ) FROM repayment_contract rc 
 WHERE lc.load_id = rc.load_id ) balanceAmt
FROM
    load_contract lc 
WHERE
    lc.persion = 'zzl') t

结果

totalBalanceAmt
45.00
  • 场景六:统计所有人的贷款余额(还没还的贷款)
    
SELECT
    persion,
    SUM( balanceAmt ) totalBalanceAmt 
FROM
    (
    SELECT
        lc.persion persion,
        load_amt - ( SELECT SUM( rc.repay_amt ) FROM repayment_contract rc WHERE lc.load_id = rc.load_id ) balanceAmt 
    FROM
        load_contract lc 
    ) t 
GROUP BY
    t.persion

结果

persion totalBalanceAmt
zzl 45.00
zzy 15.00
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 204,793评论 6 478
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 87,567评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 151,342评论 0 338
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,825评论 1 277
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,814评论 5 368
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,680评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,033评论 3 399
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,687评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 42,175评论 1 300
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,668评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,775评论 1 332
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,419评论 4 321
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,020评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,978评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,206评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,092评论 2 351
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,510评论 2 343

推荐阅读更多精彩内容