【问题标题】:Distinct random time generation in the fixed interval固定间隔内不同的随机时间生成
【发布时间】:2014-06-12 09:41:23
【问题描述】:

我正在尝试为从数据集中选择的每一行在上午 8:00 和晚上 8:00 之间生成一个随机时间,但是,我总是得到 same 每行的随机值 - 我希望每行 不同

表架构和数据:

╔══════╦════════════════╗
║  ID  ║  CREATED_DATE  ║
╠══════╬════════════════╣
║ ID/1 ║   26/04/2014   ║
║ ID/2 ║   26/04/2014   ║
║ ID/3 ║   26/04/2014   ║
║ ID/4 ║   26/04/2014   ║
║ ID/5 ║   26/04/2014   ║
╚══════╩════════════════╝

当前的 SQL 语句:

SELECT [ID]
     , MyFunction.dbo.AddWorkDays(14, [CREATED_DATE]) AS [New Date]
     , CONVERT(VARCHAR, DATEADD(MILLISECOND, CAST(43200000 * RAND() AS INT), CONVERT(TIME, '08:00')), 114) AS [New Time]
FROM [RandomTable]

当前结果([New Time] 列中每一行的时间相同):

╔══════╦════════════════╦════════════════╗
║  ID  ║    New Date    ║    New Time    ║
╠══════╬════════════════╬════════════════╣
║ ID/1 ║   10/05/2014   ║    09:41:43    ║
║ ID/2 ║   10/05/2014   ║    09:41:43    ║
║ ID/3 ║   10/05/2014   ║    09:41:43    ║
║ ID/4 ║   10/05/2014   ║    09:41:43    ║
║ ID/5 ║   10/05/2014   ║    09:41:43    ║
╚══════╩════════════════╩════════════════╝

期望的结果([New Time] 列中每一行的不同时间):

╔══════╦════════════════╦════════════════╗
║  ID  ║    New Date    ║    New Time    ║
╠══════╬════════════════╬════════════════╣
║ ID/1 ║   10/05/2014   ║    09:41:43    ║
║ ID/2 ║   10/05/2014   ║    15:05:23    ║
║ ID/3 ║   10/05/2014   ║    10:01:05    ║
║ ID/4 ║   10/05/2014   ║    19:32:45    ║
║ ID/5 ║   10/05/2014   ║    08:43:15    ║
╚══════╩════════════════╩════════════════╝

关于如何解决这个问题的任何想法?以上所有只是示例数据——我的真实表有大约 2800 条记录(不确定这是否会对任何人的建议产生影响)。

【问题讨论】:

  • @Ben :我不知道,但由于自 4 月以来答案不被接受,我想它不符合需求,也许它太慢或其他什么。我试图从 OP 那里得到反馈,但没有成功,所以你也可以向他提出这个问题。在这个问题上给予赏金有两个原因:1)它首先没有受到太多关注,所以我认为很遗憾,因为我在这个网络上没有找到任何类似的东西。2)我我正在寻找一种更好的方法来实现相同的目标,因为我对 SQL Server 不像我使用的其他 RDBMS 那样熟悉。
  • 我刚刚看到你是回答这个问题的人。我不是有点困惑,但我想去吧。老实说,我认为你不会比你的回答更好:-)。
  • @AMC 4 月份发布的答案是否对您没有帮助,如果不是,您的问题是什么需要您发布赏金?
  • @notulysses 我意识到您不是 OP,因此可能有稍微不同的要求,但是:1)可以在同一日期的时间发生冲突吗? 2)可以吗假设表上会有一个 INT 或 BIGINT IDENTITY 字段?

标签: sql sql-server sql-server-2008 tsql random-time-generation


【解决方案1】:

OP 仅使用 rand() 时遇到的问题是由于它的评估每个查询一次

来自documentation

如果未指定 seed,SQL Server 数据库引擎会随机分配一个 seed 值。对于指定的 seed 值,返回的结果始终相同。

下面描述的方法消除了优化并抑制了这种行为,因此rand() 被评估每行一次

dateadd( second
       , rand(cast(newid() as varbinary)) * 43200
       , cast('08:00:00' as time) )
  • newid() 生成 uniqueidentifier 类型的唯一值;
  • cast 转换该值以用作rand([seed]) 函数中的seed 以生成从0 到1 的伪随机float 值,由于 seed 始终是唯一的,因此返回值也是唯一的。

SQLFiddle

