MySQL在true时选择条目,在false时选择null

时间:2019-03-03 23:21:19

标签: mysql sql

我正在制定办公室时间表。

我有一个表users,其中每个用户都有一个常规时间表hreg,该时间表是星期一至星期五。

我有一个表hleave,其中输入了用户离职日期的请假代码。

hleave

hleave_id   user        dateStart   dateEnd     leaveCode
int(11)PK   int(11)FK   date        date        int(11)FK
1           5           2019-02-11  2019-02-13  1
2           1           2019-02-28  2019-02-28  1
3           3           2019-02-26  2019-02-28  2

users

user_id     firstName   lastName    link_team_id    link_hreg_id
int(11)PK   varchar     varchar     int(11)FK       int(11)FK
1           Bob         Smith       1               1
2           Alice       Fraser      1               1
3           Jenny       Summers     1               1
4           Carl        Raisman     1               1
5           Roger       Wayne       1               1

teams

team_id     teamName
int(11)PK   varchar
1           team 1
2           team 2
3           team 3

hreg

hreg_id     weekStart   weekEnd
int(11)PK   int(11)     int(11)
1           1           5
2           1           4
3           2           5

leave_codes

code_id     codeName
int(11)PK   varchar
1           A
2           B 
3           C
4           D

如果有一个日期范围从本周开始,本周结束或在本周之前开始并在本周之后结束的日期转换条目,我想显示请假代码。

如果没有,我想显示用户的常规时间表“工作”。例如:

        Monday  Tuesday Wednesday   Thursday    Friday
BOB     working working working     A           working
ALICE   working working working     working     working
JENNY   working B       B           B           working
CARL    working working working     working     working

这是我对TEAM#1的查询(为清楚起见,我用实际日期替换了变量$ monday和$ friday):

SELECT 
    users.firstName,
    users.lastName,
    users.link_team_id,
    users.link_hreg_id,
    hreg.weekStart,
    hreg.weekEnd,
    leave_codes.codeName,
    hleave.dateStart,
    hleave.dateEnd
FROM users
    LEFT JOIN hleave ON hleave.user = users.user_id
    JOIN teams ON users.link_team_id = teams.team_id 
    JOIN hreg ON users.link_hreg_id = hreg.hreg_id
    LEFT JOIN leave_codes ON hleave.leaveCode = leave_codes.code_id
WHERE
    (CASE 
        WHEN (hleave.dateStart BETWEEN '2019-02-25' AND '2019-03-01')
            THEN (users.link_team_id = 1)
        WHEN (hleave.dateEnd BETWEEN '2019-02-25' AND '2019-03-01')
            THEN (users.link_team_id = 1)
        WHEN (hleave.dateStart < '2019-02-25' AND hleave.dateEnd > '2019-03-01')
            THEN (users.link_team_id = 1)
        WHEN (hleave.dateStart IS NULL AND hleave.dateEnd IS NULL)
            THEN (users.link_team_id = 1)
    END)

结果:

firstName   lastName    link_team_id    link_hreg_id    weekStart   weekEnd     codeName    dateStart   dateEnd
Bob         Smith       1               1               1           5           A           2019-02-28  2019-02-28
Alice       Fraser      1               1               1           5           NULL        NULL        NULL
Jenny       Summers     1               1               1           5           B           2019-02-26  2019-02-28
Carl        Raisman     1               1               1           5           NULL        NULL        NULL

此查询有效。如果这周发生,我将为用户获得codeNamedateStartdateEnd。如果dateStartdateEnd为NULL,我仍然得到用户firstNamelastNamehreg,因此我可以在网站上正常显示它们。

到目前为止还不错,但是我有一个问题。当dateStart < '2019-02-25'dateEnd < '2019-03-01'时(例如,假发生在本周之前),因为该日期范围被验证为FALSE,并且该日期也不为NULL,所以我没有为该用户获得任何收益。例如,如果罗杰(Roger)在2019-02-11到2019-02-13休假,那么罗杰就不会包含在结果集中。

同一件事在将来的日期范围内:dateStart > '2019-02-25'dateEnd> '2019-03-01'

实现此目标的最佳方法是什么?我在SELECT子句中尝试了CASE WHEN来确定我想要选择的日期,但结果是相同的。我需要选择其他方法还是将验证为FALSE的条目转换为NULL?

2 个答案:

答案 0 :(得分:2)

这是解决您问题的一种方法。

首先,让我们将5个表连接在一起。这里的窍门是通过检查休假间隔是否与周被过滤而重叠来LEFT JOIN hleaves表中;经典的方式是使用类似l.dateStart < @weekEnd AND l.dateEnd < @weekStart的表达式:

SELECT *
FROM 
    users u
    INNER JOIN teams t ON t.team_id = u.link_team_id
    INNER JOIN hreg  r ON r.hreg_id = u.link_hreg_id
    LEFT JOIN hleave l ON l.user = u.user_id AND l.dateStart < @weekEnd AND l.dateEnd < @weekStart
    LEFT JOIN leave_codes lc ON lc.code_id = l.leaveCode

有了该数据集之后,我们现在可以使用条件聚合产生所需的结果。例如,要检查星期二的状态,我们可以这样做:

MAX(
    CASE 
        -- employee on leave
        WHEN DATE_ADD(@weekStart, INTERVAL 1 DAY) BETWEEN l.dateStart AND l.dateEnd THEN lc.codeName
        -- employee on schedule for that day
        WHEN 2 BETWEEN h.weekStart AND h.weekEnd THEN 'working'
        -- employee not on schedule for that day
        ELSE 'not working'
    END
) AS Tuesday

