$a = 1950-05-01
$b = 1965-08-10
$c = 1990-12-30
$d = 1990-12-29
$e = 2012-09-03
从按日期升序排序的mysql数据库中检索日期。
我需要一个mysql或PHP脚本来获取具有最大天差的两个 CONSECUTIVE 日期。
解释:脚本应计算$ 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
答案 0 :(得分:14)
假设每个日期都有一个顺序id
。见in action。
<强>模式强>
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
<强>结果强>
| DATE1 | DATE2 | DIFF | -------------------------------------------------------------------------- | August, 10 1965 00:00:00-0700 | December, 30 1990 00:00:00-0800 | 9273 |
$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";
<强>结果强>
The maximum days difference is between 1965-08-10 and 1990-12-30, with a difference of 9273.0416666667 days
更新1
关于PHP解决方案,如果您的日期不合适,您可以使用sort($array);
在循环之前对数组进行排序。
答案 1 :(得分: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
之间。
为了使上一个和下一个日期彼此相邻(为了方便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 |
+----------+------------+
请注意WHERE
条件,即日期必须介于两个指定日期之间。您可以在此处从脚本中插入开始/结束日期参数。
现在我们已经对每个日期进行了排名,现在我们需要根据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 |
+----------+-------------+----------+------------+
“略微调整的参数”只是第二个子选择中的ascrank变量(@b_rn
)从-1
而不是0
开始。这样,a.ascrank = b.ascrank
的连接条件以升序加入 next 日期。我们也可以将这两个变量初始化为0
,但是在a.ascrank = b.ascrank-1
的条件下加入,这会产生相同的结果。
但是等一下,5
的ascrank发生了什么?因为这是订单中的最后一个日期,所以没有日期可以从中获取差异,因此它不需要出现在结果的左侧,只需要与其前一个日期进行比较。
现在我们彼此相邻,我们可以在两者之间取得日期差异(通过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 |
+-------------+------------+--------+
现在,选择最大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 Demo of Final Result
我们已经得出了最终结果。
答案 2 :(得分:3)
我正在使用njk的表格方案 - 并在我的mysql db上检查它。
<强>方案强>
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')
<强> QUERY 强>
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
答案 3 :(得分: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 |
+------------+------------+----------+
答案 4 :(得分: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)
答案 5 :(得分:1)
如果日期在表上你可以做类似的事情(这不是T-SQL,它只是一个算法,要获得previous_date,你需要在同一个表上使用aclias X的另一个选择top 1例如,其中X.date&lt; = date)
select date, datediff(date, previous_date)
并按第二列desc排序,因此第一行将是您想要的日期
答案 6 :(得分: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'子句,只获取第一行,其中最大值为'difference'。请注意,您必须使用两个不同的变量来生成两个子查询的日期顺序。
答案 7 :(得分: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列一起使用,而无需指定格式字符串以包括小时,分钟,秒。)
别名为d1
且d2
的内联视图包含的查询返回您希望行(日期)比较的SPECIFIED顺序中的日期列表。您需要这些行的顺序为如果你想保证查询的结果一致,那就是确定性的。
作为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');
答案 8 :(得分: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);
答案 9 :(得分: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));
答案 10 :(得分: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
作为奖励,我的功能也会为您提供两个日期之间的天数。
答案 11 :(得分: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
答案 12 :(得分:-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];