【讨论】:

  • 单独RAND() 不起作用的原因是RAND()runtime constant,因此被折叠(每个查询仅评估一次)。添加NEWID() 会移除优化,RAND() 将被评估一次每行
【解决方案2】:

您也可以使用:

SELECT DATEADD(s, ABS(CHECKSUM(NewId()) % 43201), CAST('08:00:00' AS Time))

ABS(CHECKSUM(NewId()) % 43201)043200 之间生成一个随机数。见Discussion here

SQL Fiddle

MS SQL Server 2008 架构设置

查询 1

SELECT DATEADD(s, ABS(CHECKSUM(NewId()) % 43201), CAST('08:00:00' AS Time)) AS [RandomTime]
FROM 
( VALUES (1), (2), (3), (4), (5)
) Y(A)
CROSS JOIN
( VALUES (1), (2), (3), (4), (5)
) Z(A)

Results

|    RANDOMTIME    |
|------------------|
| 16:51:58.0000000 |
| 10:42:44.0000000 |
| 14:01:38.0000000 |
| 13:33:51.0000000 |
| 18:00:51.0000000 |
| 11:29:03.0000000 |
| 10:21:14.0000000 |
| 16:38:27.0000000 |
| 09:55:37.0000000 |
| 13:21:13.0000000 |
| 11:29:37.0000000 |
| 10:57:49.0000000 |
| 14:56:42.0000000 |
| 15:33:11.0000000 |
| 18:49:45.0000000 |
| 16:23:28.0000000 |
| 09:00:05.0000000 |
| 09:20:01.0000000 |
| 11:26:23.0000000 |
| 15:26:23.0000000 |
| 10:38:44.0000000 |
| 11:46:30.0000000 |
| 16:00:59.0000000 |
| 09:29:18.0000000 |
| 09:09:19.0000000 |

【讨论】:

  • @notulysses 注意我使用的是 CHECKSUM 而不是 RAND
  • 我不是 talking 关于答案的内容 - 我很高兴还有另一种方法不同。
  • @notulysses 哦,我明白了,我没有注意到这个问题太老了。当您发布它时,您的答案应该已经被接受了
【解决方案3】:

原问题解读:

问题说明:

  • 在上午 8:00 到晚上 8:00 之间生成一个随机时间(即 12 小时窗口)
  • 每一行应该是不同的(即在所有行中都是唯一的)
  • 真实表大约有 2800 条记录

现在考虑以下几点:

  • 示例数据仅显示一个日期
  • 24 小时有 86,400 秒,因此 12 小时有 43,200 秒

以下方面存在一些歧义:

  • 在“每行都不同”的上下文中,究竟什么是随机的,因为不能保证真正的随机值对于每一行都是不同的。事实上,真正随机数可能理论上是每一行的相同。那么是强调“随机”还是“不同”?还是我们真的在谈论不同但不是按顺序排列的(以呈现随机性而不是实际随机性)?
  • 如果行数超过 2800 行怎么办?如果有 100 万行怎么办?
  • 如果可以有超过 43,200 行,如何处理“不同的每一行”(因为不可能在所有行中都具有唯一性)?
  • 日期会有所不同吗?如果是这样,我们真的在谈论“每行每个日期都不同”吗?
  • 如果“每行每个日期都不同”:
    • 每个日期的时间是否可以遵循相同的非连续模式?还是每个日期的模式都不同?
    • 任何特定日期的行数是否会超过 43,200 行?如果是这样,时间只能是唯一的每组 43,200 行

鉴于以上信息,有几种方法可以解释请求:

  1. 强调“随机”:日期和行数无关紧要。使用其他答案中显示的三种方法之一生成真正随机的时间,这些时间很可能是唯一的,但不能保证
    • @notulysses:RAND(CAST(NEWID() AS VARBINARY)) * 43200
    • @史蒂夫福特:ABS(CHECKSUM(NewId()) % 43201)
    • @弗拉基米尔·巴拉诺夫:CAST(43200000 * (CAST(CRYPT_GEN_RANDOM(4) as int) / 4294967295.0 + 0.5) as int)
  2. 强调“每行不同”,始终 如果行数从不超过可用秒数,则很容易保证所有行的唯一时间,无论是相同还是不同的日期,并且似乎是随机排序的。
  3. 强调“每行不同”,可能 > 43,200 行:如果行数可以超过可用秒数,则无法保证所有的唯一性em> 行,但仍然可以保证任何特定日期的行之间的唯一性,前提是没有特定日期的行数超过 43,200 行。

