查询不存在的数据

Rol*_*ruz 5 mysql index

我有这个设置

attendance table:
------------------------------------------
| member_id | attendance_date | event_id |
------------------------------------------

attendance indeces
---------------------------------------------
| p1 (member_id, attendance_date, event_id) |
| p2 (attendance_date, member_id, event_id) |
| member (member_id)                        |
| total (attendance_date, event_id)         |
---------------------------------------------

members table
-------------------------------------------------
| member_id | department_id | registration_date |
-------------------------------------------------

members indeces:
-----------------------------------------------
| PK (member_id)                              |
| registration (registration_date, member_id) |
| department (department_id, member_id)       |
-----------------------------------------------
Run Code Online (Sandbox Code Playgroud)

所以基本上这是一个考勤系统。我需要根据出勤表运行报告。除了我需要知道成员在给定时间内缺席的次数之外,我已经弄清楚了大部分所需的查询。我几乎知道如何查询那里的数据。但我不知道如何查询不存在的数据(缺失)。

我试过这个:

SELECT
  attendance_date,
  COUNT(DISTINCT event_id)
FROM
  attendance
WHERE
  WEEKOFYEAR(attendance_date) BETWEEN 27 AND 31
GROUP BY
  WEEKOFYEAR(attendance_date)
Run Code Online (Sandbox Code Playgroud)

根据其他人的出席记录,这给了我一个成员应该能够参加的活动数量,但问题是当活动完全没有参与者(不太可能)或有新注册的成员时。

我试过这个:

   EXPLAIN SELECT
     m.member_id AS id,
     COUNT(DISTINCT a.event_id)
   FROM
     members AS m
     LEFT JOIN
       attendance AS a
       ON
         m.registration_date < a.attendance_date AND
         WEEKOFYEAR(a.attendance_date) BETWEEN 27 AND 31
   WHERE
     m.department_id = 1
Run Code Online (Sandbox Code Playgroud)

但是EXPLAIN告诉我它会扫描 4M 行,所以我知道这不是要走的路。我知道查询是错误的,因为 COUNT(DISTINCT) 会给我每个成员多行。有什么想法吗?关于我应该如何为此查询索引数据库的任何建议也值得赞赏。

编辑

更具体地说,这就是我想对查询做的事情:

  1. 获取部门的所有成员
  2. 获取给定时间段之间出勤表中有记录的事件数,但仅发生在当前会员注册日期之后的事件数

当我们减去该会员的出勤记录总数时,我们实际上可以得到该会员已经有多少缺勤记录。

我知道这个问题的一部分源于应用程序结构中的一个根本错误(不应根据出勤记录定义事件),但这些事件是重复发生的,并且发生模式非常不规则。因此,为了尽量减少维护的需要,我决定根据出席人数得出活动信息(或至少是他们的人数),因为活动的参加人数不太可能为 0。

And*_*y M 4

为什么要通过另一个成员的存在来确定一个成员的缺席?

events我想你的模式中一定有一个表。我希望它有一个名为类似的专栏event_date。您应该使用该event_date列来确定成员可以访问哪些活动 ( members.registration_date < events.event_date)。

获取成员列表及其各自的有效事件后,您可以将其加入到attendance表中以查看哪个成员错过了哪些(或多少)事件。

这是一个示例查询,说明了我刚才所说的内容。LEFT JOIN它使用+检查实现反连接WHERE IS NULL

SELECT
  m.member_id,
  e.event_id
FROM       members    m
INNER JOIN events     e ON m.registration_date < e.event_date
LEFT  JOIN attendance a ON m.member_id = a.member_id
                       AND e.event_id  = a.event_id
WHERE a.member_id IS NULL
  AND ... /* some other conditions to filter members and/or events, if necessary */
Run Code Online (Sandbox Code Playgroud)

或者,这里是另一个示例,显示如何计算会员可用的总活动以及他/她实际参加的活动:

SELECT
  m.member_id,
  COUNT(e.event_id) AS total_events,
  COUNT(a.event_id) AS attended_events,
FROM       members    m
INNER JOIN events     e ON m.registration_date < e.event_date
LEFT  JOIN attendance a ON m.member_id = a.member_id
                       AND e.event_id  = a.event_id
WHERE /* some conditions as necessary */
Run Code Online (Sandbox Code Playgroud)

请注意,在最后一个示例中没有IS NULL检查。这是因为该查询需要获取每个成员的所有事件。函数不会计算错过的事件COUNT()(因为a.event_id在这些情况下相应的值包含 NULL)。

更新

由于根据您的评论,事件会重复发生(因此没有固定日期),因此我建议添加和使用一个event_calendar表,预先手动或以编程方式填充。自然地,events上面示例中的表格将替换为event_calendar

或者,如果维护事件日历不是一个选项,您可以events在查询中替换为

(
  SELECT DISTINCT
    event_id,
    attendance_date AS event_date
    /* or, if they are not purely dates:
    CAST(attendance_date AS date) AS event_date
    */
  FROM attendance
) e
Run Code Online (Sandbox Code Playgroud)

(基本上,@ypercube 的建议加上了日期)。