MySQL:让所有未在2年内完成任何培训的用户获得

时间:2015-04-26 11:05:33

标签: mysql

我需要让所有在过去两年内没有接受任何培训的用户参加。

我们每年开3门课程:1月基础1,2月基础2和3月基础3。

并非每个人都需要每年进行培训,但每个人都必须在过去两年内完成一门课程才能保持注册。目前我们会查看每个用户记录,并在过去两年内没有发布任何培训课程时突出显示用户,但随着用户数量的增加,我们需要立即检查所有用户,以便我一直在查看可能提供数据并显示上次培训日期的查询以及过去两年未完成任何培训的课程名称。

所以,例如,给出这两个表:

users_temp:
+----+-------+
| id | name  |
+----+-------+
| 1  | David |
| 2  | John  |
| 3  | Barry |
| 4  | Mary  |
+----+-------+

courses_temp:
+---------+------------+---------+
| user_id | date       | name    |
+---------+------------+---------+
| 1       | 2015-01-01 | Basic 1 |
| 1       | 2015-02-02 | Basic 2 |
| 1       | 2015-03-03 | Basic 3 |
| 2       | 2015-01-01 | Basic 1 |
| 2       | 2014-02-02 | Basic 2 |
| 2       | 2014-03-03 | Basic 3 |
| 3       | 2012-01-01 | Basic 1 |
| 3       | 2012-02-02 | Basic 2 |
| 3       | 2013-03-03 | Basic 3 |
| 4       | 2013-01-01 | Basic 1 |
| 4       | 2012-02-02 | Basic 2 |
| 4       | 2012-03-03 | Basic 3 |
+---------+------------+---------+

我可以手动检查并告诉大卫今年3月接受的最后一次培训,John去年1月接受培训,Barry于2013年3月接受培训,Mary于2013年1月接受培训,因此我需要培训像这样的表:

+---------+-------+---------------+-------------+
| user_id | Name  | Last Training | Last Course |
+---------+-------+---------------+-------------+
| 3       | Barry | 2013-03-03    | Basic 3     |
| 4       | Mary  | 2013-01-01    | Basic 1     |
+---------+-------+---------------+-------------+

我的第一个查询是这样的:

SELECT 
    user_id, 
    max(date)
FROM 
    courses_temp
GROUP BY 
    user_id
HAVING
    max(date) < DATE_SUB(NOW(),INTERVAL 2 YEAR)

要获得这些结果:

+---------+------------+
| user_id | max(date)  |
+---------+------------+
| 3       | 2013-03-03 |
| 4       | 2013-01-01 |
+---------+------------+

获取我添加的用户的名称:

SELECT 
    user_id, 
    max(date),
    users_temp.name
FROM 
    courses_temp
JOIN
    users_temp
ON
    courses_temp.user_id = users_temp.id
GROUP BY 
    user_id
HAVING
    max(date) < DATE_SUB(NOW(),INTERVAL 2 YEAR)

要获得这些结果:

+---------+------------+-------+
| user_id | max(date)  | name  |
+---------+------------+-------+
| 3       | 2013-03-03 | Barry |
| 4       | 2013-01-01 | Mary  |
+---------+------------+-------+

尝试获取我用过的最后一个课程的名称

SELECT 
    user_id, 
    max(date),
    users_temp.name,
    courses_temp.name
FROM 
    courses_temp
JOIN
    users_temp
ON
    courses_temp.user_id = users_temp.id
GROUP BY 
    user_id
HAVING
    max(date) < DATE_SUB(NOW(),INTERVAL 2 YEAR)

要获得这些结果:

+---------+------------+-------+---------+
| user_id | max(date)  | name  | name    |
+---------+------------+-------+---------+
| 3       | 2013-03-03 | Barry | Basic 1 |
| 4       | 2013-01-01 | Mary  | Basic 1 |
+---------+------------+-------+---------+

但那是错的,应该是:

+---------+------------+-------+---------+
| user_id | max(date)  | name  | name    |
+---------+------------+-------+---------+
| 3       | 2013-03-03 | Barry | Basic 1 |
| 4       | 2013-01-01 | Mary  | Basic 3 |
+---------+------------+-------+---------+

如何获得正确的课程名称(基础3 )?

1 个答案:

答案 0 :(得分:1)

看起来你过于复杂化了。 left join负责查找最近参加的培训 - 如果courses_temp c2中没有相同ID的行,以及比courses_temp c1中相应行更新的日期,您将获得null值,允许我们识别每个用户的最新行。在那之后,它是肉汁。

select u.*, c1.name, c1.date
  from users_temp u
    inner join courses_temp c1
      on u.id = c1.user_id
    left join courses_temp c2
      on u.id = c2.user_id and c1.date < c2.date
  where c2.date is null and c1.date < now() - interval 2 year;

你的最后一次尝试是一个很好的例子,mysqls有助于group by处理有点像一个bugbear。您不能依赖它为group by约束中不存在的任何字段选择正确的值,或者不在聚合公式中使用。有关详细信息,请参阅12.16.3 MySQL Handling of GROUP BY

  

MySQL扩展了GROUP BY的使用,以便选择列表可以引用   GROUP BY子句中未命名的非聚合列[...]您可以   使用此功能可以避免不必要的操作来获得更好的性   列排序和分组。但是,这在以下情况下非常有用   在GROUP BY中未命名的每个非聚合列中的所有值都是   每组都一样。服务器可以自由选择任何值   每个组,所以除非它们相同,否则选择的值是   不确定的。