因此,我的回答基于以下想法:

  • 即使 OP 的行数从不超过 2800,大多数其他遇到类似随机性需求的人更有可能使用更大的数据集(即很容易有 100 万行,对于任意数量的日期:1、5000 等)
  • 在对所有 5 行使用相同日期的情况下,样本数据过于简单化,或者即使在这种特殊情况下所有行的日期都相同,在大多数其他情况下这种情况不太可能发生
  • 独特性优于随机性
  • 如果每个日期的秒数“看似随机”排序存在模式,则至少应在各个日期(当日期按顺序排序时)的序列开头有一个不同的偏移量,以提供任何一小组日期之间出现随机性。

答案:

如果情况需要唯一的时间,那么任何生成真正随机值的方法都无法保证这一点。我真的很喜欢@Vladimir Baranov 对CRYPT_GEN_RANDOM 的使用,但要生成一组独特的值几乎是不可能的:

DECLARE @Table TABLE (Col1 BIGINT NOT NULL UNIQUE);

INSERT INTO @Table (Col1)
    SELECT CONVERT(BIGINT, CRYPT_GEN_RANDOM(4))
    FROM [master].sys.objects so
    CROSS JOIN [master].sys.objects so2
    CROSS JOIN [master].sys.objects so3;
    -- 753,571 rows

将随机值增加到 8 个字节似乎确实有效:

DECLARE @Table TABLE (Col1 BIGINT NOT NULL UNIQUE);

INSERT INTO @Table (Col1)
    SELECT CONVERT(BIGINT, CRYPT_GEN_RANDOM(8))
    FROM [master].sys.objects so
    CROSS JOIN [master].sys.objects so2
    CROSS JOIN [master].sys.objects so3;
    -- 753,571 rows

当然,如果我们生成到第二个,那么只有 86,400 个。缩小范围似乎会有所帮助,因为以下方法有时会起作用:

DECLARE @Table TABLE (Col1 BIGINT NOT NULL UNIQUE);

INSERT INTO @Table (Col1)
    SELECT TOP (86400) CONVERT(BIGINT, CRYPT_GEN_RANDOM(4))
    FROM [master].sys.objects so
    CROSS JOIN [master].sys.objects so2
    CROSS JOIN [master].sys.objects so3;

但是,如果每天需要唯一性,事情就会变得有点棘手(这似乎是这类项目的合理要求,而不是在所有日子里都是唯一的)。但是随机数生成器不会知道在每个新的一天重置。

如果仅具有随机外观是可以接受的,那么我们可以保证每个日期的唯一性,而无需:

  • 循环/游标结构
  • 在表格中保存已使用的值
  • 使用RAND()NEWID()CRYPT_GEN_RANDOM()

以下解决方案使用我在此答案中了解到的Modular Multiplicative Inverses (MMI) 的概念:generate seemingly random unique numeric ID in SQL Server。当然,这个问题没有像我们这里那样严格定义的值范围,每天只有 86,400 个值。因此,我使用了 86400 的范围(作为“模数”)并在 online calculator 中尝试了一些“互质”值(作为“整数”)来获取它们的 MMI:

  • 13 (MMI = 39877)
  • 37 (MMI = 51373)
  • 59 (MMI = 39539)

我在 CTE 中使用 ROW_NUMBER(),按 CREATED_DATE 分区(即分组)作为为一天中的每一秒分配一个值的方法。

但是,虽然按顺序为 0、1、2、... 等秒生成的值会显得随机,但在不同的日子里,该特定秒将映射到相同的值。因此,第二个 CTE(名为“WhichSecond”)通过将日期转换为 INT(将日期转换为从 1900 年 1 月 1 日开始的顺序偏移量)然后乘以 101 来移动每个日期的起点。

DECLARE @Data TABLE
(
  ID INT NOT NULL IDENTITY(1, 1),
  CREATED_DATE DATE NOT NULL
);

INSERT INTO @Data (CREATED_DATE) VALUES ('2014-10-05');
INSERT INTO @Data (CREATED_DATE) VALUES ('2014-10-05');
INSERT INTO @Data (CREATED_DATE) VALUES ('2014-10-05');
INSERT INTO @Data (CREATED_DATE) VALUES ('2014-10-05');
INSERT INTO @Data (CREATED_DATE) VALUES ('2014-10-05');
INSERT INTO @Data (CREATED_DATE) VALUES ('2015-03-15');
INSERT INTO @Data (CREATED_DATE) VALUES ('2016-10-22');
INSERT INTO @Data (CREATED_DATE) VALUES ('2015-03-15');

