聚合查询的左外连接

时间:2013-12-12 15:29:05

标签: sql oracle aggregate cartesian

所以我想在Oracle SQL DB中比较两个支付表。我想比较使用地点和发票以及总付款的总付款。它更复杂,但基本上是:

select
  tbl1.location,
  tbl1.invoice,
  Sum(tbl1.payments),
  Sum(tbl2.payments)    
From 
  tbl1    
  left outer join tbl2 on 
    tbl1.location = tbl2.location 
    and tbl1.invoice = tbl2.invoice    
group by 
  (tbl1.location,tbl1.invoice)

我想要左外连接,因为除了比较付款金额外,我想查看tbl1中可能不存在于tbl2中的所有订单。

问题在于,两个表中的每个订单(位置和发票)都有多个记录(不一定是相同数量的记录,即tbl1中的2个到tbl2中的1个,反之亦然)但总付款每个订单(地点和发票)应匹配。所以只是直接加入给我一个笛卡尔积。

所以我想我可以做两个查询,首先按商店和商店汇总付款总额。每个发票,然后对这些结果进行联接,因为在汇总结果中,每个订单(商店和发票)只有一条记录。但我不知道该怎么做。我已经尝试了几个子查询,但似乎无法动摇笛卡尔积。我希望能够在一个查询中执行此操作,而不是创建表并加入这些表,因为这将持续进行。

提前感谢您的帮助。

3 个答案:

答案 0 :(得分:1)

您可以使用With语句创建两个查询,然后按照您的说法加入。我会放下sintaxe,如果你需要更多的帮助,请问。那是因为你没有提供你的桌子的完整细节。所以我会猜测我的答案。

WITH tmpTableA as ( 
        select
          tbl1.location,
          tbl1.invoice,
          Sum(tbl1.payments) totalTblA
        From 
          tbl1
        group by 
          tbl1.location,
          tbl1.invoice
         ),
   tmpTableB as ( 
        select
          tbl2.location,
          tbl2.invoice,
          Sum(tbl2.payments) totalTblB
        From 
          tbl2
        group by 
          tbl2.location,
          tbl2.invoice
         )
Select tmpTableA.location,  tmpTableA.invoice, tmpTableA.totalTblA,
       tmpTableB.location,  tmpTableB.invoice, tmpTableB.totalTblB
  from tmpTableA, tmpTableB
 where tmpTableA.location = tmpTableB.location (+)
   and tmpTableA.invoice = tmpTableB.invoice (+)

(+)运算符是Oracle数据库的left join运算符(当然,如果您愿意,可以使用LEFT JOIN语句)

答案 1 :(得分:0)

对不起,我的第一个回答是错误的。感谢您提供sqlfiddle,MT0。

我错过的一点是你需要首先总结每张桌子上的付款,所以每张桌子只留下一行,然后加入。这就是MT0在他的陈述中所做的。

如果您想要一个看起来更“对称”的解决方案,请尝试:

select A.location, A.invoice, B.total sum1, C.total sum2
from (select distinct location, invoice from tbl1) A
left outer join (select location, invoice, sum(payments) as total from tbl1 group by location, invoice) B on A.location=B.location and A.invoice=B.invoice
left outer join (select location, invoice, sum(payments) as total from tbl2 group by location, invoice) C on A.location=C.location and A.invoice=C.invoice

导致

LOCATION    INVOICE SUM1    SUM2
a           2       3       2
a           1       5       3
b           1       1       5
b           2       1       (null)

答案 2 :(得分:0)

另外两个选择:

SQL Fiddle

Oracle 11g R2架构设置

CREATE TABLE tbl1 ( id, location, invoice, payments ) AS 
          SELECT  1, 'a', 1, 1 FROM DUAL
UNION ALL SELECT  2, 'a', 1, 1 FROM DUAL
UNION ALL SELECT  3, 'a', 1, 1 FROM DUAL
UNION ALL SELECT  4, 'a', 1, 1 FROM DUAL
UNION ALL SELECT  5, 'a', 1, 1 FROM DUAL
UNION ALL SELECT  6, 'a', 2, 1 FROM DUAL
UNION ALL SELECT  7, 'a', 2, 1 FROM DUAL
UNION ALL SELECT  8, 'a', 2, 1 FROM DUAL
UNION ALL SELECT  9, 'b', 1, 1 FROM DUAL
UNION ALL SELECT 10, 'b', 2, 1 FROM DUAL;

CREATE TABLE tbl2 ( id, location, invoice, payments ) AS 
          SELECT  1, 'a', 1, 1 FROM DUAL
UNION ALL SELECT  2, 'a', 1, 1 FROM DUAL
UNION ALL SELECT  3, 'a', 1, 1 FROM DUAL
UNION ALL SELECT  4, 'a', 2, 1 FROM DUAL
UNION ALL SELECT  5, 'a', 2, 1 FROM DUAL
UNION ALL SELECT  6, 'b', 1, 1 FROM DUAL
UNION ALL SELECT  7, 'b', 1, 1 FROM DUAL
UNION ALL SELECT  8, 'b', 1, 1 FROM DUAL
UNION ALL SELECT  9, 'b', 1, 1 FROM DUAL
UNION ALL SELECT 10, 'b', 1, 1 FROM DUAL;

查询1

这个使用相关的子查询来计算第二个表的总数:

SELECT location,
       invoice,
       SUM( payments ) AS total_payments_1,
       COALESCE( (SELECT SUM( payments )
                  FROM   tbl2 i
                  WHERE  o.location = i.location
                     AND o.invoice  = i.invoice),
                 0 ) AS total_payments_2
FROM   tbl1 o
GROUP BY
       location,
       invoice
ORDER BY
       location,
       invoice

<强> Results

| LOCATION | INVOICE | TOTAL_PAYMENTS_1 | TOTAL_PAYMENTS_2 |
|----------|---------|------------------|------------------|
|        a |       1 |                5 |                3 |
|        a |       2 |                3 |                2 |
|        b |       1 |                1 |                5 |
|        b |       2 |                1 |                0 |

查询2

这个使用命名子查询预先计算表1的总数,然后使用第二个表执行LEFT OUTER JOIN,并包括组中表1的总数。

如果没有任何索引,从解释计划中,查询1似乎效率更高,但您的索引可能意味着优化程序找到了更好的计划。

WITH tbl1_sums AS (
  SELECT location,
         invoice,
         SUM( payments ) AS total_payments_1
  FROM   tbl1
  GROUP BY
         location,
         invoice
)
SELECT t1.location,
       t1.invoice,
       t1.total_payments_1,
       COALESCE( SUM( t2.payments ), 0 ) AS total_payments_2
FROM   tbl1_sums t1
       LEFT OUTER JOIN 
       tbl2 t2
       ON (    t1.location = t2.location
           AND t1.invoice = t2.invoice)
GROUP BY
       t1.location,
       t1.invoice,
       t1.total_payments_1
ORDER BY
       t1.location,
       t1.invoice

<强> Results

| LOCATION | INVOICE | TOTAL_PAYMENTS_1 | TOTAL_PAYMENTS_2 |
|----------|---------|------------------|------------------|
|        a |       1 |                5 |                3 |
|        a |       2 |                3 |                2 |
|        b |       1 |                1 |                5 |
|        b |       2 |                1 |                0 |