【问题标题】:Oracle SQL - Left Join on 1:M relationship between tables and incorrect data outputOracle SQL - 表之间的 1:M 关系的左连接和不正确的数据输出
【发布时间】:2017-08-18 20:56:21
【问题描述】:

我想使用左连接来连接多个表。我目前在连接中得到重复的记录和不一致的数据。有任何想法吗?

我相信我找到了解决重复记录的方法,但我不确定它是否可靠。

在此示例中,下订单时,它应附加到与订单关联的任何项目以及与发票关联的任何客户。 Items 和 Client 表基本上是静态的,只有在添加新的 Items 或 Clients 时才会更新,这并不常见。

这是一个小规模的例子,整个数据集大约有 600 万行。

Orders 与 Orders Detail Level 具有 1:M 的关系

Invoice 与 Orders 具有 M:1 关系,与 Orders Detail Level 具有 M:M 关系

Client 仅与 Invoice 以 1:1 的关系关联

Items 与 Orders Detail 具有 1:M 关系

SELECT DISTINCT row_number() over (partition by a.apk order by a.apk desc) as SEQNUM FROM table WHERE SEQNUM = 1

目前的解决方案

SELECT DISTINCT
a.apk
,a.DATE
,bc.CATEGORY
,bc.ITEMNAME
,bc.amt
,d.clientname
,e.invoiceid
,row_number() over (partition by a.apk order by a.apk desc) as SEQNUM
FROM atable a 
LEFT JOIN 
    (SELECT DISTINCT
     tableb.amt
     ,tableb.apk
     ,tablec.CATEGORY
     ,tablec.ITEMNAME
     FROM tableb b 
        LEFT JOIN tablec c ON (b.cpk = c.cpk)) bc
     ON (a.apk = bc.apk)
LEFT JOIN 
    (SELECT DISTINCT
     d.clientname
     ,e.invoiceid
     ,e.epk
     ,d.dpk
     FROM tabled d 
        LEFT JOIN tableE e ON (d.dpk = e.epk)) ed
     ON (a.apk = ed.apk)
WHERE SEQNUM = 1

样本数据

表A(订单)Primary Key: apk

apk  DATE      
1    8/17/17   

表 B(订单详细级别)Primary Key: bpk

bpk  apk  cpk amt
1    1    1   5
2    1    2   100

表 C(项目)Primary Key: cpk

cpk ITEMNAME     ITEMID  CATEGORY    
1   Tape         1234    Office Supplies   
2   Toner        5678    Printer Supplies

表 D(客户)Primary Key: dpk

dpk  CLIENTNAME
5    STAPLES
6    WALMART

表E(发票)Primary Key: epk

epk apk dpk INVOICEID  INVOICE_DATE
1   1   5   776        8/18/17
2   1   6   934        8/19/17

当前结果

apk DATE    CATEGORY        ITEMNAME  amt    CLIENTNAME    INVOICEID
1   8/17/17 Office Supplies Tape      5      Staples       776       
1   8/17/17 Office Supplies Tape      100    Walmart       934
1   8/17/17 Office Supplies Tape      5      Staples       776       
1   8/17/17 Office Supplies Tape      100    Walmart       934
1   8/17/17 Office Supplies Tape      100    Walmart       934       
1   8/17/17 Office Supplies Tape      100    Walmart       934

预期结果

apk DATE    CATEGORY         ITEMNAME     amt    CLIENTNAME    INVOICEID
1   8/17/17 Office Supplies  Tape         5      Staples       776       
1   8/17/17 Printer Supplies Toner        100    Walmart       934

提前致谢!

编辑:数据,澄清

