【问题标题】:How do I perform the following SQL (or PL/SQL) Query如何执行以下 SQL(或 PL/SQL)查询
【发布时间】:2021-12-28 11:12:18
【问题描述】:

如果面临以下要求:用户需要一个表格,在该表格中他可以看到员工 ID、员工姓名和员工不工作的天数。原始数据存储在下表中:

Employee: Emp_ID, Emp_Name

WorkDays: Emp_ID, Date

流程是每当员工工作时,他的 ID 和当天的日期都会插入到 WorkDays 表中。

现在我需要一个 SQL 或 PL/SQL 查询,在其中我选择一个日期范围(fe 介于 2021 年 10 月 1 日和 2021 年 11 月 1 日之间)以及在此期间至少有 1 天没有工作的每个员工(意味着在 WorkDays 表中有一行“缺少”特定 ID 和日期),他显示在一行中,其中包含他没有工作的日期。日期都在该行的一列中,f.e.用逗号分隔。

为了更好地理解,如果有两名员工在那段时间没有工作,那么决赛桌应该是这样的:

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             |

我不希望我的代码为我完成,而是在查询表的问题上获得帮助,我必须循环遍历数据并在相应的行中显示多个结果(日期)。

非常感谢您的帮助! 杰克

【问题讨论】:

  • 请不要只要求我们解决问题或为您编写解决方案。欢迎初学者,但我们希望您付出一些努力在提出问题之前解决您自己的问题。 SO 不是免费的编码服务,尽管我们非常愿意帮助您解决您编写的代码的问题。
  • 要改善您的体验,请take the tour 并阅读how to ask、On Topic question,然后查看Question Check list、perfect question 以及如何创建Minimal, Complete and Verifiable Example
  • 1NF(第一范式)规则:每个表格单元格应包含一个值;每条记录都必须是唯一的。没有工作 2021-10-01、2021-10-03、2021-10-06 不在 1NF 中。更正你的表。
  • @RiggsFolly 你是对的!不是我打算完成我的代码我只是想不出一种方法来描述我可以理解的具体问题,因此我认为描述整个需求会更容易。谢谢!
  • @HalleyOliveira 没错,这将违反 1NF,但我并没有尝试存储数据,而是仅在我认为一般情况下这是可能的地方显示它。具有由“emp_name || ',' || emp_lastname”组成的虚拟列

标签: sql plsql


【解决方案1】:

使用行生成器创建日历,然后使用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小提琴here

【讨论】:

  • 分区外连接语法很棒。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2019-07-01
  • 1970-01-01
  • 2014-05-31
  • 2012-10-26
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多