两种方式都可以从SQL中输入的日期中找到最接近的日期

时间:2018-12-16 19:52:42

标签: mysql sql

我有问题,我有一项任务是查找距给定日期最近的日期,无论年龄大小,年龄均较小。但是我不知道,我是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 :(得分:0)

永远记住:没有ORDER BY by的TOP没有多大意义;添加一个按升序排列的顺序(您的birthdate > @date比较要求所有大于或大于后的生日,因此按生日升序排列的TOP(1)将是大于变量的最早生日)

然后使用整个查询,将其再次粘贴,将UNION ALL放在它们之间,然后在第二个查询中将ORDER BY降为下,而您的比较小于。

因此,您最终得到一个查询,该查询选择大于该值的最小值和小于该值的最大值,即与变量最接近的值

考虑是否应该使用> =和<=(如果符合要求的日期符合规范)

答案 1 :(得分:0)

使用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;

答案 2 :(得分:0)

我不会按顺序使用函数(因为服务器将无法使用索引)。 相反,我将寻求两个查询的解决方案。 可以将它包装在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

因此,在某些情况下,将仅执行单个查询(第一个查询)。 该查询将使用BirthDate的索引。 如果从指定日期开始的第一个查询差异小于2天,则将完全不执行第二个查询(按有序DESC比较复杂)。 可以进一步简化SP,但是我将其“按原样”保存,因此更易于理解。