Получение коллекций последовательных дат - PullRequest
2 голосов
/ 11 августа 2011

У меня есть база данных, в которой хранятся сотрудники. Каждый день, когда сотрудник находится в отпуске, в базу данных заносится новая запись. Я хочу, чтобы кто-то ввел идентификатор сотрудника и диапазон дат, и для каждого периода отсутствия возвращается запись с указанием даты, даты, продолжительности, а также того, был ли это AM или PM (для половины дней).

Это должно выглядеть примерно так (для сотрудника 9999 и дат с 2011-08-08 по 2011-09-01):

employee_id | Start      | start_am_pm | End        | end_am_pm | Duration
9999        | 2011-08-10 | PM          | 2011-08-12 | AM        | 2
9999        | 2011-09-01 |             | 2011-09-01 |           | 1

Примечание: первая продолжительность выше 2, потому что 10-е и 12-е - полдня, а 11-е - полное.

Во всяком случае. Мой запрос работает точно так же, как и следовало ожидать, если дата С не является датой, на которую сотрудник ушел. Например, в приведенном выше примере, если я установил дату от 10-го, 11-го или 12-го, эта строка будет удалена. Он должен считать дни между указанными датами.

Как это отображается в данный момент (для сотрудника 9999 и даты с 2011-08-11 по 2011-09-01):

employee_id | Start      | start_am_pm | End        | end_am_pm | Duration
9999        | 2011-09-01 |             | 2011-09-01 |           | 1

Подобное происходило с датой До, но я исправил это. Подобный подход не работал для даты С. Ниже моя хранимая процедура.

DELIMITER $$

USE `test`$$

DROP PROCEDURE IF EXISTS `GetLeaveDates`$$

CREATE DEFINER=`root`@`%` PROCEDURE `GetLeaveDates`(pEmpID INT, pDateFrom DATETIME, pDateTo DATETIME)
BEGIN

SELECT 
    a.start_date,
CASE WHEN a.am_pm = 1 THEN "AM"
     WHEN a.am_pm = 2 THEN "PM"
     ELSE "" END AS start_am_pm,
    CASE WHEN pDateTo > MIN(c.start_date) THEN
        MIN(c.start_date)
    ELSE
        pDateTo
    END AS End,
CASE WHEN c.am_pm = 1 THEN "AM"
     WHEN c.am_pm = 2 THEN "PM"
     ELSE "" END AS start_am_pm,
    CASE WHEN a.am_pm = 0 AND c.am_pm = 0 THEN
        DATEDIFF(MIN(c.start_date),a.start_date)+1
         WHEN (a.am_pm = 0 AND c.am_pm <> 0) OR (c.am_pm = 0 AND a.am_pm <> 0) THEN
        DATEDIFF(MIN(c.start_date),a.start_date)+0.5
         WHEN a.am_pm <> 0 AND c.am_pm <> 0 THEN
        DATEDIFF(MIN(c.start_date),a.start_date)
    END
     AS Duration
FROM t AS a
LEFT JOIN t AS b ON a.employee_id=b.employee_id AND a.start_date = ADDDATE(b.start_date,1)
LEFT JOIN t AS c ON a.employee_id=c.employee_id AND a.start_date <= c.start_date
LEFT JOIN t AS d ON c.employee_id=d.employee_id AND c.start_date = ADDDATE(d.start_date,-1)
WHERE b.start_date IS NULL AND c.start_date IS NOT NULL AND d.start_date IS NULL
AND a.EMPLOYEE_ID = pEmpID
AND a.START_DATE BETWEEN pDateFrom AND pDateTo
GROUP BY a.employee_id, a.start_date   
; END$$

DELIMITER ;

1 Ответ

0 голосов
/ 12 августа 2011

Хорошо, я понял это, и это было довольно просто, в основном на каждом из LEFT JOIN мне пришлось отфильтровать дату начала по переданным параметрам.

Я также должен был отфильтровать, был ли утвержден отпуск, поля Approved_DateTime и Approved_By, где оно было заполнено, если оно было утверждено. Кроме того, при некоторых обстоятельствах расчет длительности был немного неправильным. Итак, моя хранимая процедура теперь выглядит так:

DELIMITER $$

USE `test`$$

DROP PROCEDURE IF EXISTS `GetLeaveDates`$$

CREATE DEFINER=`root`@`%` PROCEDURE `GetLeaveDates`(pEmpID INT, pDateFrom DATETIME, pDateTo DATETIME, pApproved BOOLEAN)
BEGIN

SELECT 
    DATE_FORMAT(a.start_date,'%d/%m/%y') AS start,
CASE WHEN a.am_pm = 1 THEN "AM"
     WHEN a.am_pm = 2 THEN "PM"
     ELSE "" END AS start_am_pm,
    DATE_FORMAT(CASE WHEN pDateTo > MIN(c.start_date) THEN
        MIN(c.start_date)
    ELSE
        pDateTo
    END, '%d/%m/%y') AS end,
CASE WHEN c.am_pm = 1 THEN "AM"
     WHEN c.am_pm = 2 THEN "PM"
     ELSE "" END AS end_am_pm,
  CASE WHEN a.am_pm = 0 THEN
    CASE WHEN c.am_pm = 0 OR c.am_pm = 2 THEN
        DATEDIFF(MIN(c.start_date),a.start_date)+1
    WHEN c.am_pm = 1 THEN
        DATEDIFF(MIN(c.start_date),a.start_date)+0.5
    END
WHEN a.am_pm = 1 THEN
    CASE WHEN c.am_pm = 0 OR c.am_pm = 2 THEN
        DATEDIFF(MIN(c.start_date),a.start_date)+1
    WHEN c.am_pm = 1 THEN
        DATEDIFF(MIN(c.start_date),a.start_date)+0.5
    END
WHEN a.am_pm = 2 THEN
    CASE WHEN c.am_pm = 0 OR c.am_pm = 2 THEN
        DATEDIFF(MIN(c.start_date),a.start_date)+0.5
    WHEN c.am_pm = 1 THEN
        DATEDIFF(MIN(c.start_date),a.start_date)
    END 
END AS Duration

FROM t AS a
LEFT JOIN t AS b ON a.employee_id=b.employee_id AND a.start_date = ADDDATE(b.start_date,1) AND ISNULL(b.approved_datetime) <> pApproved AND b.start_date BETWEEN pDateFrom AND pDateTo
LEFT JOIN t AS c ON a.employee_id=c.employee_id AND a.start_date <= c.start_date AND ISNULL(c.approved_datetime) <> pApproved AND c.start_date BETWEEN pDateFrom AND pDateTo
LEFT JOIN t AS d ON c.employee_id=d.employee_id AND c.start_date = ADDDATE(d.start_date,-1) AND ISNULL(d.approved_datetime) <> pApproved AND d.start_date BETWEEN pDateFrom AND pDateTo
WHERE b.start_date IS NULL AND c.start_date IS NOT NULL AND d.start_date IS NULL
AND a.EMPLOYEE_ID = pEmpID
AND a.START_DATE BETWEEN pDateFrom AND pDateTo
AND ISNULL(a.approved_datetime) <> pApproved
AND a.start_date BETWEEN pDateFrom AND pDateTo
GROUP BY a.employee_id, a.start_date
; END$$

DELIMITER ;
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...