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

PostgreSQL中SUM与GROUP BY避免同记录重复聚合的方案

多关联场景下的聚合查询问题解决

数据表说明

系统包含4张数据表:

  • product(产品表)
  • projecteddata(预计数据表)
  • payoutblock(支付关联表)
  • users(用户表)

产品表(product)

CREATE TABLE IF NOT EXISTS public.product
(
    id integer NOT NULL DEFAULT nextval('product_id_seq'::regclass),
    "productType" character varying COLLATE pg_catalog."default" NOT NULL, -- 产品类型
    amount numeric NOT NULL DEFAULT '0'::numeric, -- 金额
    "startDate" date NOT NULL -- 起始日期
)

预计数据表(projecteddata)

CREATE TABLE IF NOT EXISTS public.projecteddata
(
    id integer NOT NULL DEFAULT nextval('projecteddata'::regclass),
    "productType" character varying COLLATE pg_catalog."default" NOT NULL, -- 产品类型
    amount numeric NOT NULL DEFAULT '0'::numeric, -- 金额
    "startDate" date NOT NULL, -- 起始日期
    "systemGeneratedStartDate" date NOT NULL, -- 系统生成起始日期
    "systemGeneratedEndDate" date NOT NULL, -- 系统生成结束日期
    "productId" integer, -- 关联产品ID
    CONSTRAINT "FK_c6c14f315490aceb2f7a9ffb3ed" FOREIGN KEY ("productId")
        REFERENCES public.product (id) MATCH SIMPLE
        ON UPDATE NO ACTION
        ON DELETE NO ACTION
)

支付关联表(payoutblock)

CREATE TABLE IF NOT EXISTS public.payoutblock
(
    id integer NOT NULL DEFAULT nextval('payoutblock_id_seq'::regclass),
    "productId" integer, -- 关联产品ID
    "payeeId" uuid, -- 收款人ID
    CONSTRAINT "FK_1bc960a61d22c0daf3efd691d70" FOREIGN KEY ("productId")
        REFERENCES public.product (id) MATCH SIMPLE
        ON UPDATE NO ACTION
        ON DELETE NO ACTION,
    CONSTRAINT "FK_e6f4e4b0f0750910b39ec67f816" FOREIGN KEY ("payeeId")
        REFERENCES public.users (id) MATCH SIMPLE
        ON UPDATE NO ACTION
        ON DELETE NO ACTION
)

用户表(users)

CREATE TABLE IF NOT EXISTS public.users
(
    id uuid NOT NULL DEFAULT uuid_generate_v4(), -- 用户ID(主键)
    CONSTRAINT "PK_a3ffb1c0c8416b9fc6f907b7433" PRIMARY KEY (id)
)

示例数据

产品表插入数据

INSERT INTO public.product(
    id, "productType", amount, "startDate")
    VALUES (1, 'ABCD', 100, '2022-10-02'), -- 产品ID1,类型ABCD,金额100,起始日期2022-10-02
    VALUES (2, 'XYZ', 100, '2022-12-14'); -- 产品ID2,类型XYZ,金额100,起始日期2022-12-14

支付关联表插入数据

INSERT INTO public.payoutblock(
    id,"productId", "payeeId")
    VALUES (1, 1, '0005c2b6-29f2-491f-8565-c30567f232f9'), -- 关联产品1,收款人ID0005c2b6...
        VALUES (2, 1 , 'ec5e7cbb-2f82-4b05-8135-73746b59afb0'), -- 关联产品1,收款人IDec5e7cbb...
    VALUES (3, 2, '0005c2b6-29f2-491f-8565-c30567f232f9'); -- 关联产品2,收款人ID0005c2b6...

用户表插入数据

INSERT INTO public.users(
    id)
    VALUES ('0005c2b6-29f2-491f-8565-c30567f232f9'),
    ('ec5e7cbb-2f82-4b05-8135-73746b59afb0');

预计数据表插入数据

INSERT INTO public.projecteddata(
    id, "productType", amount, "startDate", "systemGeneratedStartDate", "systemGeneratedEndDate",
    "productId")
    VALUES (1, 'ABCD', 100, '2022-10-02', '2022-10-01', '2022-10-31', 1),
    VALUES (2, 'ABCD', 100, '2022-10-02', '2022-11-01', '2022-11-30', 1),
    VALUES (3, 'XYZ', 100, '2022-12-14', '2022-12-01', '2022-12-31', 2),
    VALUES (4, 'ABCD', 100, '2022-10-02', '2022-12-01', '2022-12-31', 1),
    VALUES (5, 'XYZ', 100, '2022-12-14', '2023-01-01','2023-01-31', 2),
    VALUES (6, 'ABCD', 100, '2022-10-02', '2023-01-01', '2023-01-31', 1),
    VALUES (7, 'XYZ', 100, '2022-12-14', '2023-02-01', '2023-02-28', 2),
    VALUES (8, 'ABCD', 100, '2022-10-02', '2023-02-01', '2023-02-28', 1);

