10
$a = 1950-05-01
$b = 1965-08-10
$c = 1990-12-30
$d = 1990-12-29
$e = 2012-09-03

从按日期升序排序的 mysql 数据库中检索日期。

我需要一个 mysql 或 PHP 脚本来获取两个具有最大天数差异的连续日期。

说明:脚本要计算$a和$b、$b和$c、$c和$d、$d和$e、$e和$a之间的天数,然后输出天数相差最大的两个日期.

有没有办法用快速的 mysql/php 代码来做到这一点,或者我应该用下面的脚本做一些循环(在 stackoverflow 上的另一个问题上找到它)?

$now = time(); // or your date as well
$your_date = strtotime("2010-01-01");
$datediff = $now - $your_date;
echo floor($datediff/(60*60*24));

列出日期的查询:

SELECT date AS count FROM table WHERE column1 = 'YES' AND data BETWEEN 1950-01-01 AND 2012-09-04
4

13 回答 13

14

MySQL 解决方案

假设每个日期都有一个连续的id. 看到它在行动

架构

CREATE TABLE tbl (
  id tinyint,
  dt date);

INSERT INTO tbl VALUES 
(1, '1950-05-01'),
(2, '1965-08-10'),
(3, '1990-12-30'),
(4, '1990-12-29'),
(5, '2012-09-03')

询问

SELECT a.dt AS date1, 
    (SELECT dt FROM tbl WHERE id = a.id - 1) AS date2,
    DATEDIFF(a.dt, b.dt) AS diff
FROM tbl a
LEFT JOIN tbl b ON b.id = a.id -1
GROUP BY a.id
ORDER BY diff DESC
LIMIT 1

结果

| 日期1 | 日期2 | 差异 |
-------------------------------------------------- ----------------------
| 1965 年 8 月 10 日 00:00:00-0700 | 1990 年 12 月 30 日 00:00:00-0800 | 9273 |

PHP解决方案

$array = array('1950-05-01', '1965-08-10', '1990-12-30', '1990-12-29', '2012-09-03');

$maxDiff = 0;
$maxStart = NULL;
$maxEnd = NULL;

for($i = 1; $i <= count($array); $i++) {
    if(isset($array[$i])) {
        $diff = (strtotime($array[$i]) - strtotime($array[$i-1])) / (60*60*24);

        if($diff > $maxDiff) {
            $maxDiff = $diff;
            $maxStart = $array[$i-1];
            $maxEnd = $array[$i];
        }
    }
}

echo "The maximum days difference is between $maxStart and $maxEnd, with a difference of $maxDiff days";

结果

最大天数差异在 1965-08-10 和 1990-12-30 之间,相差 9273.0416666667 天

更新 1

关于 PHP 解决方案,如果您的日期不按顺序排列,您可以在循环之前使用sort($array);.

于 2012-09-07T16:06:45.697 回答
4

您可以使用此单语句解决方案:

SELECT   a.date date1,
         b.date date2,
         DATEDIFF(b.date, a.date) ddiff
FROM     (
         SELECT     @a_rn:=@a_rn+1 ascrank,
                    date
         FROM       tbl
         CROSS JOIN (SELECT @a_rn:=0) var_init
         WHERE      date BETWEEN '1950-05-01' AND '2012-09-04'
         ORDER BY   date
         ) a
JOIN     (
         SELECT     @b_rn:=@b_rn+1 ascrank,
                    date
         FROM       tbl
         CROSS JOIN (SELECT @b_rn:=-1) var_init
         WHERE      date BETWEEN '1950-05-01' AND '2012-09-04'
         ORDER BY   date
         ) b ON a.ascrank = b.ascrank
ORDER BY ddiff DESC
LIMIT    1

查询细分


给定这个示例数据集:

CREATE TABLE tbl (
  date DATE
);

INSERT INTO tbl VALUES
('1950-05-01'),
('1965-08-10'),
('1990-12-30'),
('1990-12-29'),
('2012-09-03');

