1

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

我想列出Buildings一个关联的Address. 如果 aBuilding有几个入口,因此有几个Addresses,我不在乎显示哪个。如果 aBuilding没有已知地址,则地址字段应为null

也就是说,我想要一个像左连接这样的东西,它最多连接每一行一次。

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

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

4

5 回答 5

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 上存在合适的索引。一旦为每个查询的建筑物找到一个地址,它将停止寻找更多地址。

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

于 2010-02-25T00:11:44.997 回答
2

因为您不在乎显示多个地址中的哪一个:

甲骨文 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
于 2010-02-24T22:56:24.107 回答
0

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

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 个地址。

于 2010-02-24T21:25:40.680 回答
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) 
于 2010-02-24T21:32:58.323 回答
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 视图:
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 (+);

享受!
马修

于 2010-02-25T13:47:09.437 回答