;WITH cte AS
(
  SELECT tmp.ID,
         CONVERT(DATETIME, tmp.CREATED_DATE) AS [CREATED_DATE],
         ROW_NUMBER() OVER (PARTITION BY tmp.CREATED_DATE ORDER BY (SELECT NULL))
                      AS [RowNum]
  FROM   @Data tmp
), WhichSecond AS
(
  SELECT cte.ID,
         cte.CREATED_DATE,
         ((CONVERT(INT, cte.[CREATED_DATE]) - 29219) * 101) + cte.[RowNum]
                      AS [ThisSecond]
  FROM   cte
)
SELECT parts.*,
       (parts.ThisSecond % 86400) AS [NormalizedSecond], -- wrap around to 0 when
                                                         -- value goes above 86,400
       ((parts.ThisSecond % 86400) * 39539) % 86400 AS [ActualSecond],
       DATEADD(
                 SECOND,
                 (((parts.ThisSecond % 86400) * 39539) % 86400),
                 parts.CREATED_DATE
              ) AS [DateWithUniqueTime]
FROM WhichSecond parts
ORDER BY parts.ID;

返回:

ID  CREATED_DATE  ThisSecond  NormalizedSecond  ActualSecond  DateWithUniqueTime
1   2014-10-05    1282297     72697             11483         2014-10-05 03:11:23.000
2   2014-10-05    1282298     72698             51022         2014-10-05 14:10:22.000
3   2014-10-05    1282299     72699              4161         2014-10-05 01:09:21.000
4   2014-10-05    1282300     72700             43700         2014-10-05 12:08:20.000
5   2014-10-05    1282301     72701             83239         2014-10-05 23:07:19.000
6   2015-03-15    1298558      2558             52762         2015-03-15 14:39:22.000
7   2016-10-22    1357845     61845             83055         2016-10-22 23:04:15.000
8   2015-03-15    1298559      2559              5901         2015-03-15 01:38:21.000

如果我们只想生成上午 8:00 到晚上 8:00 之间的时间,我们只需要做一些小的调整:

  1. 将范围(作为“模”)从 86400 更改为一半:43200
  2. 重新计算 MMI(可以使用与“整数”相同的“互质”值):39539(与之前相同)
  3. 28800 添加到DATEADD 的第二个参数作为8 小时偏移量

结果将是只更改一行(因为其他行是诊断性的):

-- second parameter of the DATEADD() call
28800 + (((parts.ThisSecond % 43200) * 39539) % 43200)

另一种以不太可预测的方式改变每一天的方法是通过在“WhichSecond”CTE 中传递CREATED_DATE 的INT 形式来使用RAND()。这将为每个日期提供一个稳定的偏移量,因为RAND(x) 将为传入的x 的相同值返回相同的值y,但将为传入的x 的不同值返回不同的值y in. 含义:

兰德(1) = y1
兰德(2) = y2
兰德(3) = y3
兰德(2) = y2

第二次调用 RAND(2) 时,它仍然返回与第一次调用时相同的 y2 值。

因此,“WhichSecond”CTE 可能是:

(
  SELECT cte.ID,
         cte.CREATED_DATE,
         (RAND(CONVERT(INT, cte.[CREATED_DATE])) * {some number}) + cte.[RowNum]
                      AS [ThisSecond]
  FROM   cte
)

【讨论】:

  • 我突然想到可以使用RAND(CHECKSUM(HASHBYTES('MD5', CAST(ROW_NUMBER() OVER(ORDER BY ...) AS varbinary(4))))) 之类的东西来生成一系列唯一随机数。我已将此方法添加到我对所有方法列表的回答中。主要思想是我们将种子提供给RAND 函数,我们控制这个种子是什么。如果提供的种子是唯一的,那么RAND 会将它们转换为唯一的随机数。 CRYPT_GEN_RANDOM 的行为可能类似,您不需要 CHECKSUM 即可将 HASHBYTES 的结果减少为 int。
  • @VladimirBaranov 有趣。这个想法有希望。请参阅我在您的答案上留下的注释,因为那似乎是一个更好的地方。我并没有消极,只是指出了两个问题。
  • 在阅读了您与@notulysses 的聊天内容后,我终于明白了您为什么要专门寻找唯一的随机数。我从来没有想过以这种方式解释原始问题。我在回答中补充说,我认为这类似于对一组(唯一)值进行洗牌。
  • 很好的答案。尽管我使用了不同的解决方案来解决我当时遇到的问题,但这是迄今为止我收到的最好的答案。任何阅读本文的人也应该查看@VladimirBaranov 的答案。