问题描述

执行以下包含SUM和GROUP BY的查询时,未得到预期结果:

SELECT sum(rpd."amount"),to_char(rpd."systemGeneratedStartDate", 'YYYY-MM-DD') as "systemGeneratedStartDate",
            to_char(rpd."systemGeneratedEndDate", 'YYYY-MM-DD') as "systemGeneratedEndDate"
            FROM "projecteddata" rpd
            left join public.product rp
            on rpd."productId" = rp."id"
            left join public.payoutblock pb
            on pb."productId" = rp."id"
            WHERE "systemGeneratedStartDate" >= '2022-10-01' AND "systemGeneratedStartDate" < '2024-04-01' 
             AND (pb."payeeId" IN ( 'ec5e7cbb-2f82-4b05-8135-73746b59afb0' , '0005c2b6-29f2-491f-8565-c30567f232f9' )) 
            GROUP BY "systemGeneratedStartDate","systemGeneratedEndDate",rp."id"
                    ORDER BY "systemGeneratedStartDate" ASC

当前输出中每个时间区间的sum值为200,而非预期的100。尝试将payeeId加入GROUP BY后,sum值恢复正确,但每个时间区间会出现两行重复数据。

问题根源在于payoutblock表中单个产品对应多个收款人ID,导致关联查询时数据被重复展开,最终聚合计算时金额被重复累加。需求是:即使一个产品关联多个收款人,每个时间区间的sum值仍保持原始金额,且每个时间区间仅返回一行数据。

预期输出:

sumsystemGeneratedStartDatesystemGeneratedEndDate
100"2022-12-01""2022-12-31"
100"2023-02-01""2023-02-28"
100"2023-03-01""2023-03-31"
100"2023-05-01""2023-05-31"
100"2023-06-01""2023-06-30"
100"2023-07-01""2023-07-31"
100"2023-08-01""2023-08-31"
100"2023-09-01""2023-09-30"

解决方案

方案1:使用EXISTS过滤替代JOIN

payoutblock仅用于筛选关联指定收款人的产品,无需关联展开数据,用EXISTS子查询避免数据重复:

SELECT 
    sum(rpd."amount") AS sum,
    to_char(rpd."systemGeneratedStartDate", 'YYYY-MM-DD') AS "systemGeneratedStartDate",
    to_char(rpd."systemGeneratedEndDate", 'YYYY-MM-DD') AS "systemGeneratedEndDate"
FROM "projecteddata" rpd
LEFT JOIN public.product rp ON rpd."productId" = rp."id"
WHERE 
    rpd."systemGeneratedStartDate" >= '2022-10-01' 
    AND rpd."systemGeneratedStartDate" < '2024-04-01'
    AND EXISTS (
        SELECT 1 
        FROM public.payoutblock pb 
        WHERE pb."productId" = rp."id"
        AND pb."payeeId" IN ('ec5e7cbb-2f82-4b05-8135-73746b59afb0', '0005c2b6-29f2-491f-8565-c30567f232f9')
    )
GROUP BY rpd."systemGeneratedStartDate", rpd."systemGeneratedEndDate", rp."id"
ORDER BY rpd."systemGeneratedStartDate" ASC;

方案2:先聚合再关联过滤

先对projecteddata按时间区间和产品ID聚合,再关联过滤,避免提前关联导致的数据重复:

WITH aggregated_projected AS (
    SELECT 
        "productId",
        "systemGeneratedStartDate",
        "systemGeneratedEndDate",
        sum("amount") AS amount
    FROM "projecteddata"
    WHERE "systemGeneratedStartDate" >= '2022-10-01' 
        AND "systemGeneratedStartDate" < '2024-04-01'
    GROUP BY "productId", "systemGeneratedStartDate", "systemGeneratedEndDate"
)
SELECT 
    ap.amount AS sum,
    to_char(ap."systemGeneratedStartDate", 'YYYY-MM-DD') AS "systemGeneratedStartDate",
    to_char(ap."systemGeneratedEndDate", 'YYYY-MM-DD') AS "systemGeneratedEndDate"
FROM aggregated_projected ap
LEFT JOIN public.product rp ON ap."productId" = rp."id"
WHERE EXISTS (
    SELECT 1 
    FROM public.payoutblock pb 
    WHERE pb."productId" = rp."id"
    AND pb."payeeId" IN ('ec5e7cbb-2f82-4b05-8135-73746b59afb0', '0005c2b6-29f2-491f-8565-c30567f232f9')
)
ORDER BY ap."systemGeneratedStartDate" ASC;

内容的提问来源于Stack Exchange,提问作者Ayush Singh Gaur

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.16 10:55:21