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

SQL查询重复值问题:子查询正常主查询结果异常

主查询重复值问题的解决方法

我编写了如下主查询:

SELECT
    facturas.total AS facturas_total,
    notas_credito.total AS notas_credito_total,
    facturas.total - notas_credito.total AS total_dinero
FROM
    (SELECT
         main_venta.periodo AS periodo,
         SUM(main_venta.total) AS total
     FROM  
         main_contribuyente
     LEFT JOIN 
         main_venta ON main_venta.emisor_id = main_contribuyente.id
     LEFT JOIN 
         main_documentotipo ON main_venta.dte_id = main_documentotipo.id
     WHERE 
         main_contribuyente.id = 1
         AND EXTRACT(YEAR FROM to_date(to_char(main_venta.periodo, '999999'), 'YYYYMM')) = 2023
         AND main_documentotipo.codigo IN (30, 32, 33, 34, 110, 901, 904, 909, 910)
     GROUP BY 
         main_venta.periodo) AS facturas,
    (SELECT
         main_venta.periodo AS periodo,
         SUM(main_venta.total) AS total
     FROM 
         main_contribuyente
     LEFT JOIN 
         main_venta ON main_venta.emisor_id = main_contribuyente.id
     LEFT JOIN 
         main_documentotipo ON main_venta.dte_id = main_documentotipo.id
     WHERE 
         main_contribuyente.id = 1
         AND EXTRACT(YEAR FROM to_date(to_char(main_venta.periodo, '999999'), 'YYYYMM')) = 2023
         AND main_documentotipo.codigo IN (112, 60, 61)
     GROUP BY 
         main_venta.periodo) AS notas_credito

单独执行第一个子查询:

SELECT
    main_venta.periodo AS periodo,
    SUM(main_venta.total) AS total
FROM 
    main_contribuyente
LEFT JOIN 
    main_venta ON main_venta.emisor_id = main_contribuyente.id
LEFT JOIN 
    main_documentotipo ON main_venta.dte_id = main_documentotipo.id
WHERE 
    main_contribuyente.id = 1
    AND EXTRACT(YEAR FROM to_date(to_char(main_venta.periodo, '999999'), 'YYYYMM')) = 2023
    AND main_documentotipo.codigo IN (30, 32, 33, 34, 110, 901, 904, 909, 910)
GROUP BY 
    main_venta.periodo

结果中periodo和SUM值均正常;单独执行第二个子查询:

SELECT
    main_venta.periodo as periodo,
    SUM(main_venta.total) as total
FROM 
    main_contribuyente
LEFT JOIN 
    main_venta ON main_venta.emisor_id = main_contribuyente.id
LEFT JOIN 
    main_documentotipo ON main_venta.dte_id = main_documentotipo.id
WHERE 
    main_contribuyente.id = 1
    AND EXTRACT(YEAR FROM to_date(to_char(main_venta.periodo, '999999'), 'YYYYMM')) = 2023
    AND main_documentotipo.codigo IN (112, 60, 61)
GROUP BY 
    main_venta.periodo

结果同样正常,但执行主查询时出现了重复值问题,请问该如何解决?


问题原因

主查询中两个子查询facturas和notas_credito之间未指定连接条件,触发了笛卡尔积——即每个facturas的行都会和每个notas_credito的行配对,从而产生大量重复数据。

解决方法

方法1:通过periodo字段关联子查询

在主查询中添加基于periodo的JOIN条件,确保同一期间的记录匹配:

SELECT
    facturas.total AS facturas_total,
    COALESCE(notas_credito.total, 0) AS notas_credito_total,
    facturas.total - COALESCE(notas_credito.total, 0) AS total_dinero
FROM
    (SELECT
         main_venta.periodo AS periodo,
         SUM(main_venta.total) AS total
     FROM  
         main_contribuyente
     LEFT JOIN 
         main_venta ON main_venta.emisor_id = main_contribuyente.id
     LEFT JOIN 
         main_documentotipo ON main_venta.dte_id = main_documentotipo.id
     WHERE 
         main_contribuyente.id = 1
         AND EXTRACT(YEAR FROM to_date(to_char(main_venta.periodo, '999999'), 'YYYYMM')) = 2023
         AND main_documentotipo.codigo IN (30, 32, 33, 34, 110, 901, 904, 909, 910)
     GROUP BY 
         main_venta.periodo) AS facturas
LEFT JOIN
    (SELECT
         main_venta.periodo AS periodo,
         SUM(main_venta.total) AS total
     FROM 
         main_contribuyente
     LEFT JOIN 
         main_venta ON main_venta.emisor_id = main_contribuyente.id
     LEFT JOIN 
         main_documentotipo ON main_venta.dte_id = main_documentotipo.id
     WHERE 
         main_contribuyente.id = 1
         AND EXTRACT(YEAR FROM to_date(to_char(main_venta.periodo, '999999'), 'YYYYMM')) = 2023
         AND main_documentotipo.codigo IN (112, 60, 61)
     GROUP BY 
         main_venta.periodo) AS notas_credito
ON facturas.periodo = notas_credito.periodo;

这里用LEFT JOIN保证即使某期间无信用票据,也能保留发票记录;COALESCE将NULL值转为0,避免计算时出现异常。

方法2:合并子查询,用条件聚合简化SQL

将两个子查询合并为一个,通过CASE语句在聚合时区分发票和信用票据,减少表扫描次数:

SELECT
    main_venta.periodo AS periodo,
    SUM(CASE WHEN main_documentotipo.codigo IN (30, 32, 33, 34, 110, 901, 904, 909, 910) THEN main_venta.total ELSE 0 END) AS facturas_total,
    SUM(CASE WHEN main_documentotipo.codigo IN (112, 60, 61) THEN main_venta.total ELSE 0 END) AS notas_credito_total,
    SUM(CASE WHEN main_documentotipo.codigo IN (30, 32, 33, 34, 110, 901, 904, 909, 910) THEN main_venta.total ELSE 0 END) 
    - SUM(CASE WHEN main_documentotipo.codigo IN (112, 60, 61) THEN main_venta.total ELSE 0 END) AS total_dinero
FROM  
    main_contribuyente
LEFT JOIN 
    main_venta ON main_venta.emisor_id = main_contribuyente.id
LEFT JOIN 
    main_documentotipo ON main_venta.dte_id = main_documentotipo.id
WHERE 
    main_contribuyente.id = 1
    AND EXTRACT(YEAR FROM to_date(to_char(main_venta.periodo, '999999'), 'YYYYMM')) = 2023
GROUP BY 
    main_venta.periodo;

这种方式性能更优,同时从根源上避免了笛卡尔积问题。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.24 02:23:13