Oracle SQL中关联三表获取所有子项已过期的A表记录方法
我来帮你搞定这个查询需求!先明确目标:我们要从A表中筛选出那些**所有关联到C表的child_id对应的expiration_date都早于当前系统时间(sysdate)**的记录——说白了就是某个ba_no下的所有子项全过期了,没有一个还在有效期内的。
先把已知的表结构和数据示例摆出来,方便对照:
表结构及数据示例
| A表(ba_no, account) | B表(ba_no, key_id) | C表(key_id, child_id, expiration_date) | sysdate |
|---|---|---|---|
| 100, 1 | 100, 23 | 23, 1001, 28-apr-20 | sysdate |
| 100, 1 | 101, 24 | 23, 1002, 28-apr-21 | sysdate |
| 101, 2 | 102, 25 | 23, 1003, 28-apr-20 | sysdate |
| 102, 3 | 24, 2000 | - | sysdate |
| - | 24, 2052 | - | sysdate |
| - | 25, 5201 | - | sysdate |
实现方案
这里给你两种常用的SQL写法,按需选择:
方法一:用NOT EXISTS排除存在有效子项的记录
这种方法的思路是:找到那些不存在任何关联到C表且expiration_date >= sysdate(未过期)的子项的ba_no,剩下的就是全过期的。
SELECT DISTINCT a.ba_no, a.account FROM A a JOIN B b ON a.ba_no = b.ba_no -- 如果需要排除没有关联C表记录的ba_no,就保留下面的LEFT JOIN和WHERE条件;如果要包含无C表记录的,去掉即可 LEFT JOIN C c ON b.key_id = c.key_id WHERE NOT EXISTS ( SELECT 1 FROM B b2 JOIN C c2 ON b2.key_id = c2.key_id WHERE b2.ba_no = a.ba_no AND c2.expiration_date >= SYSDATE ) AND c.key_id IS NOT NULL;
方法二:用GROUP BY + HAVING统计全过期的ba_no
这种方法更直接:先关联所有表,然后按A表的ba_no和account分组,判断该分组下所有C表的expiration_date的最大值都小于sysdate(也就是没有一个未过期的)。
SELECT a.ba_no, a.account FROM A a JOIN B b ON a.ba_no = b.ba_no JOIN C c ON b.key_id = c.key_id GROUP BY a.ba_no, a.account HAVING MAX(c.expiration_date) < SYSDATE;
两种方法的区别
- 方法一灵活度更高:可以轻松控制是否包含没有关联任何C表记录的ba_no(只要去掉
AND c.key_id IS NOT NULL即可)。 - 方法二逻辑更直观:适合确定要筛选的ba_no一定有对应C表记录的场景,执行效率通常也不错。
拿你的示例数据来说,如果sysdate是2024年的话,ba_no=100对应的C表记录过期日期都是2020和2021年,都早于sysdate,所以会被这两个查询选中;而ba_no=101、102要么没有对应C表记录,要么关联的B表key_id没有C表数据,方法二会直接过滤掉它们,方法一如果去掉那个条件会包含进来(取决于你的实际需求)。
内容的提问来源于stack exchange,提问作者Mudit B