【问题讨论】:

  • 我不明白您如何决定在预期结果的最后两个属性中选择值。 apk1 有两种选择(订书钉和wallmart),那为什么第一排有订书钉,第二排是walmart,第二排是even bounty(加上934属于walmart)?
  • 实际上,如果您只执行内部连接,那么您将获得 6 行。从您的回答中不清楚您的预期结果是如何产生的。
  • 这个问题毫无意义。例如,在您的表数据中,“Tape”的 cpk=1,其中一个 amt 值已知:3,但在您当前和所需的输出中,您将“Tape”与 5 的数量联系起来。“Paper towels”仅与数量为 100,但在您想要的输出中您想要 3!?
  • @RadimBača 这就是我的数据设置方式。例如,一张发票可能包含多个项目,每个项目都与一个客户相关联。我试图复制它在我身上的样子。为那里的任何混乱道歉。为了澄清,我删除了第三项。
  • @trincot 我已经删除了第三项并进行了相应的调整。我只是对为什么在提取任何数据时会在所有行上提取不正确的项目名称和类别感到困惑。

标签: sql oracle join distinct


【解决方案1】:

我认为您应该将bpk 添加到invoice 表或将epk 添加到order details 以在这些表之间创建连接。否则我们不知道碳粉是由 Staples 还是 Wallmart 订购的。

但在当前数据模型中,假设order details 中的第一项与invoice 中的第一项匹配,我们可以使用row_number()。 “第一”是指较低的 id:

select apk, adate, category, itemname, amt, clientname, invoiceid
  from a
  join (select b.*, row_number() over (partition by apk order by bpk) rn 
          from b) b using (apk)
  join c using (cpk)
  join (select e.*, row_number() over (partition by apk order by epk) rn 
          from e) e using (apk, rn)
  join d using (dpk)

演示:

with 
    -- orders
    a (apk, adate) as (  
        select 1, date '2017-08-17' from dual),
    -- order details
    b (bpk, apk, cpk, amt) as (
        select 1, 1, 1,   5 from dual union all
        select 2, 1, 2, 100 from dual),
    -- items
    c (cpk, itemname, itemid, category) as (
        select 1, 'Tape',  1234, 'Office Supplies'  from dual union all
        select 2, 'Toner', 5678, 'Printer Supplies' from dual),
    -- clients
    d (dpk, clientname) as (
        select 5, 'Staples'  from dual union all
        select 6, 'Wallmart' from dual),
    -- invoice
    e (epk, apk, dpk, invoiceid) as (
        select 1, 1, 5, 776 from dual union all
        select 2, 1, 6, 934 from dual)
select apk, adate, category, itemname, amt, clientname, invoiceid
  from a
  join (select b.*, row_number() over (partition by apk order by bpk) rn
          from b) b using (apk)
  join c using (cpk)
  join (select e.*, row_number() over (partition by apk order by epk) rn
          from e) e using (apk, rn)
  join d using (dpk)

结果:

   APK ADATE       CATEGORY         ITEMNAME        AMT CLIENTNAME  INVOICEID
------ ----------- ---------------- -------- ---------- ---------- ----------
     1 2017-08-17  Office Supplies  Tape              5 Staples           776
     1 2017-08-17  Printer Supplies Toner           100 Wallmart          934

【讨论】:

    【解决方案2】:

    如果我们假设 INVOICE 表有 cpk 列

    Table E (Invoice) Primary Key: epk
    
    epk apk cpk dpk INVOICEID
    1   1   1   5   776
    2   1   2   6   934
    

    SQL 查询将是

    SELECT os.apk,
           os.ord_dt,
           it.category,
           it.itemname,
           od.amt,
           cs.clientname,
           ie.invoiceid
      FROM orders os
     INNER JOIN order_dtl od
        ON os.apk = od.apk
     INNER JOIN items it
        ON od.cpk = it.cpk
     INNER JOIN invoice ie
        ON ie.apk = od.apk
       AND ie.cpk = od.cpk
     INNER JOIN clients cs
        ON cs.dpk = ie.dpk;
    

    结果

           APK ORD_DT    CATEGORY             ITEMNAME          AMT CLIENTNAME  INVOICEID
    ---------- --------- -------------------- ---------- ---------- ---------- ----------
             1 17-AUG-17 Office Supplies      Tape                5 STAPLES           776
             1 17-AUG-17 Printer Supplies     Toner             100 WALMART           934
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2017-07-13
      • 1970-01-01
      • 2016-08-24
      • 2019-08-01
      相关资源
      最近更新 更多