是的,您可以在 MySQL 中执行此操作。 N个区间的通用算法是:
- 在开始时间 + 1 秒和结束时间 - 2 秒之间生成 N-1 个不同的随机时间戳。
- 对它们进行排序,从大到小。这些是您的间隔开始点。
- 从每个结束点减去一秒,得到前一个区间的开始点。
示例
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'));
在我的示例中,我省略了两件事:
- 我忽略了在开始后一秒和结束前两秒开始的需要。因此,您的随机值之一可能等于开始点或结束点,这将违反您声明的需求。您可以使用 MySQL
INTERVAL 运算符添加这些约束。
- 在一个真正随机的系统中,一长串相同的值与一个混乱的值一样可能。例如,在二进制中,000000000000000 的可能性与 111111111111111 的可能性与 0101010111101011 的可能性一样。这意味着我们可以从查询中得到 50 个相同的日期。您可以通过生成一大堆随机时间(可能是 1000 秒)并从中剔除来解决此问题。