Oracle SQL:最多加入一个关联实体

时间:2010-02-24 21:21:41

标签: sql oracle plsql analytics

我有表BuildingAddress,其中每个Building与0..n Addresses相关联。

我想列出Buildings与关联的Address。如果Building有几个入口,因此有几个Addresses,我不关心显示哪一个。{1}}。如果Building没有已知地址,则地址字段应为null

这就是,我希望像左连接一样最多连接一行。

如何在Oracle SQL中表达这一点?

PS:我的查询将包括对两个表的相关限制。因此,我想避免在查询文本中重复这些限制。

5 个答案:

答案 0 :(得分:4)

我会考虑查询SELECT子句中的地址,例如:

SELECT b.*
      ,(SELECT a.text
        FROM   addresses a
        WHERE  a.buildingid = b.id
        AND    ROWNUM=1) as atext
FROM   building b;

ROWNUM=1表示“只要有一个,不关心哪个”。

这种方法的优点是,只要在addresses.buildingid上存在合适的索引,它就可能比大多数替代方案表现更好。一旦为每个查询的建筑物找到一个地址,它就会停止寻找更多的地址。

这种方法的缺点是如果你想要地址表中的多个列,你不能 - 尽管你可以将它们连接成一个字符串。

答案 1 :(得分:2)

因为您不关心显示多个地址中的哪一个:

Oracle 9i +:

WITH summary AS (
      SELECT b.*,
             a.*,
             ROW_NUMBER() OVER (PARTITION BY b.building_id) rn
        FROM BUILDINGS b
   LEFT JOIN ADDRESSES a ON a.building_id = b.building_id)
SELECT s.*
  FROM summary s
 WHERE s.rn = 1

非子查询因子等价:

SELECT s.*
  FROM (SELECT b.*,
               a.*,
               ROW_NUMBER() OVER (PARTITION BY b.building_id) rn
           FROM BUILDINGS b
      LEFT JOIN ADDRESSES a ON a.building_id = b.building_id) s
 WHERE s.rn = 1

答案 2 :(得分:0)

您可以做的是对您加入的地址数据的限制。 例如,要求没有地址较低的地址:

select *
from building b
left join addresses a on (a.buildingid = b.id)
where not exists (select 1 from addresses a2
                  where a2.buildingid = b.id and a2.id < a.id)

在这种情况下,每栋建筑最多可获得1个地址。

答案 3 :(得分:0)

select b.*, max(a.id) as aid 
from building b 
left outer join addresses a on (a.buildingid = b.id) 
group by a.buildingid 

select b.*, maxid
from building b 
left outer join 
(
 select buildingid, max(id) as maxid
 from addresses
 group by buildingid 
) a on (a.buildingid = b.id) 

答案 4 :(得分:0)

梅里,

此方法使用嵌套的内联视图。我已经在大型数据集上证明了这种方法,它表现得非常好。

理解查询的最佳方法是从最内层的“M”内联视图开始。为了调试和清晰,我添加了计数。这标识了每个建筑物的最大(即最近的???)地址ID:

   select maxa.b_id, max(maxa.a_id) a_id, count(*) c
   from address maxa
   group by maxa.b_id;

下一个“A”内联视图使用上面的“M”内联视图来决定获取哪个地址,然后加入该地址id以返回一组地址字段:

  select ma.b_id, ma.a_id, ma.addr1, ma.addr2, ma.addr3, m.c
  from address ma, 
     ( select maxa.b_id, max(maxa.a_id) a_id, count(*) c
       from address maxa
       group by maxa.b_id ) m
  where ma.a_id = m.a_id;

上面的“A”内联视图为最终查询提供了一组转换后的地址。虽然BUILDING和ADDRESS之间的关系是1到0..n,但BUILDING和“A”之间的关系是1到0..1,一个基本的外连接:

select b.b_id, b.b_code, b.b_name, a.*
 from building b, 
    ( select ma.b_id, ma.a_id, ma.addr1, ma.addr2, ma.addr3, m.c
      from address ma, 
         ( select maxa.b_id, max(maxa.a_id) a_id, count(*) c
           from address maxa
           group by maxa.b_id ) m
      where ma.a_id = m.a_id ) a
 where b.b_id = a.b_id (+);

这种方法的主要优点是:

  1. 提供任意数量的地址列。
  2. 确定性,每次运行时返回完全相同的结果。
  3. 不会给你的最终查询带来不必要的复杂性,这肯定会比这个更复杂。
  4. “A”内联视图可以很容易地封装在数据库视图中,也许称之为LATEST_ADDRESS视图:
  5. create view latest_address (b_id, a_id, addr1, addr2, addr3, c) as
    select ma.b_id, ma.a_id, ma.addr1, ma.addr2, ma.addr3, m.c
     from address ma, 
        ( select maxa.b_id, max(maxa.a_id) a_id, count(*) c
          from address maxa
          group by maxa.b_id ) m
     where ma.a_id = m.a_id;
    
    select b.b_id, b.b_code, b.b_name, a.*
     from building b, latest_address a
     where b.b_id = a.b_id (+);
    

    享受!
    马修