将单个table的数据集限制在multi-tablesqlselect语句中

Limit the data set of a single table within a multi-table sql select statement

我在 Oracle 环境中工作。

在 1:M table 关系中,我想编写一个查询,从“1”table 和 只有 1 个匹配行中获取每一行 来自 "many" table.

举个虚构的例子...(* = 主要 Key/Foreign 键)

EMPLOYEE
*emp_id
name
department

PHONE_NUMBER
*emp_id
num

很多 phone 个号码 一个 员工。

假设我想 return 所有员工和 只有一个 他们的 phone 号码。 (例子牵强请见谅,我是模拟职场场景)

我试过 运行:

SELECT emp.*, phone.num
FROM EMPLOYEE emp
JOIN PHONE_NUMBER phone
    ON emp.emp_id = phone.emp_id
WHERE phone.ROWNUM <= 1;

事实证明(现在对我来说这很有意义)ROWNUM 仅存在于整个查询 return 的结果上下文中。每个 table 的数据集都没有 "ROWNUM"。

我也试过:

SELECT emp.*, phone.num
FROM EMPLOYEE emp
JOIN PHONE_NUMBER phone
    ON emp.emp_id = phone.emp_id
WHERE phone.num = (SELECT MAX(num)
                   FROM PHONE_NUMBER);

那个 return 总共给我排了一排。对于 EMPLOYEE 中的 each 行,我希望内部 SELECT 到 运行 一次。

我不知道还能怎么想这个。我基本上希望我的结果集是 EMPLOYEE table 中的行数,对于每一行 PHONE_NUMBER table 中的 first 匹配行.

显然有各种各样的方法可以使用过程和脚本等来做到这一点,但我觉得那里有一个单一查询解决方案...

有什么想法吗?

我会使用 rank(或 dense_rankrow_number,具体取决于您希望如何处理平局)

SELECT *
  FROM (SELECT emp.*, 
               phone.num, 
               rank() over (partition by emp.emp_id
                                order by phone.num) rnk
          FROM EMPLOYEE emp
               JOIN PHONE_NUMBER phone
                 ON emp.emp_id = phone.emp_id)
 WHERE rnk = 1

phone 中每个 emp_id 的行按 num 和 return 排在第一行。如果相同的 emp_id 和相同的 num 可以有两行,rank 将同时分配一个 rnk 为 1,这样你就会得到重复的行。您可以向 order by 添加其他条件来打破平局。或者您可以使用 row_number 而不是 rank 来任意打破平局。

如果您只想要一个 phone 号码,则使用 row_number():

SELECT e.*, p.num
FROM EMPLOYEE emp JOIN
     (SELECT p.*,
             ROW_NUMBER() OVER (PARTITION BY emp_id ORDER BY emp_id) as seqnum
      FROM PHONE_NUMBER p
     ) p
    ON e.emp_id = p.emp_id and seqnum = 1;

或者,您可以使用聚合来获取最小值或最大值。

这是我的解决方案。简单但可能无法很好地扩展到很多列。

Sql Fiddle Demo

select e.emp_id, e.name, e.dep, min(p.phone_num)
from 
    EMPLOYEE e inner join
    PHONE_NUMBER p on e.emp_id = p.emp_id
group by e.emp_id, e.name, e.dep
order by e.emp_id;

这修复了您尝试的查询

Sql Fiddle 2

SELECT emp.*, phone.num
FROM EMPLOYEE emp
    JOIN PHONE_NUMBER phone
    ON emp.emp_id = phone.emp_id
WHERE phone.num = (SELECT MAX(num)
                   FROM PHONE_NUMBER p
                   WHERE p.emp_id = emp.emp_id );

以上所有答案都适用于您描述的场景。

但是如果 phone 表中缺少一些员工,那么您需要像下面这样进行左外连接。 (我遇到了类似的情况,我也需要隔离 parents)

EMP
---------
emp_id Name
---------
 1   AA
 2   BB
 3   CC

PHONE
----------
emp_id no

1   7555
1   7777
2   5555


select emp.emp_id,ph.no from emp left outer join
(
select emp_id,no,
ROW_NUMBER() OVER (PARTITION BY emp_id ORDER BY emp_id) as rnum
  FROM phone) ph
on emp.emp_id = ph.emp_id
  where ph.rnum = 1 or ph.rnum is null

Result
EMP_ID  NO
  1    7555
  2    5555
  3   (null)