如何在带日期的 GROUP_CONCAT 中使用 NOT IN 运算符 (<>)?

How to use the NOT IN operator (<>) in a GROUP_CONCAT with dates?

我有 日历 table,其中包含 2021 年 12 月的所有日期(我只会举例说明 table 中的一些日期,但是据了解,它实际上包含所述月份的所有日期):

ID date
01 2021-12-01
02 2021-12-02
03 2021-12-03
04 2021-12-04
05 2021-12-05

我有 用户 table:

ID name num_employee
01 Andrew 101
02 Mary 102

我有table辅助

ID date num_employee
01 2021-12-03 101
02 2021-12-04 101
03 2021-12-03 102
04 2021-12-04 102
05 2021-12-05 101
06 2021-12-06 102

我进行了查询以显示员工编号、姓名、出勤天数和缺勤天数:

SELECT u.num_employee,
       u.name,
       a.date AS attendances,
       c.date as faults FROM users u
JOIN (SELECT num_employee,
      GROUP_CONCAT(DISTINCT EXTRACT(DAY FROM date)) AS date FROM attendances
      WHERE date BETWEEN '2021-12-01' AND '2021-12-31'
      GROUP BY num_employee) a ON a.not_employee = u.num_employee
LEFT JOIN (SELECT GROUP_CONCAT(DISTINCT EXTRACT(DAY FROM date)) AS date FROM calendar
           WHERE date BETWEEN '2021-12-01' AND '2021-12-31') c ON c.date <> a.date

通过上面的查询,我得到了这个:

num_employee name assists faults
101 Andrew 3,4,5 1,2,3,4,5,6,7,8,9,10...
102 Mary 3,4,6 1,2,3,4,5,6,7,8,9,10...

在考勤栏我获取的是12月份每个员工出勤的天数,而在faults我应该只获取缺勤的天数,但是12月份的所有天数显示。

我几乎可以肯定问题在于我如何评估 attends 列中显示的天数未显示在 absences栏目。具体这部分我认为我的评价是错误的:

ON c.date <> a.date

我的印象是,既然我在 GROUP_CONCAT 工作,我应该以不同的方式评估日期。我如何调整我的查询以获得以下内容?

not_employee name attendances faults
101 Andrew 3,4,5 1,2,3,6,7,8,9,10...
102 Mary 3,4,6 1,2,5,7,8,9,10...

考虑到我正在使用的 MariaDB 版本,有问题的查询无法适应使用 CTE。我正在开发 phpMyAdmin。

一个解决方案是子选择。

这在 mysql 5 中也有效,在 mysql 8 中你可以从 attendense 中获得 CTE。

CREATE TABLE calendar (
  `ID` INTEGER,
  `date` VARCHAR(10)
);

INSERT INTO calendar
  (`ID`, `date`)
VALUES
  ('01', '2021-12-01'),
  ('02', '2021-12-02'),
  ('03', '2021-12-03'),
  ('04', '2021-12-04'),
  ('05', '2021-12-05'),
  ('06', '2021-12-06'),
  ('07', '2021-12-07'),
  ('08', '2021-12-08'),
  ('09', '2021-12-09'),
  ('10', '2021-12-10'),
  ('11', '2021-12-11');
CREATE TABLE users (
  `ID` INTEGER,
  `name` VARCHAR(6),
  `num_employee` INTEGER
);

INSERT INTO users
  (`ID`, `name`, `num_employee`)
VALUES
  ('01', 'Andrew', '101'),
  ('02', 'Mary', '102');
CREATE TABLE attendances (
  `ID` INTEGER,
  `date` VARCHAR(10),
  `num_employee` INTEGER
);

INSERT INTO attendances
  (`ID`, `date`, `num_employee`)
VALUES
  ('01', '2021-12-03', '101'),
  ('02', '2021-12-04', '101'),
  ('03', '2021-12-03', '102'),
  ('04', '2021-12-04', '102'),
  ('05', '2021-12-05', '101'),
  ('06', '2021-12-06', '102');
SELECT u.num_employee,
       u.name,
       a.date AS attendances,
        (SELECT GROUP_CONCAT(DISTINCT EXTRACT(DAY FROM date)) AS date FROM calendar
           WHERE date BETWEEN '2021-12-01' AND '2021-12-31'
           AND NOT FIND_IN_SET(EXTRACT(DAY FROM date),a.date)) as faults FROM users u
JOIN (SELECT num_employee,
      GROUP_CONCAT(DISTINCT EXTRACT(DAY FROM date)) AS date FROM attendances
      WHERE date BETWEEN '2021-12-01' AND '2021-12-31'
      GROUP BY num_employee) a ON a.num_employee = u.num_employee
num_employee | name   | attendances | faults           
-----------: | :----- | :---------- | :----------------
         101 | Andrew | 3,4,5       | 1,2,6,7,8,9,10,11
         102 | Mary   | 3,4,6       | 1,2,5,7,8,9,10,11

db<>fiddle here

考虑对所有可能的员工和日期成对匹配的用户和日历表进行交叉联接。然后 left join to assistance and 运行 aggregate GROUP_CONCAT with one conditional expression for faults:

SELECT u.num_employee,
       u.name,
       GROUP_CONCAT(DISTINCT EXTRACT(DAY FROM a.date)) AS attendances,
       GROUP_CONCAT(DISTINCT 
           IF(a.date IS NULL, EXTRACT(DAY FROM c.date), NULL)
       ) AS faults 
FROM calendar c
INNER JOIN users u
  ON c.date BETWEEN '2021-12-01' AND '2021-12-31'
LEFT JOIN attendances a
   ON c.date = a.date
   AND u.num_employee = a.num_employee
   AND a.date BETWEEN '2021-12-01' AND '2021-12-31'
GROUP BY u.num_employee,
         u.name;

Online Demo