【解决方案4】:

有几种方法:

  • 预先生成一个包含随机数的表格,并在需要时使用它。或者从some reputable source 获取这些数据。
  • 使用NEWID 函数为RAND 提供种子的各种组合。应谨慎使用,因为不能保证 NEWID 值的分布。使其或多或少均匀分布的最佳方法之一是通过CHECKSUMRAND(CHECKSUM(NEWID()))。这种方法的好处是 NEWID 函数从 SQL Server 2000 开始可用。
  • 而不是NEWID 使用,例如,某个列的MD5 作为RAND 的种子:RAND(CHECKSUM(HASHBYTES('MD5', CAST(SomeID AS varbinary(4))))) 或简单的行号:RAND(CHECKSUM(HASHBYTES('MD5', CAST(ROW_NUMBER() OVER(ORDER BY ...) AS varbinary(4)))))。此方法至少从 SQL Server 2005 开始可用。与NEWID 方法的主要区别在于您可以完全控制随机序列。您无法控制NEWID 返回的内容,也无法再次从相同的数字重新启动随机序列。如果您使用PARTITION BY 提供相同的行号集,您将获得相同的随机数集。当您需要多次使用相同的随机数序列时,它可能很有用。有可能为两个不同的种子获得相同的随机数。我测试了从 1 到 1,000,000 的行号。 MD5 他们都是不同的。 CHECKSUMMD5 导致 122 次冲突。这个CHECKSUM 中的RAND 导致246 次冲突。当使用从 1 到 100,000 的行号进行测试时,CHECKSUM 有 1 次冲突,RAND 有 3 次冲突。
  • 另一种可能性是在 T-SQL 中简单地实现您自己的用户定义函数,该函数使用您喜欢的算法生成一个随机数。在这种情况下,您可以完全控制一切。通常伪随机生成器必须在两次调用之间存储其内部状态,因此您最终可能会拥有一个存储这些数据的专用表。
  • 您可以使用 CLR 编写用户定义的函数。在这种情况下,您可以实现自己的生成器,或使用 .NET 内置的函数,例如 Random classRNGCryptoServiceProvider class
  • 最后,从 SQL Server 2008 开始,有一个内置函数CRYPT_GEN_RANDOM

我会详细描述最后一种方法,因为我认为它对于SQL Server 2008 及以上版本是一个非常好的解决方案。 CRYPT_GEN_RANDOM 为结果集的每一行调用,而RAND 只调用一次。

CRYPT_GEN_RANDOM (Transact-SQL)

返回由 Crypto API 生成的加密随机数 (CAPI)。输出是指定数量的十六进制数 字节。

此外,CRYPT_GEN_RANDOM 应该提供比RAND 更好的随机值。在分布和加密强度方面更好。示例:

(CAST(CRYPT_GEN_RANDOM(4) as int) / 4294967295.0 + 0.5)

这会生成 4 个随机字节作为 varbinary。我们必须首先明确地将它们转换为int。然后将结果转换为 0 到 1 之间的浮点数。

所以,原来的查询是这样的:

SELECT ID AS [ID]
     , MyFunction.dbo.AddWorkDays(14, S.CREATED_DATE) AS [New Date]
     , CONVERT(VARCHAR, DATEADD(MILLISECOND, 
     CAST(43200000 * (CAST(CRYPT_GEN_RANDOM(4) as int) / 4294967295.0 + 0.5) as int),
     CONVERT(TIME, '08:00')), 114) AS [New Time]
FROM RandomTable

这是一个易于复制粘贴和尝试的独立示例(我使用了来自@Steve Ford 的另一个答案的查询):

SELECT DATEADD(millisecond, 
    CAST(43200000 * (CAST(CRYPT_GEN_RANDOM(4) as int) / 4294967295.0 + 0.5) as int), 
    CAST('08:00:00' AS Time)) AS [RandomTime]
FROM 
    ( VALUES (1), (2), (3), (4), (5)
    ) Y(A)
    CROSS JOIN
    ( VALUES (1), (2), (3), (4), (5)
    ) Z(A)

