MySql查找最接近的日期和之前和之后的日期

时间:2016-05-23 20:21:55

标签: php mysql date range

所以我已经仔细研究了互联网的凹陷,但我找不到是否有一种简单的方法可以做到这一点。

我在MySql(我知道)数据库中有一个杂志日期列表。我需要能够找到最接近给定日期的日期(“1962-5-22”可能没有问题,因此我们需要DB中最接近的日期)但同时,获取NEXT日期和DB中的PREVIOUS日期,以便我可以填充页面上的“NEXT”和“PREVIOUS”按钮。所有这些都要记住日期的上限和下限。呼。

这是否可以通过相对简单的查询来实现?

2 个答案:

答案 0 :(得分:0)

整蛊,挑剔,但可行:)

首先,我尝试使用以下语句尽可能地设置您的场景:

create table issues (
   issueon date not null primary key 
);

insert into issues values('2015-01-10'), ('2015-01-15'), ('2014-10-25');
insert into issues values('2015-01-08'), ('2015-01-20'), ('2014-1-10');

(不失一般性,我只对一本杂志这样做。)

以下是完整的SELECT语句,经过一些测试后,我假设我正在尝试检测'2015-01-11'周围的当前,上一个和下一个发布日期:

select tableNext.theIssue, theNext, thePrevious
from 
(
    select basis.issueon as theIssue, target.issueon as thePrevious, datediff(basis.issueon, target.issueon) as surroundingdistance
    from issues as basis, issues as target
    where basis.issueon = (
        select closestissue from ( 
            select issueon as closestissue, abs(datediff('2015-01-11', issueon)) as distance from issues order by distance asc limit 1 
        ) as theissue
    ) and datediff(basis.issueon, target.issueon) > 0 
    order by surroundingdistance limit 1
) as tableNext
left join 
(
    select basis.issueon as theIssue, target.issueon as theNext, datediff(basis.issueon, target.issueon) as surroundingdistance
    from issues as basis, issues as target
    where basis.issueon = (
        select closestissue from ( 
            select issueon as closestissue, abs(datediff('2015-01-11', issueon)) as distance from issues order by distance asc limit 1 
        ) as theissue
    ) and datediff(basis.issueon, target.issueon) < 0 
    order by surroundingdistance desc limit 1
) as tablePrevious
on tableNext.theIssue = tableNext.theIssue
;

不要惊慌 - 这是一个怪物,我会一步一步地解释它:

Innerst声明 - 第一层

让我们先来看看最里面的选择语句:

select issueon as closestissue, abs(datediff('2015-01-11', issueon)) as distance 
from issues order by distance asc limit 1

该声明计算所有问题的日期与我们开始日期之间的“距离”。为了确保我们同时向前看和向后看,都要考虑距离的绝对值。使用order by distance limit 1,我们确定“最接近的匹配”(注意:这也可以使用GROUP BYHAVING子句来完成,但MySQL不像LIMIT 1那样受欢迎)。 然后,可以在属性closestissue中找到“最佳匹配”的值。

第二层

现在让我们来看看

select closestissue from ( 
            select issueon as closestissue, abs(datediff('2015-01-11', issueon)) as distance from issues order by distance asc limit 1 
        ) as theissue

有了这个,我们只是剥离属性distance,因为它会阻碍我们在第二个

第三层

在第三层,我们将获取第二层的结果并将其作为WHERE条件的输入用于再次从issues表中选择“正确”问题:

select basis.issueon as theIssue, target.issueon as theNext, datediff(basis.issueon, target.issueon) as surroundingdistance
    from issues as basis, issues as target
    where basis.issueon = (
        select closestissue from ( 
            select issueon as closestissue, abs(datediff('2015-01-11', issueon)) as distance from issues order by distance asc limit 1 
        ) as theissue
    )

之后,我们再次加入同一张桌子(自行加入)笛卡尔积。请注意,连接的左表只能包含一个记录(假设每天只有一个问题,我希望这是安全的)。因此,这个笛卡尔积没有风险,因为它只会返回表issues中我们无论如何都有的记录(因此我们保持复杂性O(n))。此权利的自由侧(“右侧”)在查询中称为target

我们现在可以将“正确发行日期”的“距离”再次与表格中的所有日期进行比较。我们通过再次计算datediff(basis.issueon, target.issueon) as surroundingdistance来实现这一目标。

上一页和下一页

一旦我们拥有sourroundingdistance,就可以轻松应用与第一层相同的模式。但是,这次我们可能不只是采取绝对距离,但我们必须看两个不同的集合:

  • 早期(sourroundingdistance < 0)和
  • 的那些
  • 后来的那些(sourroundingdistance > 0

现在使用ORDER BY + LIMIT 1再次执行相同的技巧,我们可以隔离一个问题。但是,由于我们必须单独考虑这两个集合,我们必须执行相同的查询(稍作修改)两次 - 这就是为什么这个野兽变得如此长久的原因。

全部放在一起

我们现在有两个查询,

  • 包含相应的发布日期和
  • 分别表示前一个下一个发行日期。

这两个查询的共同属性是相应的发布日期。因此,我们可以通过这个公共属性将这两个查询相互连接起来。

并且voilà:“怪物”查询提供了以下所需结果:

theIssue    theNext     thePrevious
2015-01-10  2015-01-15  2015-01-08

修订

虽然我简要了解了查询的执行计划(EXPLAIN),并且惊讶于复杂性仍然非常有限,从维护的角度来看,这样的查询是一场噩梦。

如果你能够并且你可以负担得起顺序执行多个语句,那么可能有更好的方法,而不是向服务器发出这么大的查询。例如,您可以触发包含第一层内容的第一个查询,获取结果并将其用作第二个查询的输入,您将使用该查询确定“下一个问题”。第三个查询可以检索“上一个问题”。

此外,如果你的环境允许,你可以考虑为它编写一个函数(CREATE FUNCTION),它以更顺序的方式封装所有这些函数。

所有这些只是相同方法的变体。最后,我希望我可以使用SQL为你提供必要的“武器”来解决这个问题。

答案 1 :(得分:0)

通过尝试在单个查询中执行此操作,您真的很复杂。 Something like this应该可以帮助您获取最近的日期:

SELECT * FROM magazines ORDER BY ABS(TIMESTAMPDIFF(SECOND, issuedate, '1962-05-22')) LIMIT 1;

然后你有一个约会,可以得到另外两个:

SELECT id FROM magazines WHERE issuedate > $current_date LIMIT 1;
SELECT id FROM magazines WHERE issuedate < $current_date LIMIT 1;

从代码中删除的复杂程度可能比执行单个查询所节省的时间更少。