如何执行以下 SQL(或 PL/SQL)查询
How do I perform the following SQL (or PL/SQL) Query
如果面临以下需求:用户需要一个table,他可以在其中看到employeeID、employeeName 和员工不工作的日期。原始数据存储在以下tables:
Employee: Emp_ID, Emp_Name
WorkDays: Emp_ID, Date
过程是每次员工工作时,他的 ID 和当天的日期被插入 WorkDays table。
现在我需要一个 SQL 或 PL/SQL 查询,我在其中选择一个日期范围(f.e。在 2021-10-01 和 2021-11.01 之间),并且对于每个员工在那段时间里至少有 1 天没有工作(这意味着在 WorkDays table 中有一行“缺失”用于特定的 ID 和日期),他与他没有工作的日期一起显示在一行中工作了。日期都在行的一列中,f.e。用逗号分隔。
为了更好地理解,如果有两名员工在那段时间没有工作,那么最后的 table 应该看起来像这样:
Emp_ID | Emp_Name | Hasn't Worked |
1 | John | 2021-10-01, 2021-10-03, 2021-10-06 |
3 | Mary | 2021-10-02, 2021-10-03 |
我不想让我的代码为我完成,而是寻求帮助解决查询 table 的问题,我必须循环遍历数据并在一个中显示多个结果(日期)对应行。
非常感谢您的帮助!
杰克
使用行生成器创建日历,然后使用 PARTITION OUTER JOIN
找到不匹配的行,然后聚合:
WITH calendar (day) AS (
SELECT DATE '2021-10-01' FROM DUAL
UNION ALL
SELECT day + 1 FROM calendar WHERE day < DATE '2021-11-01'
)
SELECT w.emp_id,
MAX(emp_name) AS emp_name,
LISTAGG(TO_CHAR(c.day, 'YYYY-MM-DD'), ', ') WITHIN GROUP (ORDER BY c.day)
AS non_work_days
FROM calendar c
LEFT OUTER JOIN workdays w
PARTITION BY (w.emp_id)
ON (c.day = w."DATE")
LEFT OUTER JOIN employee e
ON (w.emp_id = e.emp_id)
WHERE w."DATE" IS NULL
GROUP BY w.emp_id;
或者您可以使用 CROSS JOIN
:
WITH calendar (day) AS (
SELECT DATE '2021-10-01' FROM DUAL
UNION ALL
SELECT day + 1 FROM calendar WHERE day < DATE '2021-11-01'
)
SELECT e.emp_id,
MAX(e.emp_name) AS emp_name,
LISTAGG(TO_CHAR(c.day, 'YYYY-MM-DD'), ', ') WITHIN GROUP (ORDER BY c.day)
AS non_work_days
FROM calendar c
CROSS JOIN employee e
LEFT OUTER JOIN workdays w
ON (c.day = w."DATE" AND e.emp_id = w.emp_id)
WHERE w."DATE" IS NULL
GROUP BY e.emp_id;
其中,对于示例数据:
CREATE TABLE Employee (Emp_ID, Emp_Name) AS
SELECT 1, 'Alice' FROM DUAL UNION ALL
SELECT 2, 'Beryl' FROM DUAL;
CREATE TABLE WorkDays (Emp_ID, "DATE") AS
WITH calendar (dt) AS (
SELECT DATE '2021-10-01' + LEVEL - 1 FROM DUAL CONNECT BY LEVEL <= 32
)
SELECT 1, dt FROM calendar WHERE dt NOT IN (DATE '2021-10-01', DATE '2021-10-03', DATE '2021-10-06')
UNION ALL
SELECT 2, dt FROM calendar WHERE dt NOT IN (DATE '2021-10-02', DATE '2021-10-03')
双输出:
EMP_ID
EMP_NAME
NON_WORK_DAYS
1
Alice
2021-10-01, 2021-10-03, 2021-10-06
2
Beryl
2021-10-02, 2021-10-03
db<>fiddle here
如果面临以下需求:用户需要一个table,他可以在其中看到employeeID、employeeName 和员工不工作的日期。原始数据存储在以下tables:
Employee: Emp_ID, Emp_Name
WorkDays: Emp_ID, Date
过程是每次员工工作时,他的 ID 和当天的日期被插入 WorkDays table。
现在我需要一个 SQL 或 PL/SQL 查询,我在其中选择一个日期范围(f.e。在 2021-10-01 和 2021-11.01 之间),并且对于每个员工在那段时间里至少有 1 天没有工作(这意味着在 WorkDays table 中有一行“缺失”用于特定的 ID 和日期),他与他没有工作的日期一起显示在一行中工作了。日期都在行的一列中,f.e。用逗号分隔。
为了更好地理解,如果有两名员工在那段时间没有工作,那么最后的 table 应该看起来像这样:
Emp_ID | Emp_Name | Hasn't Worked |
1 | John | 2021-10-01, 2021-10-03, 2021-10-06 |
3 | Mary | 2021-10-02, 2021-10-03 |
我不想让我的代码为我完成,而是寻求帮助解决查询 table 的问题,我必须循环遍历数据并在一个中显示多个结果(日期)对应行。
非常感谢您的帮助! 杰克
使用行生成器创建日历,然后使用 PARTITION OUTER JOIN
找到不匹配的行,然后聚合:
WITH calendar (day) AS (
SELECT DATE '2021-10-01' FROM DUAL
UNION ALL
SELECT day + 1 FROM calendar WHERE day < DATE '2021-11-01'
)
SELECT w.emp_id,
MAX(emp_name) AS emp_name,
LISTAGG(TO_CHAR(c.day, 'YYYY-MM-DD'), ', ') WITHIN GROUP (ORDER BY c.day)
AS non_work_days
FROM calendar c
LEFT OUTER JOIN workdays w
PARTITION BY (w.emp_id)
ON (c.day = w."DATE")
LEFT OUTER JOIN employee e
ON (w.emp_id = e.emp_id)
WHERE w."DATE" IS NULL
GROUP BY w.emp_id;
或者您可以使用 CROSS JOIN
:
WITH calendar (day) AS (
SELECT DATE '2021-10-01' FROM DUAL
UNION ALL
SELECT day + 1 FROM calendar WHERE day < DATE '2021-11-01'
)
SELECT e.emp_id,
MAX(e.emp_name) AS emp_name,
LISTAGG(TO_CHAR(c.day, 'YYYY-MM-DD'), ', ') WITHIN GROUP (ORDER BY c.day)
AS non_work_days
FROM calendar c
CROSS JOIN employee e
LEFT OUTER JOIN workdays w
ON (c.day = w."DATE" AND e.emp_id = w.emp_id)
WHERE w."DATE" IS NULL
GROUP BY e.emp_id;
其中,对于示例数据:
CREATE TABLE Employee (Emp_ID, Emp_Name) AS
SELECT 1, 'Alice' FROM DUAL UNION ALL
SELECT 2, 'Beryl' FROM DUAL;
CREATE TABLE WorkDays (Emp_ID, "DATE") AS
WITH calendar (dt) AS (
SELECT DATE '2021-10-01' + LEVEL - 1 FROM DUAL CONNECT BY LEVEL <= 32
)
SELECT 1, dt FROM calendar WHERE dt NOT IN (DATE '2021-10-01', DATE '2021-10-03', DATE '2021-10-06')
UNION ALL
SELECT 2, dt FROM calendar WHERE dt NOT IN (DATE '2021-10-02', DATE '2021-10-03')
双输出:
EMP_ID EMP_NAME NON_WORK_DAYS 1 Alice 2021-10-01, 2021-10-03, 2021-10-06 2 Beryl 2021-10-02, 2021-10-03
db<>fiddle here