将单个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_rank
或 row_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;
或者,您可以使用聚合来获取最小值或最大值。
这是我的解决方案。简单但可能无法很好地扩展到很多列。
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;
这修复了您尝试的查询
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)
我在 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_rank
或 row_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;
或者,您可以使用聚合来获取最小值或最大值。
这是我的解决方案。简单但可能无法很好地扩展到很多列。
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;
这修复了您尝试的查询
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)