MySQL выбирает записи, когда истина, и заменяет нуль, если ложь - PullRequest
0 голосов
/ 04 марта 2019

Я программирую расписание офиса.

У меня есть таблица 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 (я заменил переменные $ понедельник и $ пятница на реальные даты для ясности):

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

Этот запрос работает.Я получаю codeName, dateStart и dateEnd для пользователя, если это произойдет на этой неделе.Если dateStart и dateEnd равны NULL, я по-прежнему получаю пользователя firstName, lastName и hreg, поэтому я могу отображать их на веб-сайте как работающие.

Пока все хорошо, но у меня проблема.Когда dateStart < '2019-02-25' и dateEnd < '2019-03-01' (то есть, если отпуск произошел до этой недели), я ничего не получаю для этого пользователя, поскольку диапазон дат проверяется как ЛОЖЬ, и он также не равен NULL.Например, если у Роджера был отпуск с 2019-02-11 по 2019-02-13, Роджер не включается в набор результатов.

То же самое относится и к диапазону дат в будущем: dateStart > '2019-02-25'и dateEnd> '2019-03-01'.

Каков наилучший способ добиться этого?Я попытался CASE WHEN в предложении SELECT для точного определения дат, которые я хочу выбрать, но результат тот же.Мне либо нужно выбрать по-другому, либо превратить записи, проверяющие как ЛОЖЬ, в НЕДЕЙСТВИТЕЛЬНЫЙ?

Ответы [ 2 ]

0 голосов
/ 04 марта 2019

Вот подход к решению вашего вопроса.

Для начала давайте объединим 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
0 голосов
/ 04 марта 2019

Как я отметил в своем комментарии, кажется, что вы делаете запрос немного сложнее, чем нужно.

Основная проблема - CASE/WHEN в ваших WHERE критериях, ограничивающих users теми, которые соответствуют в CASE/WHEN, поскольку Роджер был в отпуске, но не совпадал с указанными датами в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>
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...