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

多表联查产生重复行问题排查及优化方案咨询

多表联查重复结果的优化去重方案

问题背景

我有payment_process、process、booking_extras三张表,用户预订附加服务时会向这三张表插入数据。当每张表各有2行数据时,执行联查SQL返回了6行重复结果。我尝试过子查询结合GROUP BY的方式解决,但不确定该方案的性能问题,希望得到更优的去重办法。

原联查SQL

SELECT
DISTINCT
p.booking_id AS PBID,
p.process_id AS PID,
p.payment_method,
p.price,
p.paid,
p.created_at,
be.km,
be.km_50,
be.km_100,
be.km_200,
be.km_300,
be.km_500,
be.to_date,
pc.text,
pc.is_owner
FROM process pc
INNER JOIN payment_process p
ON p.process_id = pc.id
LEFT OUTER JOIN booking_extras be
ON be.booking_id = p.booking_id
WHERE pc.booking_id = $1 LIMIT 100;

查询结果

[
  {
    "pbid": 2,
    "pid": 5,
    "payment_method": "100% Bar vor Ort",
    "price": 300,
    "paid": 0,
    "created_at": "2023-03-23T16:40:09.271Z",
    "km": null,
    "km_50": 1,
    "km_100": null,
    "km_200": null,
    "km_300": null,
    "km_500": null,
    "to_date": null,
    "text": "Buchung",
    "is_owner": 1
  },
  {
    "pbid": 2,
    "pid": 5,
    "payment_method": "100% Bar vor Ort",
    "price": 300,
    "paid": 0,
    "created_at": "2023-03-23T16:40:09.271Z",
    "km": null,
    "km_50": null,
    "km_100": null,
    "km_200": 1,
    "km_300": null,
    "km_500": null,
    "to_date": null,
    "text": "Buchung",
    "is_owner": 1
  },
  {
    "pbid": 2,
    "pid": 6,
    "payment_method": "100% Bar vor Ort",
    "price": 25,
    "paid": 0,
    "created_at": "2023-03-23T16:37:38.811Z",
    "km": null,
    "km_50": 1,
    "km_100": null,
    "km_200": null,
    "km_300": null,
    "km_500": null,
    "to_date": null,
    "text": "Buchung",
    "is_owner": 1
  },
  {
    "pbid": 2,
    "pid": 6,
    "payment_method": "100% Bar vor Ort",
    "price": 25,
    "paid": 0,
    "created_at": "2023-03-23T16:37:38.811Z",
    "km": null,
    "km_50": null,
    "km_100": null,
    "km_200": 1,
    "km_300": null,
    "km_500": null,
    "to_date": null,
    "text": "Buchung",
    "is_owner": 1
  },
  {
    "pbid": 2,
    "pid": 7,
    "payment_method": "100% Bar vor Ort",
    "price": 100,
    "paid": 0,
    "created_at": "2023-03-23T16:43:02.549Z",
    "km": null,
    "km_50": 1,
    "km_100": null,
    "km_200": null,
    "km_300": null,
    "km_500": null,
    "to_date": null,
    "text": "Buchung",
    "is_owner": 1
  },
  {
    "pbid": 2,
    "pid": 7,
    "payment_method": "100% Bar vor Ort",
    "price": 100,
    "paid": 0,
    "created_at": "2023-03-23T16:43:02.549Z",
    "km": null,
    "km_50": null,
    "km_100": null,
    "km_200": 1,
    "km_300": null,
    "km_500": null,
    "to_date": null,
    "text": "Buchung",
    "is_owner": 1
  }
]

现有解决方案SQL