这是结果:

RandomTime
10:58:24.7200000
19:40:06.7220000
11:04:29.0530000
08:57:31.6130000
15:03:14.9470000
09:15:34.9380000
13:46:43.1250000
11:27:00.8940000
14:42:23.6100000
15:07:56.2120000
11:39:09.8830000
08:16:44.3960000
14:23:38.4820000
17:28:31.7440000
16:29:31.4320000
09:09:15.0210000
12:31:09.8370000
11:23:09.8430000
15:35:45.5480000
17:42:49.3390000
08:07:05.4930000
18:17:16.2980000
11:49:08.2010000
10:20:21.7620000
15:56:58.6110000

加法

当我阅读原始问题时,我认为没有必要确保所有生成的随机数都是唯一的。 我将问题中的“不同”一词解释为与使用简单的SELECT RAND() 时在结果的每一行中看到相同的数字相反。 我认为在许多情况下,碰撞随机数是否很少并不重要。在许多情况下,这实际上是正确的行为。

所以,我的理解是,当需要一个唯一随机数序列时,它在某种意义上相当于下面的任务。 我们有一组一些值/行,例如,一组唯一 ID 或一天中的所有 86400 秒或给定一天的 2800 行。 我们想打乱这些值/行。我们希望以随机顺序重新排列这些行。

要打乱给定的一组行,我们只需要ORDER BY 随机数(这些随机数在这里可能有合理数量的冲突)。随机数可以通过任何方法生成。像这样的:

ROW_NUMBER() OVER ([optional PARTITION BY ...] ORDER BY CRYPT_GEN_RANDOM(4)) 

或字面意思

SELECT ...
FROM ...
ORDER BY CRYPT_GEN_RANDOM(4)

取决于使用地点和方式。

【讨论】:

  • 关于您与HASHBYTES('MD5', CAST(ROW_NUMBER() OVER(ORDER BY ...) AS varbinary(4))) 相关的更新:如果按日期分区,它将每天重置,但它不是保证唯一的,因为在执行@ 时可能会发生冲突987654362@。这些冲突可能永远不会发生,因此在实际意义上它可能是唯一的,但理论上仍然可能不是唯一的,特别是如果给定日期有接近 86400 甚至 43200 个条目。此外,如果不使用随日期变化的偏移量,所有日期的相同行数将是相同的秒。
  • @srutzky 你是对的。我从答案中删除了关于随机数唯一的条款。我测试并确认确实发生了碰撞。因此,这种方法的要点是易于控制生成的序列。使用它可以很容易地生成几个相同的序列。
  • 当然真正的随机数不一定是唯一的,掷骰子6次!
【解决方案5】:

测试一下:

 Declare @t table(ID int,CREATED_DATE datetime)
insert into @t values
 (1 ,  '04/26/2014'),
 (2 ,  '04/26/2014'),
 (3 ,  '04/26/2014'),
 (4 ,  '04/26/2014')

 ;WITH CTE AS
 (
   SELECT *,CONVERT(VARCHAR, DATEADD(SECOND, RAND(CAST(NEWID() AS VARBINARY)) * 43200, 
   CAST('08:00:00' AS TIME)),114) AS [New Time] FROM @t WHERE ID=1
   UNION ALL
   SELECT *,CONVERT(VARCHAR, DATEADD(SECOND, RAND(CAST(NEWID() AS VARBINARY)) * 43200, 
   CAST('08:00:00' AS TIME)), 114)  FROM @t WHERE ID>1 AND ID<=5
 )
 SELECT * FROM CTE

