Найти ближайшую дату из введенной даты в SQL, оба пути - PullRequest
0 голосов
/ 16 декабря 2018

У меня проблема, у меня есть задача найти ближайшую дату к данной дате, смотря в обе стороны, старше или моложе.Но я понятия не имею, я новичок в SQL, пробовал поискать в Google, но не нашел никакой помощи.

create proc Task
(@Date date)
as
begin

    select top(1) p.FirstName, p.LastName, e.BirthDate, e.JobTitle  from HumanResources.Employee e
    join Person.Person p
    on p.BusinessEntityID = e.BusinessEntityID
     where e.BirthDate>@Date

end

Я запустил что-то подобное, а затем потерял

Ответы [ 3 ]

0 голосов
/ 16 декабря 2018

Используйте datediff(), чтобы получить продолжительность между двумя датами.Поскольку вам все равно, будет ли дата в будущем или в прошлом, используйте abs(), чтобы получить абсолютное значение продолжительности.Затем упорядочите по абсолютной продолжительности и возьмите верхнюю запись.

Я не уверен, действительно ли вы используете MySQL или SQL Server.TOP (1) обозначает SQL Server, тег - MySQL.

Вот версия MySQL:

SELECT p.firstname,
       p.lastname,
       e.birthdate,
       e.jobtitle
       FROM humanresources.employee e
            INNER JOIN person.person p
                       ON p.businessentityid = e.businessentityid
       ORDER BY abs(datediff(e.birthdate, @date))
       LIMIT 1;

А вот для SQL Server:

SELECT TOP (1)
       p.firstname,
       p.lastname,
       e.birthdate,
       e.jobtitle
       FROM humanresources.employee e
            INNER JOIN person.person p
                       ON p.businessentityid = e.businessentityid
       ORDER BY abs(datediff(day, e.birthdate, @date));

Может понадобитьсянекоторые изменения в зависимости от используемых вами типов данных.


Редактировать:

Решение проблемы с fifoniks, которая может работать лучше, если существуют соответствующие индексы (на humanresources.employee.birthdate оптимально один раз по возрастанию и один раз по убыванию).

Сначала он получает объединение ближайшей записи в будущем @date (включая @date) и аналоговой записи в прошлом, возможно, используя индексы вдольпуть.Из этих двух записей выбирается та, которая имеет самую низкую абсолютную продолжительность до @date.Затем person присоединяется.

SELECT p.firstname,
       p.lastname,
       y.bithdate,
       y.jobtitle
       FROM (SELECT TOP (1)
                    x.businessentityid,
                    x.birthdate,
                    x.jobtitle
                    FROM (SELECT TOP (1)
                                 e.businessentityid,
                                 e.birthdate,
                                 e.jobtitle
                                 FROM humanresources.employee e
                                 WHERE e.birthdate >= @date
                                 ORDER BY e.birthdate ASC
                          UNION ALL
                          SELECT TOP (1)
                                 e.businessentityid,
                                 e.birthdate,
                                 e.jobtitle
                                 FROM humanresources.employee e
                                 WHERE e.birthdate <= @date
                                 ORDER BY e.birthdate DESC) x
                    ORDER BY abs(datediff(day, x.birthdate, @date)) ASC) y
            INNER JOIN person.person p
                       ON p.businessentityid = y.businessentityid;
0 голосов
/ 17 декабря 2018

Я бы не использовал функции по порядку (так как сервер не сможет использовать индексы).Вместо этого я бы пошел на решение двух запросов.Он может быть обернут в SP примерно так (версия MySQL):

CREATE FUNCTION `Task`(
    `aDate` DATE
)
RETURNS INT
BEGIN
    SELECT
          `BusinessEntityID`
        , `BirthDate`
    INTO
          @id_next
        , @birthdate_next
    FROM
        `Employee`
    WHERE
        `BirthDate` >= aDdate
    ORDER BY
        `BirthDate` ASC
    LIMIT
        1
    ;

    IF @birthdate_next IS NULL THEN
        SELECT
              `BusinessEntityID`
            , `BirthDate`
        INTO
            @id_prev
          , @birthdate_prev
        FROM
            `Employee`
        WHERE
            `BirthDate` < aDate
        ORDER BY
            `BirthDate` DESC
        LIMIT
            1
        ;
    ELSE
        IF DATEDIFF(@birthdate_next, aDate) > 1 THEN
            SELECT
                  `BusinessEntityID`
                , `BirthDate`
            INTO
                @id_prev
              , @birthdate_prev
            FROM
                `Employee`
            WHERE
                    `BirthDate` < aDate
                AND `BirthDate` > DATE_SUB(aDate, INTERVAL DATEDIFF(@birthdate_next, aDate) DAY)
            ORDER BY
                `BirthDate` DESC
            LIMIT
                1
            ;
        END IF;
    END IF;

    CASE
        WHEN @id_prev IS NULL AND @id_next IS NULL THEN RETURN NULL;
        WHEN @id_prev IS NULL THEN RETURN @id_next;
        WHEN @id_next IS NULL THEN RETURN @id_prev;
        WHEN DATEDIFF(@birthdate_next, aDate) < DATEDIFF(aDate, @birthdate_prev) THEN RETURN @id_next;
        ELSE RETURN @id_prev;
    END CASE;

END

Так что в некоторых случаях будет выполняться только один запрос (первый).В запросе будет использоваться индекс по дате рождения.Если разница первого запроса с указанной датой составляет менее 2 дней, второй запрос не будет выполнен вообще (он более сложен, чем в случае упорядоченного DESC).Можно еще больше упростить SP, но я держу его «как есть», чтобы его было легче понять.

0 голосов
/ 16 декабря 2018

Всегда помните: TOP без ORDER BY by не имеет особого смысла;добавьте порядок по возрастанию (ваше birthdate > @date сравнение запрашивает все даты рождения, превышающие / после, так что ТОП (1), упорядоченный по возрастанию даты рождения, будет самой ранней датой рождения, превышающей вашу переменную)

Затем возьмите весь запрос, вставьте его снова, поместите UNION ALL между ними и переверните ваш ORDER BY, чтобы он был убывающим, а ваше сравнение было меньше, чем, во втором запросе

Таким образом, вы получите запрос, который выбираетсамое маленькое, которое больше чем, и самое большое, которое меньше чем то. Ближайшие к вашей переменной

Подумайте, следует ли вам использовать> = и <=, если дата, на которую накладывается удар, соответствует спецификации </p>

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