SELECT
pc.text,
pc.is_owner,
payment_process.booking_id, payment_process.process_id, payment_process.price, payment_process.paid, payment_process.payment_method, payment_process.created_at,
booking_extras.km,
booking_extras.km_50,
booking_extras.km_100,
booking_extras.km_200,
booking_extras.km_300,
booking_extras.km_500,
booking_extras.to_date
FROM process pc
INNER JOIN (
  SELECT 
    payment_process.booking_id, 
    payment_process.process_id, 
    payment_process.price,
    payment_process.paid,
    payment_process.payment_method,
    payment_process.created_at
  FROM payment_process
   GROUP BY payment_process.booking_id, payment_process.process_id, payment_process.price, payment_process.paid, payment_process.payment_method, payment_process.created_at
) payment_process ON payment_process.process_id = pc.id
LEFT OUTER JOIN (
  SELECT 
    booking_extras.id, 
    booking_extras.km, 
    booking_extras.km_50,
    booking_extras.km_100,
    booking_extras.km_200,
    booking_extras.km_300,
    booking_extras.km_500,
    booking_extras.to_date
    FROM booking_extras
     GROUP BY 
     booking_extras.id, 
     booking_extras.km, 
     booking_extras.km_50,
     booking_extras.km_100,
     booking_extras.km_200,
     booking_extras.km_300,
     booking_extras.km_500,
     booking_extras.to_date
) booking_extras ON pc.booking_extras_id = booking_extras.id
WHERE pc.booking_id = $1 GROUP BY
 pc.id, pc.text, pc.is_owner,
  payment_process.booking_id, payment_process.process_id, payment_process.price, payment_process.paid, payment_process.payment_method, payment_process.created_at,
  booking_extras.km,
  booking_extras.km_50,
  booking_extras.km_100,
  booking_extras.km_200,
  booking_extras.km_300,
  booking_extras.km_500,
  booking_extras.to_date
LIMIT 50;

问题分析与优化方案

重复原因

原SQL的核心问题是关联条件错误:booking_extras通过booking_id和payment_process关联,当一个booking_id对应多条booking_extras和多条payment_process记录时,会产生笛卡尔积,导致结果重复。而你的现有方案中已经用到了pc.booking_extras_id = booking_extras.id这个正确关联,说明原SQL的关联逻辑本身就错了。

现有方案的性能问题

现有方案中的子查询GROUP BY完全冗余——没有使用聚合函数,只是将所有字段分组,效果等价于DISTINCT,但会额外生成临时表增加IO开销;外层又重复执行GROUP BY,进一步降低查询效率。

优化方案

方案1:修正关联条件(最优)

直接使用process和booking_extras的直接关联字段,从根源避免笛卡尔积:

SELECT DISTINCT
    p.booking_id AS PBID,
    p.process_id AS PID,
    p.payment_method,
    p.price,
    p.paid,
    p.created_at,
    be.km,
    be.km_50,
    be.km_100,
    be.km_200,
    be.km_300,
    be.km_500,
    be.to_date,
    pc.text,
    pc.is_owner
FROM process pc
INNER JOIN payment_process p ON p.process_id = pc.id
LEFT OUTER JOIN booking_extras be ON be.id = pc.booking_extras_id -- 修正关联条件
WHERE pc.booking_id = $1 
LIMIT 100;

方案2:窗口函数去重(保留原关联逻辑时使用)

如果业务上必须通过booking_id关联,可使用窗口函数按唯一标识分组取第一条,避免重复:

SELECT 
    PBID, PID, payment_method, price, paid, created_at,
    km, km_50, km_100, km_200, km_300, km_500, to_date,
    text, is_owner
FROM (
    SELECT
        p.booking_id AS PBID,
        p.process_id AS PID,
        p.payment_method,
        p.price,
        p.paid,
        p.created_at,
        be.km,
        be.km_50,
        be.km_100,
        be.km_200,
        be.km_300,
        be.km_500,
        be.to_date,
        pc.text,
        pc.is_owner,
        -- 按process_id和booking_extras.id分组,取每组第一条
        ROW_NUMBER() OVER(PARTITION BY p.process_id, be.id ORDER BY p.created_at) AS rn
    FROM process pc
    INNER JOIN payment_process p ON p.process_id = pc.id
    LEFT OUTER JOIN booking_extras be ON be.booking_id = p.booking_id
    WHERE pc.booking_id = $1
) t
WHERE rn = 1
LIMIT 100;

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.27 11:58:11