【讨论】:

    【解决方案6】:

    这是另一个选项,可让您更好地控制时间的生成方式。 您可以指定随机时间之间的间隔。 它也没有使用RAND 函数。

    DECLARE @StartTime  VARCHAR(10) = '08:00',
            @EndTime    VARCHAR(10) = '20:00',
            @Interval   INT = 5 --(In Seconds)
    
    WITH times AS(
        SELECT CONVERT(TIME, @StartTime) AS t
        UNION ALL
        SELECT DATEADD(SECOND, @Interval, t)
        FROM times
        WHERE t < @EndTime
    )
    
    SELECT *, 
    (SELECT TOP 1 t FROM times WHERE d.Id > 0 ORDER BY NEWID())
    FROM #data d
    option (maxrecursion 0)
    

    附注:
    如果您删除上述子查询 (WHERE d.Id &gt; 0) 中的 WHERE 子句,则所有行都返回相同的时间值,即与您开始时遇到的相同问题

    【讨论】:

      【解决方案7】:

      全部,

      我想我会分享我的问题的答案。我不记得我在哪里找到了详细信息 - 我认为是通过 sgeddes 提供的链接之一。

      我使用以下查询来获取上午 8 点到晚上 7:55(大致)之间的随机时间

      SELECT convert(varchar,CONVERT(varchar, DATEADD(ms, dbo.MyRand(335 ,830) * 86400, 0), 114),114)
      

      MyRand 函数如下:

      SET ANSI_NULLS ON;
      GO
      SET QUOTED_IDENTIFIER ON;
      GO
      CREATE FUNCTION dbo.myRand(@Min INT, @Max INT) RETURNS decimal(18,15) AS
      BEGIN
      DECLARE @BinaryFloat BINARY(8)
      SELECT @BinaryFloat = CAST(Id AS BINARY) FROM vwGuid
      
      DECLARE
      @PartValue TINYINT,
      @Mask TINYINT,
      @Mantissa FLOAT,
      @Exponent SMALLINT,
      @Bit TINYINT,
      @Ln2 FLOAT,
      @BigValue BIGINT,
      @RandomNumber FLOAT
      
      SELECT
      @Mantissa = 1,
      @Bit = 1,
      @Ln2 = LOG(2),
      @BigValue = CAST(@BinaryFloat AS BIGINT),
      @Exponent = (@BigValue & 0x7ff0000000000000) / EXP(52 * @Ln2)
      
      WHILE @Part <= 8
      BEGIN
      SELECT
      @PartValue = CAST(SUBSTRING(@BinaryFloat, @Part, 1) AS TINYINT),
      @Mask =
      
      WHILE @Mask > 0
      BEGIN
      IF @PartValue & @Mask > 0
      SET @Mantissa = @Mantissa + EXP(-@Bit * @Ln2)
      
      SELECT
      @Mask = @Mask / 2
      END
      END
      
      SET @RandomNumber = CASE @Exponent WHEN 0 THEN 0 ELSE CAST(@Exponent AS FLOAT) / 2047 END
      
      RETURN CAST((@RandomNumber * (@Max - @Min)) + @Min AS DECIMAL(18,15))
      
      END
      GO
      END
      

      我希望这会有所帮助。我没有阅读上面的许多回复,如果有人有更好的答案,我深表歉意 - 这就是我解决它的方法。

      谢谢

      【讨论】:

      • 这个用户自定义函数中的vwGuidvwGuid.Id是什么?
      • @AMC :如果可能,请详细说明您的答案。无论如何,我认为让您与我一起选择赏金提议的答案可能是个好主意,因为这首先是您的问题。此外,在赏金到期后,请选择已接受的答案之一以通知人们,这会遇到相同的问题,即已解决。
      • @VladimirBaranov vwGuid 很可能是一个简单地执行SELECT NEWID() AS [Id]; 的视图,因为这是一种解决无法在 T-SQL 函数中调用 NEWID() 的限制的方法。
      • strutzky 是正确的。只需使用 CREATE VIEW dbo.vwGuid AS SELECT Id = NEWID()
      • @notulysses :我今晚会这样做。谢谢
      【解决方案8】:

      Get Random Time in a given range: Sql Server

      SELECT
          X.Value,
          RT.RandomTime,
          DateObject = CONVERT(SMALLDATETIME, CONVERT(DATE, GETDATE())) + CONVERT(SMALLDATETIME, RT.RandomTime)
       FROM (VALUES(101),(204),(77),(54),(75),(66)) X(Value)  /* YOUR TABLE */
       CROSS APPLY(SELECT FromTime = '08:20:00', ToTime = '08:33:00') FT
       CROSS APPLY(SELECT MaxSeconds = DATEDIFF(ss, FT.FromTime, FT.ToTime)) MS
       CROSS APPLY(SELECT RandomTime = CONVERT(TIME, DATEADD(SECOND, (MS.MaxSeconds + 1) * RAND(CONVERT(VARBINARY, NEWID() )) , FT.FromTime))) RT
      

      【讨论】:

        猜你喜欢
        • 2016-08-14
        • 2022-12-04
        • 2017-06-25
        • 1970-01-01
        • 2019-05-31
        • 1970-01-01
        • 2011-10-03
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多