Oracle替代generate_series实现学生笔记本异动排查需求
Oracle下两类无笔记本异动学生的查询方案
一、替代PostgreSQL generate_series的日期生成方法
Oracle中无需用递归CTE生成日期序列,以下两种方式更简便:
- 层级查询(CONNECT BY):动态生成连续日期/周序列
生成最近X周的每周起始日(周一):
生成指定区间内的所有日期:SELECT TRUNC(SYSDATE - INTERVAL 'X' WEEK + (LEVEL - 1)*7, 'IW') AS week_start FROM DUAL CONNECT BY LEVEL <= X;SELECT start_date + (LEVEL - 1) AS date_val FROM (SELECT DATE '2024-01-01' AS start_date, DATE '2024-01-31' AS end_date FROM DUAL) CONNECT BY LEVEL <= end_date - start_date + 1; - 日期维度表(推荐):若数据库存在预定义的日期维度表(如
dim_date),直接查询该表过滤日期范围即可,性能远超动态生成序列。
二、两类学生的查询实现
假设核心表字段如下:
etudiantLaptops:etudiant_id(学生ID)、laptop_id(笔记本ID)mouvementlaptops:laptop_id(笔记本ID)、typemouvement(异动类型)、date_mouvement(异动日期)
1. 最近X周未进行笔记本异动的学生
查询逻辑:学生名下所有笔记本,在最近X周内均无entree或sortie类型的异动。
基础版(无需日期序列)
SELECT DISTINCT el.etudiant_id FROM etudiantLaptops el WHERE NOT EXISTS ( SELECT 1 FROM mouvementlaptops ml WHERE ml.laptop_id = el.laptop_id AND ml.typemouvement IN ('entree', 'sortie') AND ml.date_mouvement >= TRUNC(SYSDATE - INTERVAL 'X' WEEK) AND ml.date_mouvement < TRUNC(SYSDATE) + 1 );
按周校验版(确保每周均无异动)
如果需要确认学生在最近X周的每一周都没有异动,结合日期序列实现:
WITH week_series AS ( SELECT TRUNC(SYSDATE - INTERVAL 'X' WEEK + (LEVEL - 1)*7, 'IW') AS week_start FROM DUAL CONNECT BY LEVEL <= X ) SELECT el.etudiant_id FROM etudiantLaptops el CROSS JOIN week_series ws LEFT JOIN mouvementlaptops ml ON ml.laptop_id = el.laptop_id AND ml.typemouvement IN ('entree', 'sortie') AND TRUNC(ml.date_mouvement, 'IW') = ws.week_start GROUP BY el.etudiant_id HAVING COUNT(ml.laptop_id) = 0;
2. 指定日期区间内未进行笔记本异动的学生
查询逻辑:学生名下所有笔记本,在指定日期区间内均无entree或sortie类型的异动。
基础版(无需日期序列)
SELECT DISTINCT el.etudiant_id FROM etudiantLaptops el WHERE NOT EXISTS ( SELECT 1 FROM mouvementlaptops ml WHERE ml.laptop_id = el.laptop_id AND ml.typemouvement IN ('entree', 'sortie') AND ml.date_mouvement >= DATE '2024-01-01' -- 替换为实际起始日期 AND ml.date_mouvement < DATE '2024-02-01' -- 替换为实际结束日期+1天,避免日期截断问题 );
按日校验版(确保区间内每日均无异动)
如果需要确认区间内每一天都没有异动,结合日期序列实现:
WITH date_series AS ( SELECT DATE '2024-01-01' + (LEVEL - 1) AS date_val FROM DUAL CONNECT BY LEVEL <= DATE '2024-01-31' - DATE '2024-01-01' + 1 ) SELECT el.etudiant_id FROM etudiantLaptops el CROSS JOIN date_series ds LEFT JOIN mouvementlaptops ml ON ml.laptop_id = el.laptop_id AND ml.typemouvement IN ('entree', 'sortie') AND TRUNC(ml.date_mouvement) = ds.date_val GROUP BY el.etudiant_id HAVING COUNT(ml.laptop_id) = 0;
内容的提问来源于stack exchange,提问作者SuperUser
相关产品推荐
相关产品推荐

