在MySQL或PHP中,在两个日期之间生成50个随机持续时间间隔

时间:2014-08-29 15:58:55

标签: php mysql date datetime date-range

我正在尝试将50个日期时间范围的随机列表插入到MySQL数据库中。这些日期范围必须介于特定的开始日期和结束日期之间,并且它们必须相邻并按时间排序。

下面是我想在 2014-09-01 00:00:00 - 2014-10-12 23:59:59 :

start_time                     end_time
--------------------------------------------------------
2014-09-01 00:00:00            2014-09-01 02:45:12  
2014-09-01 02:45:13            2014-09-01 04:12:33
2014-09-01 04:12:34            2014-09-02 12:12:20
....
2014-10-12 23:30:13            2014-10-12 23:59:59

我不确定这是否可以仅在MySQL中完成,或者我是否需要PHP脚本。生成日期之间的随机日期列表很好,它将随机性在50个条目之间平均分配,让我感到困惑。

这可以在MySQL中完成,如果可以,那么首选方法是什么?

修改 为了更好地解释我的问题,我有一个日期范围,我需要生成一个包含50个开始和结束日期的列表。我应该提到,就像我的例子一样,开始日期必须是在上一个结束日期之后的下一秒。

2 个答案:

答案 0 :(得分:1)

您可以generate random dates using php添加一些功能来检查其他日期范围:

您必须加载内部日期范围:(我使用数组,但您将从数据库加载)

$ranges = array(
    0 => array('start' => '2014-09-01 00:00:00', 'end' => '2014-09-01 02:45:12'),
    1 => array('start' => '2014-09-01 02:45:13', 'end' => '2014-09-01 04:12:33'),
    2 => array('start' => '2014-09-01 04:12:34', 'end' => '2014-09-02 12:12:20'),
    3 => array('start' => '2014-10-12 23:30:13', 'end' => '2014-10-12 23:59:59')
);

检查日期的功能在$ ranges数组中:

function date_in_range($date_ranges,$rand_epoch){
    foreach($date_ranges as $date){
        $min_epoch = strtotime($date['start']);
        $max_epoch = strtotime($date['end']);
        if($rand_epoch >= $min_epoch && $rand_epoch <= $max_epoch){
            return true;
        }
    }
    return false;
}

修改函数以生成随机日期:

function rand_date($min_date, $max_date, $internal_ranges) {
    /* Gets 2 dates as string, earlier and later date.
       Returns date in between them.
    */

    $min_epoch = strtotime($min_date);
    $max_epoch = strtotime($max_date);

    $rand_epoch = rand($min_epoch, $max_epoch);

   // If date is not in internal range, get another random date again:
    while(!date_in_range($internal_ranges,$rand_epoch)){
        $rand_epoch = rand($min_epoch, $max_epoch);
    }

    return date('Y-m-d H:i:s', $rand_epoch);
}

// Testing
print rand_date('2014-09-01 00:00:00','2014-10-12 23:59:59',$ranges);

答案 1 :(得分:1)

