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

发布于 2024-08-22 15:33:43 字数 367 浏览 6 评论 0原文

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

我想列出带有关联地址建筑物。如果一栋建筑物有多个入口,因此有多个地址,我不在乎显示哪一个。如果建筑物没有已知地址,则地址字段应为null

也就是说,我想要类似左连接的东西,最多连接每行一次。

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

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

I have tables Building and Address, where each Building is associated with 0..n Addresses.

I'd like to list Buildings with an associated Address. If a Building has several entrances, and thus several Addresses, I don't care which one is displayed. If a Building has no known addresses, the address fields should be null.

This is, I want something like a left join that joins each row at most once.

How can I express this in Oracle SQL?

PS: My query will include rather involved restrictions on both tables. Therefore, I'd like to avoid repeating those restrictions in the query text.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(5

薄情伤 2024-08-29 15:33:43

我会考虑查询 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上存在合适的索引,它的性能可能会比大多数替代方法更好。一旦为所查询的每栋建筑物找到一个地址,它就会停止寻找更多地址。

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

I would consider querying the address in the SELECT clause, e.g.:

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

The ROWNUM=1 means "just get one if there are any, don't care which".

The advantage of this approach is that it will probably perform better than most alternatives, as long as a suitable index on addresses.buildingid exists. It will stop looking for more addresses as soon as it finds one for each building queried.

The downside to this approach is if you want multiple columns from the address table, you can't - although you can concatenate them together into one string.

话少情深 2024-08-29 15:33:43

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

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

Because you don't care which of many addresses is displayed:

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

Non-Subquery Factoring Equivalent:

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
獨角戲 2024-08-29 15:33:43

您可以做的是限制您加入的地址。
例如,要求不存在具有较低 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 个地址。

what you could do is an restriction on the addresses dat you join.
For instance by requiring that there is no address with a lower 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)

in this case you will get at most 1 address per building.

£烟消云散 2024-08-29 15:33:43
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) 
select b.*, max(a.id) as aid 
from building b 
left outer join addresses a on (a.buildingid = b.id) 
group by a.buildingid 

or

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) 
终止放荡 2024-08-29 15:33:43

Meriton,

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

理解查询的最好方法是从最里面的“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 视图:
创建视图latest_address (b_id, a_id, addr1, addr2, addr3, c) as
选择ma.b_id、ma.a_id、ma.addr1、ma.addr2、ma.addr3、mc
 从地址ma, 
    ( 选择 maxa.b_id, max(maxa.a_id) a_id, count(*) c
      从地址 maxa
      按 maxa.b_id 分组) m
 其中 ma.a_id = m.a_id;

选择 b.b_id、b.b_code、b.b_name、a.*
 从 b 楼出发,最新地址 a
 其中 b.b_id = a.b_id (+);

享受吧!
马修

Meriton,

This approach uses nested inline views. I've proven this approach on large data sets, it performs very well.

The best way to understand the query is to start from the inner-most "M" inline view. I added the count for the sake of debugging and clarity. This identifies the maximum (ie. most recent???) address id for each building:

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

The next "A" inline view uses the above "M" inline view to decide which address to get, then joins to that address id to return a set of address fields:

  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;

The above "A" inline view delivers a transformed set of addresses to the final query. Whereas the relationship between BUILDING and ADDRESS is 1 to 0..n, the relationship between BUILDING and "A" is 1 to 0..1, a basic outer-join:

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 (+);

The key advantages with this approach are:

  1. Delivers any number of address columns.
  2. Deterministic, returns exactly the same results each time it is run.
  3. Does not place undue complexities on your final query, which will surely be more complex than this one.
  4. The "A" inline view can be easily encapsulated within a database view, perhaps call it the LATEST_ADDRESS view:
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 (+);

Enjoy!

Matthew

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文