您可以在一周中的每一天重复此表达式,以增加计数器。这是星期一至星期三的查询:

SELECT
    u.firstName,    
    MAX(
        CASE 
            WHEN @weekStart BETWEEN l.dateStart AND l.dateEnd THEN lc.codeName
            WHEN 2 BETWEEN h.weekStart AND h.weekEnd THEN 'working'
            ELSE 'not working'
        END
    ) AS Monday,
    MAX(
        CASE 
            WHEN DATE_ADD(@weekStart, INTERVAL 1 DAY) BETWEEN l.dateStart AND l.dateEnd THEN lc.codeName
            WHEN 2 BETWEEN h.weekStart AND h.weekEnd THEN 'working'
            ELSE 'not working'
        END
    ) AS Tuesday,
    MAX(
        CASE 
            WHEN DATE_ADD(@weekStart, INTERVAL 2 DAY) BETWEEN l.dateStart AND l.dateEnd THEN lc.codeName
            WHEN 3 BETWEEN h.weekStart AND h.weekEnd THEN 'working'
            ELSE 'not working'
        END
    ) AS Wednesday
    -- other days go here
FROM 
    users u
    INNER JOIN teams t ON t.team_id = u.link_team_id
    INNER JOIN hreg  r ON r.hreg_id = u.link_hreg_id
    LEFT JOIN hleave l ON l.user = u.user_id AND l.dateStart < @weekEnd AND l.dateEnd < @weekStart
    LEFT JOIN leave_codes lc ON lc.code_id = l.leaveCode
GROUP BY
    u.user_id,
    u.firstName

答案 1 :(得分:1)

正如我在评论中指出的那样,您似乎使查询变得比所需的更为复杂。

主要问题是您的CASE/WHEN标准中的WHERE,将users限制为CASE/WHEN中的匹配者,因为Roger休假,但没有匹配WHERE条件中指定的日期,他就被排除在外。

由于您似乎只对特定团队感兴趣,因此可以直接在主查询中过滤用户团队。假设hleave仅包含user休假查询日期范围内的条目,则可以将条件从“ WHERE”移到“ LEFT JOIN”,当用户不在时显示NULL休假。

SELECT 
users.firstName,
users.lastName,
users.link_team_id,
users.link_hreg_id,
hreg.weekStart,
hreg.weekEnd,
leave_codes.codeName,
hleave.dateStart,
hleave.dateEnd
FROM users
JOIN teams 
ON users.link_team_id = teams.team_id
JOIN hreg 
ON users.link_hreg_id = hreg.hreg_id
LEFT JOIN hleave 
ON hleave.user = users.user_id
AND (
    hleave.dateStart BETWEEN '2019-02-25' AND '2019-03-01' #leave starts this week
    OR
    hleave.dateEnd BETWEEN '2019-02-25' AND '2019-03-01' #leave ends this week
    OR
    (hleave.dateStart < '2019-02-25' AND hleave.dateEnd > '2019-03-01') #leave started before this week and continues after
)
LEFT JOIN leave_codes 
ON hleave.leaveCode = leave_codes.code_id
WHERE users.link_team_id = 1

结果:

    <table>
      <thead>
        <tr>
          <th class="col0">firstName</th>
          <th class="col1">lastName</th>
          <th class="col2">link_team_id</th>
          <th class="col3">link_hreg_id</th>
          <th class="col4">weekStart</th>
          <th class="col5">weekEnd</th>
          <th class="col6">codeName</th>
          <th class="col7">dateStart</th>
          <th class="col8">dateEnd</th>
        </tr>
      </thead>
      <tbody>
        <tr>
          <td class="col0">Bob</td>
          <td class="col1">Smith</td>
          <td class="col2">1</td>
          <td class="col3">1</td>
          <td class="col4">1</td>
          <td class="col5">5</td>
          <td class="col6">A</td>
          <td class="col7">2019-02-28</td>
          <td class="col8">2019-02-28</td>
        </tr>
        <tr>
          <td class="col0">Jenny</td>
          <td class="col1">Summers</td>
          <td class="col2">1</td>
          <td class="col3">1</td>
          <td class="col4">1</td>
          <td class="col5">5</td>
          <td class="col6">B</td>
          <td class="col7">2019-02-26</td>
          <td class="col8">2019-02-28</td>
        </tr>
        <tr>
          <td class="col0">Alice</td>
          <td class="col1">Fraser</td>
          <td class="col2">1</td>
          <td class="col3">1</td>
          <td class="col4">1</td>
          <td class="col5">5</td>
          <td class="col6"></td>
          <td class="col7"></td>
          <td class="col8"></td>
        </tr>
        <tr>
          <td class="col0">Carl</td>
          <td class="col1">Raisman</td>
          <td class="col2">1</td>
          <td class="col3">1</td>
          <td class="col4">1</td>
          <td class="col5">5</td>
          <td class="col6"></td>
          <td class="col7"></td>
          <td class="col8"></td>
        </tr>
        <tr>
          <td class="col0">Roger</td>
          <td class="col1">Wayne</td>
          <td class="col2">1</td>
          <td class="col3">1</td>
          <td class="col4">1</td>
          <td class="col5">5</td>
          <td class="col6"></td>
          <td class="col7"></td>
          <td class="col8"></td>
        </tr>
      </tbody>
    </table>