是的,您可以在MySQL中执行此操作。 N个区间的一般算法是:

  1. 在您的开始时间+ 1秒和结束时间之间生成N-1个不同的随机时间戳 - 2秒。
  2. 对它们进行排序,最老到最小。这些是你的间隔开始点。
  3. 从每个终点减去一秒钟,以获得前一个间隔的起点。
  4. 示例
    Install these generator views,然后:

    CREATE TABLE times (id INT UNSIGNED NOT NULL PRIMARY KEY AUTO_INCREMENT,begin_time DOUBLE NOT NULL,end_time DOUBLE NOT NULL DEFAULT 0);
    INSERT INTO times (begin_time) SELECT @low bp UNION SELECT s.bp FROM (SELECT t.bp FROM (SELECT @low + FLOOR(RAND() * (@high-@low)) bp FROM generator_256 JOIN (SELECT @low := UNIX_TIMESTAMP('2014-09-01 00:00:00'), @high := UNIX_TIMESTAMP('2014-10-12 23:59:59')) init LIMIT 49) t ORDER by bp) s;
    UPDATE times JOIN (SELECT curr.id, curr.begin_time, (SELECT next.begin_time-1 FROM times next WHERE next.id=curr.id+1) end_time FROM times curr) g ON g.id = times.id SET times.end_time = COALESCE(g.end_time, UNIX_TIMESTAMP('2014-10-12 23:59:59'));
    SELECT FROM_UNIXTIME(begin_time), FROM_UNIXTIME(end_time) FROM times;
    +---------------------------+-------------------------+
    | FROM_UNIXTIME(begin_time) | FROM_UNIXTIME(end_time) |
    +---------------------------+-------------------------+
    | 2014-09-01 00:00:00       | 2014-09-02 13:32:45     |
    | 2014-09-02 13:32:46       | 2014-09-03 07:57:24     |
    | 2014-09-03 07:57:25       | 2014-09-04 17:34:01     |
    | 2014-09-04 17:34:02       | 2014-09-04 19:46:25     |
    | 2014-09-04 19:46:26       | 2014-09-05 17:44:48     |
                               ...
    | 2014-10-10 18:39:47       | 2014-10-11 05:11:13     |
    | 2014-10-11 05:11:14       | 2014-10-11 11:27:29     |
    | 2014-10-11 11:27:30       | 2014-10-12 13:03:02     |
    | 2014-10-12 13:03:03       | 2014-10-12 17:55:54     |
    | 2014-10-12 17:55:55       | 2014-10-12 19:11:11     |
    | 2014-10-12 19:11:12       | 2014-10-12 23:59:59     |
    +---------------------------+-------------------------+
    50 rows in set (0.00 sec)
    

    <强>解释
    让我们一步一步地打破这些。要在MySQL中生成行,您必须使用generator view。每次请求时,生成器视图只会为您提供N行。例如,获得49行(N-1):

    SELECT * FROM generator_256 LIMIT 49;
    

    要在MySQL中的两个其他数字之间生成单个随机数,请使用随机数公式low + (RAND() * (high-low))。这个公式与生成器视图相结合,为我们提供了步骤1所需的49个起点:

    SELECT (@low + FLOOR(RAND() * (@high-@low))) AS bp FROM generator_256 LIMIT 49;
    

    (我在这里使用会话变量来保持SQL简单。他们将在一点点成为查询的一部分。如果你想调试,请记住bp是时间戳,所以FROM_UNIXTIME( bp)将向您展示一种人性化的格式。)

    现在,要对列表进行排序,请使用子查询:如果对生成的查询进行排序,则会在开始时间附近获得聚类的随机值。所以,主要完成第2步:

    SELECT t.bp FROM (SELECT @low + FLOOR(RAND() * (@high-@low)) bp FROM generator_256 LIMIT 49) t ORDER by t.bp;
    

    现在,它开始变得棘手。对于任何给定的行,我们希望以比下一行开始时间少一秒的时间填充结束时间。虽然有几种方法可以实现,但我认为最清楚的是使用目标表(或其副本)来存储我们生成的起点。 (注意我在这里初始化了@low和@high的值,并且包含了列表中的起始点):

    CREATE TABLE times (id INT UNSIGNED NOT NULL PRIMARY KEY AUTO_INCREMENT,begin_time DOUBLE NOT NULL,end_time DOUBLE NOT NULL DEFAULT 0);
    INSERT INTO times (begin_time) SELECT @low bp UNION SELECT s.bp FROM (SELECT t.bp FROM (SELECT @low + FLOOR(RAND() * (@high-@low)) bp FROM generator_256 JOIN (SELECT @low := UNIX_TIMESTAMP('2014-09-01 00:00:00'), @high := UNIX_TIMESTAMP('2014-10-12 23:59:59')) init LIMIT 49) t ORDER by bp) s;
    

    最后,我们可以使用联合更新添加结束时间。

    UPDATE times JOIN (SELECT curr.id, curr.begin_time, (SELECT next.begin_time-1 FROM times next WHERE next.id=curr.id+1) end_time FROM times curr) g ON g.id = times.id SET times.end_time = COALESCE(g.end_time, UNIX_TIMESTAMP('2014-10-12 23:59:59'));
    

    在我的例子中,我没有做过两件事:

    1. 我忽略了在开始后一秒钟和结束前两秒钟开始的需要。因此,您的一个随机值可能等于开始或结束点,这可能违反您的声明需求。您可以使用MySQL INTERVAL运算符添加这些约束。
    2. 在一个真正随机的系统中,长序列的相同值与混杂值一样可能。例如,在二进制中,000000000000000与111111111111111一样可能与0101010111101011一样。这意味着我们可以从查询中获得50个相同的日期。您可以通过生成一大堆随机时间(可能是1000秒)并从中剔除来解决这个问题。