我们希望找到两个连续日期之间的最大差异(意思是,给定按升序排列的日期,找到日期与其前一个日期的最大天数差异

我们期望输出:

+-------------+------------+--------+
| date1       | date2      | ddiff  |
+-------------+------------+--------+
| 1965-08-10  | 1990-12-29 | 9272   |
+-------------+------------+--------+

因为最大的连续日期差异在1965-08-10和之间1990-12-29


第1步:

为了使上一个和下一个日期彼此并排(为了方便该DATEDIFF功能),我们要做的第一件事是根据日期的升序为每个日期附加一个排名编号。

因为日期的顺序只能依赖于它们本身(不是自动递增的 ID 或排名字段等),所以我们必须自己手动计算排名。

我们通过使用 MySQL 变量来做到这一点。其他使用变量的解决方案要求您执行三个或更多单独的语句。我在查询本身(通过CROSS JOIN)中初始化变量的技术将其包含在单个语句中。

SELECT     @a_rn:=@a_rn+1 ascrank,
           date
FROM       tbl
CROSS JOIN (SELECT @a_rn:=0) var_init
WHERE      date BETWEEN '1950-05-01' AND '2012-09-04'
ORDER BY   date 

渲染:

+----------+------------+
| ascrank  | date       |
+----------+------------+
| 1        | 1950-05-01 |
| 2        | 1965-08-10 |
| 3        | 1990-12-29 |
| 4        | 1990-12-30 |
| 5        | 2012-09-03 |
+----------+------------+

SQLFiddle 演示

请注意WHERE日期必须在两个指定日期之间的条件。您可以在此处插入脚本中的开始/结束日期参数。


第2步:

现在我们已经对每个日期进行了排名,现在我们需要根据ascrank字段对结果执行移位内连接,以便我们获得彼此相邻的连续日期。我们通过将结果包装在子选择中来做到这一点。

由于我们需要自连接派生结果,因此我们必须重复上述步骤,只需稍微调整一下参数:

SELECT   *
FROM     (
         SELECT     @a_rn:=@a_rn+1 ascrank,
                    date
         FROM       tbl
         CROSS JOIN (SELECT @a_rn:=0) var_init
         WHERE      date BETWEEN '1950-05-01' AND '2012-09-04'
         ORDER BY   date
         ) a
JOIN     (
         SELECT     @b_rn:=@b_rn+1 ascrank,
                    date
         FROM       tbl
         CROSS JOIN (SELECT @b_rn:=-1) var_init
         WHERE      date BETWEEN '1950-05-01' AND '2012-09-04'
         ORDER BY   date
         ) b ON a.ascrank = b.ascrank

渲染:

+----------+-------------+----------+------------+
| ascrank  | date        | ascrank  | date       | 
+----------+-------------+----------+------------+
| 1        | 1950-05-01  | 1        | 1965-08-10 | 
| 2        | 1965-08-10  | 2        | 1990-12-29 | 
| 3        | 1990-12-29  | 3        | 1990-12-30 | 
| 4        | 1990-12-30  | 4        | 2012-09-03 | 
+----------+-------------+----------+------------+

SQLFiddle 演示

“稍微调整的参数”只是@b_rn第二个子选择中的ascrank变量()从而-1不是开始0。这样,join 条件就以升序a.ascrank = b.ascrank加入下一个日期。我们也可以保持两个变量在 处初始化0,但在 的条件下加入a.ascrank = b.ascrank-1,这将呈现相同的结果。

但是等等,那个日期发生了什么事5由于那是订单中的最后一个日期,因此在它之后没有日期可以取差,所以它不需要出现在结果的左侧,只需要与它的前一个日期进行比较.


第 3 步:

现在我们有了彼此相邻的连续日期,我们可以获取两者DATEDIFF()之间的日期差(通过):

SELECT   a.date date1,
         b.date date2,
         DATEDIFF(b.date, a.date) ddiff
FROM     (
         SELECT     @a_rn:=@a_rn+1 ascrank,
                    date
         FROM       tbl
         CROSS JOIN (SELECT @a_rn:=0) var_init
         WHERE      date BETWEEN '1950-05-01' AND '2012-09-04'
         ORDER BY   date
         ) a
JOIN     (
         SELECT     @b_rn:=@b_rn+1 ascrank,
                    date
         FROM       tbl
         CROSS JOIN (SELECT @b_rn:=-1) var_init
         WHERE      date BETWEEN '1950-05-01' AND '2012-09-04'
         ORDER BY   date
         ) b ON a.ascrank = b.ascrank

渲染:

+-------------+------------+--------+
| date1       | date2      | ddiff  |
+-------------+------------+--------+
| 1950-05-01  | 1965-08-10 | 5580   |
| 1965-08-10  | 1990-12-29 | 9272   |
| 1990-12-29  | 1990-12-30 | 1      |
| 1990-12-30  | 2012-09-03 | 7918   |
+-------------+------------+--------+

SQLFiddle 演示


第4步:

现在选择最大值很简单ddiff。我们通过ORDER BY / LIMIT 1在现场使用一种技术来做到这一点ddiff

SELECT   a.date date1,
         b.date date2,
         DATEDIFF(b.date, a.date) ddiff
FROM     (
         SELECT     @a_rn:=@a_rn+1 ascrank,
                    date
         FROM       tbl
         CROSS JOIN (SELECT @a_rn:=0) var_init
         WHERE      date BETWEEN '1950-05-01' AND '2012-09-04'
         ORDER BY   date
         ) a
JOIN     (
         SELECT     @b_rn:=@b_rn+1 ascrank,
                    date
         FROM       tbl
         CROSS JOIN (SELECT @b_rn:=-1) var_init
         WHERE      date BETWEEN '1950-05-01' AND '2012-09-04'
         ORDER BY   date
         ) b ON a.ascrank = b.ascrank
ORDER BY ddiff DESC
LIMIT    1

渲染:

+-------------+------------+--------+
| date1       | date2      | ddiff  |
+-------------+------------+--------+
| 1965-08-10  | 1990-12-29 | 9272   |
+-------------+------------+--------+

最终结果的 SQLFiddle 演示

我们已经得出了最终结果。

于 2012-09-08T20:57:30.010 回答
3

我正在使用 njk 的表方案 - 并在我的 mysql 数据库上检查它。

方案

CREATE TABLE tbl (
  id tinyint,
  dt date);

INSERT INTO tbl VALUES 
(1, '1950-05-01'),
(2, '1965-08-10'),
(3, '1990-12-30'),
(4, '1990-12-29'),
(5, '2012-09-03')

询问

SELECT a.id, b.id, ABS(DATEDIFF(a.dt, b.dt)) AS ddiff
 FROM tbl AS a
 JOIN tbl AS b ON (a.id = (b.id + 1)) OR (a.id = (SELECT id FROM tbl ORDER BY id ASC LIMIT 1) AND b.id = (SELECT id FROM tbl ORDER BY id DESC LIMIT 1))
 ORDER BY ddiff DESC
 LIMIT 1

我将所有连续的行(a.id = (b.id + 1))和第一行与最后一行连接起来,如下所示:(a.id = (SELECT id FROM tbl ORDER BY id ASC LIMIT 1) AND b.id = (SELECT id FROM tbl ORDER BY id DESC LIMIT 1))这看起来很奇怪,但效果很好。如果你只有你提到的 5 行,这将是

 SELECT a.id, b.id, ABS(DATEDIFF(a.dt, b.dt)) AS ddiff
  FROM tbl AS a
  JOIN tbl AS b ON (a.id = (b.id + 1)) OR (a.id = 1 AND b.id = 5)
  ORDER BY ddiff DESC
  LIMIT 1

编辑:结果是 1=$a 和 5=$e

于 2012-09-07T18:02:13.453 回答
2

试试这个查询 -

SELECT
  t1.dt,
  @dt_next := (SELECT dt FROM tbl WHERE dt > t1.dt ORDER BY dt LIMIT 1) dt_next,
  DATEDIFF(@dt_next, t1.dt) max_diff
FROM tbl t1
ORDER BY max_diff DESC LIMIT 1;

+------------+------------+----------+
| dt         | dt_next    | max_diff |
+------------+------------+----------+
| 1965-08-10 | 1990-12-29 |     9272 |
+------------+------------+----------+
于 2012-09-12T06:17:58.623 回答
1

举个例子:

mysql> SELECT MIN(version) AS version FROM schema_migrations UNION SELECT MAX(version) FROM schema_migrations;
+----------------+
| version        |
+----------------+
| 20120828071352 |
| 20120830100526 |
+----------------+
2 rows in set (0.00 sec)
于 2012-09-04T15:08:11.323 回答
1

如果日期在表上,您可以执行类似的操作(这不是 T-SQL,它只是一种算法,要获取 previous_date,您需要使用 aclias X 在同一张表上运行另一个 select top 1 例如 where X.日期<=日期)

select date, datediff(date, previous_date)

并按第二列 desc 排序,因此第一行将是您想要的日期

于 2012-09-04T15:11:33.007 回答
1

从创建一个结果集的子查询开始,该结果集具有按升序排列的日期和一个从 1 开始并以 1 递增的 INT 字段 (dateOrder)。

SET @a := 0;
SELECT date, (@a:=@a+1) AS dateOrder FROM dateTable ORDER BY date

现在我们可以通过使用 a.dateOrder = b.dateOrder -1 将此结果集连接到其自身的另一个副本来获得连续日期。在该结果集中,每一行都包含来自原始表的一对连续日期,并且很容易计算差异并对结果集进行排序以找到最大的差异。

SET @a := 0; SET @b := 0;
SELECT a.date as firstDate, b.date as secondDate, 
  datediff(b.date, a.date) AS difference FROM (
    SELECT date, (@a:=@a+1) AS dateOrder FROM dateTable ORDER BY date ) a JOIN (
    SELECT date, (@b:=@b+1) AS dateOrder FROM dateTable ORDER BY date ) b 
  ON a.dateOrder = b.dateOrder - 1
ORDER BY difference desc;

您可以在查询末尾放置一个“limit 1”子句,以仅获取第一行,它具有最大的“差异”值。请注意,您必须使用两个不同的变量来为两个子查询生成日期顺序。

于 2012-09-07T17:43:33.913 回答
1

您返回日期值的查询是非确定性的……如果ORDER BY您的查询中没有子句,则无法保证这些行将以任何特定顺序返回。

在 MySQL 中,查询可以返回您指定的结果集。这是一种方法:

SELECT ABS(DATEDIFF(d.mydate,@prev_date))  AS days_diff
     , DATE_ADD(@prev_date,INTERVAL 0 DAY) AS date1
     , @prev_date := d.mydate              AS date2
  FROM ( SELECT @prev_date := NULL) i
  JOIN ( SELECT d1.*
           FROM (            -- query to return rows in a specific order
                             SELECT mydate       
                               FROM mytable3 
                              WHERE 1
                              ORDER BY foo
                ) d1
          UNION ALL 
         SELECT d2.*
           FROM (            -- query to return rows in a specific order (again)
                             SELECT mydate
                               FROM mytable3 
                              WHERE 1
                              ORDER BY foo
                   LIMIT 1
                 ) d2
       ) d
 ORDER BY days_diff DESC

笔记:

仅当您想考虑日期之间的天数时才需要该ABS()函数,无论第一个日期是在第二个日期之前还是之后,因为该DATEDIFF函数可以返回负值。

DATE_ADD( ,INTERVAL 0 DAY)用户变量周围的函数只是@prev_date将返回值转换为数据类型 DATE。`STR_TO_DATE( ,'%Y-%m-%d') 函数也可以工作。(不同之处在于 DATE_ADD 函数将使用 DATE、DATETIME 和 TIMESTAMP 列,而无需指定格式字符串以包括小时、分钟、秒。)

内联视图别名为d1d2包含查询,该查询以您希望比较行(日期)的指定顺序返回日期列表。如果要保证查询结果一致,则需要确定这些行的顺序.

别名为 as 的内联视图d2中的查询与 中的查询相同d1,除了添加了 LIMIT 1 子句。因为您指定要将 $e 与 $a 进行比较,所以我们将查询中的第一行“附加”到末尾,以便我们可以将第一行与查询中的最后一行进行比较。

结果集中的date1列不是 DATE 数据类型,但可以轻松转换为 DATE

如果您希望从两行返回其他列以及日期值,则可以使用相同的方法轻松处理。d1中的查询d2只需要返回附加列:

SELECT ABS(DATEDIFF(d.mydate,@prev_date)) AS days_diff
     , @prev_foo                          AS foo1
     , @prev_date                         AS date1
     , @prev_foo  := d.foo                AS foo2
     , @prev_date := d.mydate             AS date2
  FROM ( SELECT @prev_date := NULL, @prev_foo := NULL) i
  JOIN ( SELECT d1.*
           FROM (            -- query to return rows in a specific order
                             SELECT mydate, foo
                               FROM mytable3 
                              WHERE 1
                              ORDER BY foo
                ) d1
          UNION ALL 
         SELECT d2.*
           FROM (            -- query to return rows in a specific order (again)
                             SELECT mydate, foo
                               FROM mytable3 
                              WHERE 1
                              ORDER BY foo
                   LIMIT 1
                 ) d2
       ) d
 ORDER BY days_diff DESC
 LIMIT 1

要设置测试用例:

CREATE TABLE `mytable3` (`foo` varchar(1), `mydate` date);

INSERT INTO mytable3 VALUES 
('a','1950-05-01'),
('b','1965-08-10'),
('c','1990-12-30'),
('d','1990-12-29'),
('e','2012-09-03');
于 2012-09-12T22:36:47.167 回答
0

这是PHP解决方案

$dates  = array('1970-05-01', '1975-08-10', '1990-12-30', '1990-12-29', '2012-09-03');
$sorted = array();

foreach($dates as $i => $date) {
    $date2 = isset($dates[$i+1]) ? $dates[$i+1] : $dates[0];
    $diff  = (strtotime($date2) - strtotime($date))/(60 * 60 * 24);

    $sorted[abs($diff)] = array('start' => $date, 'end' => $date2);
}

ksort($sorted);

$result = end($sorted);
于 2012-09-09T07:13:01.523 回答
0

我会使用一些简单的 PHP,因为它既快速又整洁:

function get_the_two_consecutive_dates_with_the_maximum_days_difference($dates) {
    foreach ($dates as $i => $date) {

        $previousDate = $dates[$i - 1];
        if (!$previousDate) continue;

        $diff = strtotime($date) - strtotime($previousDate);
        if ($maxDiff < $diff) {
            $maxDiff = $diff;
            $dateA = $previousDate;
            $dateB = $date;
        }

    }
    return array($dateA, $dateB, $maxDiff);

}

// Usage
$arr = Array ( '2012-01-01', '2012-02-01', '2012-03-01', '2012-04-12', 
               '2012-05-10', '2012-08-05', '2012-09-01', '2012-09-04' );

var_dump(get_the_two_consecutive_dates_with_the_maximum_days_difference($arr));
于 2012-09-12T17:14:57.127 回答
0

我已经使用 PHP 的DateTime类寻求解决方案。原因是 strtotime() 无法指定传递给它的日期格式。在我看来,这会对将返回的内容产生歧义,因此我已经停止使用它来支持 DateTime。

由于您提供的示例日期顺序不正确,我假设它们需要先排序。以下功能实现了这一点: -

/**
 * Sorts an array of dates in given format into date order, oldest first
 * @param array $dates
 * @param type $format Optional format of dates.
 * 
 * @return array with dates in correct order.
 */
function sortArrayOfDates(array $dates, $format = 'Y-m-d')
{
    $result = array();
    foreach($dates as $date){
        $timeStamp = DateTime::createFromFormat($format, $date)->getTimestamp();
        $result[$timeStamp] = $date;
    }
    sort($result);
    return $result;
}

现在我们可以编写一个函数来完成这项工作:-

/**
 * Returns the longest gap between sets of dates
 * 
 * @param array $dates
 * @param string Optional. Format of dates.
 * 
 * @return array Containing the two dates with the longest interval and the length of the interval in days.
 */
private function longestGapBetweenDates(array $dates, $format = 'Y-m-d')
{
    $sortedDates = sortArrayOfDates($dates);
    $maxDiff = 0;
    $result = array();
    for($i = 0; $i < count($dates) - 1; $i++){
        $firstDate = DateTime::createFromFormat($format, $sortedDates[$i]);
        $secondDate = DateTime::createFromFormat($format, $sortedDates[$i + 1]);
        $diff = $secondDate->getTimestamp() - $firstDate->getTimestamp();
        if($diff > $maxDiff){
            $maxDiff = $diff;
            $result = array($firstDate->format($format), $secondDate->format($format), $firstDate->diff($secondDate)->days);
        }
    }
    return $result;
}

使用您的示例列表:-

$a = '1950-05-01';
$b = '1965-08-10';
$c = '1990-12-30';
$d = '1990-12-29';
$e = '2012-09-03';

var_dump(longestGapBetweenDates(array($a, $b, $c, $d, $e)));

输出:-

array
  0 => string '1965-08-10' (length=10)
  1 => string '1990-12-29' (length=10)
  2 => int 9272

作为奖励,我的函数也为您提供了两个日期之间的天数。

于 2012-09-13T16:05:50.973 回答
0
    Select t1.date as 'Date1', t2.date AS 'Date2', DATEDIFF(t2, t1) as 'DateDiff'
From    YourTable t1
    Left outer join YourTable t2
        ON     t1.Id <= t2.Id 
            OR (t1.Id = (Select Max(Id) From YourTable) AND t2.Id=(SELECT Min(Id) From YourTable) )
    Left outer join YourTable t3
        ON t1.Id < t3.Id AND t3.Id < t2.Id
    WHERE t3.Id IS NULL
    ORDER BY 'DateDiff' DESC
    Limit 1,1
于 2012-09-14T11:25:30.510 回答
-1

要解决此问题,请使用具有此功能的数组:

<?php 
$date_array = array('1950-05-01','1965-08-10','1990-12-30','1990-12-29','2012-09-03');

function get_max_difference_dates($dates=array()){
    if(!count($dates)){
        return false;
    }
    $max_dates_diff = 0;
    $max_dates_diff_index = 0;
    for($i=0;$i<count($dates)-1;$i++){
        $temp_diff = strtotime($dates[$i+1]) - strtotime($dates[$i]);
        if($temp_diff>$max_dates_diff){
            $max_dates_diff = $temp_diff;
            $max_dates_diff_index = $i;
        }
    }
    return $max_dates_diff_index;
}

var_dump(get_max_difference_dates($date_array));

根据我的汇编,上述答案是"1"

在索引之后,您可以通过返回的索引获取日期并向其添加一个。

$indx = get_max_difference_dates($date_array);
$date1 = $date_array[$indx];
$date2 = $date_array[$indx+1];
于 2012-09-12T10:07:52.283 回答