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

日付は、日付の昇順で並べ替えられた mysql データベースから取得されます。

最大日数差のある 2 つのCONSECUTIVE日付を取得するには、mysql または PHP スクリプトが必要です。

説明: スクリプトは、$a と $b、$b と $c、$c と $d、$d と $e、$e と $a の間の日数を計算し、日数差が最大の 2 つの日付を出力する必要があります。 .

高速な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

結果

| | DATE1 | DATE2 | 差分 |
-------------------------------------------------- ------------------------
| | 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');

連続する 2 つの日付間の最大の差を見つけたいと考えています (つまり、日付が昇順で並べられている場合、日付とその直前の日付の最大日差を見つけます)。

次の出力が期待されます。

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

連続する日付の最大の差は と の間1965-08-10にあるため1990-12-29です。


ステップ1:

前の日付と次の日付を並べて取得するために (DATEDIFF関数を容易にするために) 最初に行うことは、日付の昇順に基づいて各日付にランク番号を付けることです。

日付の順序はそれ自体 (自動インクリメント ID やランク フィールドなどではない) 以外には依存できないため、手動でランクを自分で計算する必要があります。

これは、MySQL 変数を使用して行います。変数を使用するその他のソリューションでは、3 つ以上の個別のステートメントを実行する必要があります。クエリ自体で( を介して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 つの日付の間になければならないという条件に注意してください。これは、スクリプトから開始日/終了日のパラメーターを挿入する場所です。


ステップ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 デモ

「わずかに調整されたパラメーター」とは、2 番目のサブセレクトの ascrank 変数 ( ) がではなく@b_rnから始まるということです。このように、の結合条件は次の日付を昇順で結合します。両方の変数を で初期化したままにしておくこともできますが、 の条件で結合すると、同じ結果が得られます。-10a.ascrank = b.ascrank0a.ascrank = b.ascrank-1

でも待って、アスクランクのデートはどうしたの5それは注文の最後の日付であるため、それ以降に差を求める日付はありません。そのため、結果の左側に表示する必要はなく、直前の日付と比較するだけで済みます。 .


ステップ 3:

DATEDIFF()連続した日付が並んだので、2 つの間の日付の差を ( 経由で)取ることができます。

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 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')

クエリ

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

昇順の日付と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;

クエリの最後に「limit1」句を配置して、「difference」の最大値を持つ最初の行のみを取得できます。2つのサブクエリの日付順を生成するには、2つの異なる変数を使用する必要があることに注意してください。

于 2012-09-07T17:43:33.913 に答える
1

日付がテーブル上にある場合は、次のようなことができます (これは T-SQL ではなく、単なるアルゴリズムです。previous_date を取得するには、同じテーブルで別のトップ 1 を aclias X で選択する必要があります。たとえば、 X.date<=date)

select date, datediff(date, previous_date)

2番目の列dessで並べ替えると、最初の行が必要な日付になります

于 2012-09-04T15:11:33.007 に答える
1

日付値を返すクエリは非決定論的です...ORDER BYクエリに句がない場合、行が特定の順序で返されるという保証はありません。

MySQL では、クエリは指定した結果セットを返すことができます。1 つのアプローチを次に示します。

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()ため、最初の日付が 2 番目の日付の前後にあるかどうかに関係なく、日付間の日数を考慮したい場合にのみ必要です。DATEDIFF

ユーザー変数を囲むDATE_ADD( ,INTERVAL 0 DAY)関数は@prev_date、戻り値をデータ型 DATE にキャストするためのものです。`STR_TO_DATE( ,'%Y-%m-%d') 関数も同様に機能します。(違いは、DATE_ADD 関数は、時間、分、秒を含む書式文字列を指定する必要なく、DATE、DATETIME、および TIMESTAMP 列で機能することです。)

d1としてエイリアス化されたインライン ビューd2には、行 (日付) を比較する指定された順序で日付のリストを返すクエリが含まれています。クエリからの一貫した結果を保証する場合は、これらの行の順序を決定論的にする必要があります。 .

as でエイリアス化されたインライン ビューのクエリは、LIMIT 1 句が追加されていることを除いて、d2のクエリと同じです。d1$e と $a を比較するように指定したため、最初の行をクエリの最後の行と比較できるように、クエリの最初の行を最後まで「追加」します。

結果セットのdate1列は DATE データ型ではありませんが、簡単に DATE にキャストできます

2 つの行から他の列を日付値と共に返したい場合は、同じ方法で簡単に処理できます。のクエリはd1d2追加の列を返すだけで済みます。

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
    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 に答える
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

おまけとして、私の関数は 2 つの日付の間の日数も示します。

于 2012-09-13T16:05:50.973 に答える
-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".

インデックスの後に、返されたインデックスとそれに1を追加することで日付を取得できます。

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