【问题标题】:Left outer join on aggregate queries聚合查询的左外连接
【发布时间】:2013-12-13 05:08:49
【问题描述】:

所以我有两个支付表,我想在 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 或反之亦然),但总付款每个订单(位置和发票)都应该匹配。因此,只需进行直接连接就可以得到笛卡尔积。

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

提前感谢您的帮助。

【问题讨论】:

    标签: sql oracle aggregate cartesian


    【解决方案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 语句)

    【讨论】:

    • @user3095951 很高兴为您提供帮助。如果它适合您,请不要忘记接受答案,它可以帮助其他人看到相同问题的答案:)
    【解决方案2】:

    另外两个选项:

    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 |
    

    【讨论】:

    • 非常感谢 MTO! Jorges 的回答似乎起到了与您在 Query 2 中所做的非常相似的作用。我将尝试您的解决方案以及提取数十万条记录,因此效率绝对重要。我真的需要磨练我的子查询技能。以几种方式看待它肯定会在这方面有所帮助。再次感谢!
    • 如果您单击SQLFIDDLE,那么其中包含我的查询和 Jorge 的回答。每个结果集中都有一个指向解释计划的链接(解释计划的表格视图可能更有用),您可以看到成本。如果没有任何索引,那么我的查询 1 看起来是最有效的(无论是在成本还是字节方面),其次是 Jorge 然后最差的效率似乎是我的查询 2 - 但是它可能会随着表上的适当索引而改变。
    • 感谢 MTO。这很有帮助!今天学到了很多。感谢您花时间在这上面:)
    【解决方案3】:

    对不起,我的第一个答案是错误的。感谢您提供 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)
    

    【讨论】:

    • SQLFIDDLE - 您可以在此示例中看到行的笛卡尔积。
    猜你喜欢
    • 2022-01-21
    • 1970-01-01
    • 2020-02-04
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2015-03-11
    • 1970-01-01
    • 2012-11-21
    相关资源
    最近更新 更多