Generación de tiempo aleatorio distinto en el intervalo fijo


Estoy tratando de generar un tiempo aleatorio entre las 8:00 AM y 8:00 PM para cada fila que es seleccionado de un conjunto de datos, sin embargo, siempre me mismo valor aleatorio para cada fila – yo quiero que sea diferentes para cada fila.

Esquema de tabla & datos:

╔══════╦════════════════╗
║  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   ║
╚══════╩════════════════╝

Sentencia SQL actual:

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]

Resultados actuales ( el mismo tiempo para cada fila en el [New Time] columna):

╔══════╦════════════════╦════════════════╗
║  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    ║
╚══════╩════════════════╩════════════════╝

Resultados deseados ( diferente tiempo para cada fila en la columna [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    ║
╚══════╩════════════════╩════════════════╝

¿Alguna idea sobre cómo solucionar esto? Todo lo anterior es solo datos de muestra-mi tabla real tiene alrededor de 2800 registros (no estoy seguro de si eso hará una diferencia en las sugerencias de alguien).

Author: potashin, 2014-04-26

7 answers

Interpretación de la Pregunta Original:

La pregunta dice:

  • Generar un aleatorio tiempo entre las 8:00 AM y las 8:00 PM (es decir, una ventana de 12 horas)
  • Debe ser diferente para cada fila (es decir, único en todas las filas)
  • La tabla real tiene alrededor de 2800 registros

Ahora tenga en cuenta los siguientes puntos:

  • Los datos de muestra muestran solo una fecha
  • Hay 86.400 segundos en 24 horas, por lo tanto 43.200 segundos en 12 horas

Hay cierta ambigüedad en las siguientes áreas:

  • Qué es exactamente aleatorio dentro del contexto de "diferente para cada fila", dado que no se puede garantizar que los valores verdaderamente aleatorios sean diferentes para cada fila. De hecho, verdaderamente los números aleatorios podrían teóricamente ser el mismo para cada fila. Entonces, ¿es el énfasis en "aleatorio"o " diferente"? O estamos realmente hablando de diferente pero no secuencialmente ordenado (para dar la apariencia de aleatoriedad sin ser realmente aleatorio)?
  • ¿Qué pasa si hay más de 2800 filas? ¿Qué pasa si hay 1 millón de filas?
  • Si puede haber más de 43,200 filas, ¿cómo manejar "diferente para cada fila" (ya que no es posible tener único en todas las filas)?
  • ¿Variará alguna vez la fecha? Si es así, ¿realmente estamos hablando de " diferente para cada fila por fecha "?
  • Si "diferente para cada fila por fecha":
    • ¿Pueden los tiempos para cada fecha seguir el mismo patrón no secuencial? ¿O el patrón tiene que diferir por cada fecha?
    • ¿Habrá alguna vez más de 43,200 filas para cualquier fecha en particular? Si es así, los tiempos solo pueden ser únicos por cada conjunto de 43.200 filas.

Dada la información anterior, hay algunas maneras de interpretar la solicitud:

  1. Énfasis en "aleatorio": Fechas y número de las filas no importan. Generar tiempos verdaderamente aleatorios que son altamente probables, pero no garantizados , para ser únicos usando uno de los tres métodos mostrados en las otras respuestas:
    • @notulysses: RAND(CAST(NEWID() AS VARBINARY)) * 43200
    • @Steve Ford: ABS(CHECKSUM(NewId()) % 43201)
    • @Vladimir Baranov: CAST(43200000 * (CAST(CRYPT_GEN_RANDOM(4) as int) / 4294967295.0 + 0.5) as int)
  2. Énfasis en "diferente para cada fila", siempre Si el número de filas nunca excede el número de segundos disponibles, es fácil garantizar tiempos únicos en todos filas, independientemente de fechas iguales o diferentes, y parecen estar ordenadas aleatoriamente.
  3. El énfasis en "diferente para cada fila", podría ser > 43,200 filas: Si el número de filas puede exceder el número de segundos disponibles, entonces no es posible garantizar la unicidad a través de todas las filas, pero sería posible garantizar la unicidad a través de filas de cualquier fecha en particular, siempre que ninguna fecha en particular tenga > 43,200 filas.

Por lo tanto, basé mi respuesta sobre la idea de que:

  • Incluso si el número de filas para el O. P. nunca excede 2800, es más probable que la mayoría de los demás que están encontrando una necesidad similar de aleatoriedad tendrían un conjunto de datos más grande para trabajar (es decir, podría haber fácilmente 1 millón de filas, para cualquier número de fechas: 1, 5000, etc.)
  • O bien los datos de muestra son demasiado simplistas al usar la misma fecha para las 5 filas, o incluso si la fecha es la misma para todas las filas en este caso particular, en la mayoría de los otros casos eso es menos probable que suceda
  • La unicidad debe ser favorecida sobre la aleatoriedad
  • Si hay un patrón en el orden "aparentemente aleatorio" de los segundos para cada fecha, debe haber al menos un desplazamiento variable al inicio de la secuencia a través de las fechas (cuando las fechas se ordenan secuencialmente) para dar la apariencia de aleatoriedad entre cualquier pequeño grupo de fechas.

Respuesta:

Si la situación requiere tiempos únicos, eso no puede ser garantizado con cualquier método de generación de valores verdaderamente aleatorios. Me gusta mucho el uso de CRYPT_GEN_RANDOM por @ Vladimir Baranov, pero es casi imposible obtener un conjunto único de valores generados:

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

Aumentar el valor aleatorio a 8 bytes parece funcionar:

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

Por supuesto, si estamos generando hasta el segundo, entonces solo hay 86,400 de esos. Reducir el alcance parece ayudar, ya que lo siguiente funciona ocasionalmente:

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;

Sin embargo, las cosas se ponen un poco más complicado si la singularidad necesita por cada día (lo que parece un requisito razonable de este tipo de proyecto, en lugar de único en todos los días). Pero un generador de números aleatorios no va a saber reiniciar en cada nuevo día.

Si es aceptable simplemente tener la apariencia de ser aleatorio, entonces podemos garantizar la unicidad por cada fecha sin: {[30]]}

  • looping / cursor construye
  • guardar valores ya utilizados en una tabla
  • utilizando RAND(), NEWID(), o CRYPT_GEN_RANDOM()

La siguiente solución utiliza el concepto de Inversos Multiplicativos Modulares (MMI) que aprendí en esta respuesta: generar un ID numérico único aparentemente aleatorio en SQL Server . Por supuesto, esa pregunta no tenía un rango de valores estrictamente definido como el que tenemos aquí con solo 86,400 de ellos por día. Por lo tanto, utilicé un rango de 86400 (como "Módulo") y probé algunos valores "coprime" (como "Entero") en un en línea calculadora para obtener sus MMIs:

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

Utilizo ROW_NUMBER() en un CTE, particionado (es decir, agrupado) por CREATED_DATE como medio para asignar un valor a cada segundo del día.

Pero, mientras que los valores generados por segundos 0, 1, 2, ... y así sucesivamente aparecerá secuencialmente al azar, a través de diferentes días ese segundo particular se asignará al mismo valor. Por lo tanto, el segundo CTE (llamado " WhichSecond") cambia el punto de inicio para cada fecha convirtiendo la fecha a un INT (que convierte las fechas a un desplazamiento secuencial de 1900-01-01) y luego se multiplica por 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;

Devuelve:

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

Si solo queremos generar tiempos entre las 8: 00 AM y las 8: 00 PM, solo necesitamos hacer algunos ajustes menores:

  1. Cambie el rango (como" Módulo") de 86400 a la mitad de él: 43200
  2. Recalcular el MMI (puede usar los mismos valores "coprime" que "Integer"): 39539 (igual que antes)
  3. Agregue 28800 al segundo parámetro de DATEADD como un desplazamiento de 8 horas

El resultado será un cambio a una sola línea (ya que las otras son de diagnóstico):

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

Otro medio de cambiar cada día de una manera menos predecible sería hacer uso de RAND() pasando en la forma INT de CREATED_DATE en el CTE "WhichSecond". Esto daría un desplazamiento estable por cada fecha ya que RAND(x) devolverá el mismo valor y para el mismo valor de x pasado, pero devolverá un valor diferente y para un valor diferente de x pasado. Significado:

RAND (1) = y1
RAND (2) = y2
RAND (3) = y3
RAND (2) = y2

La segunda vez que se llamó RAND(2), todavía devolvió el mismo valor de y2 que devolvió la primera vez que se llamó.

Por lo tanto, el CTE" cual segundo " podría ser:

(
  SELECT cte.ID,
         cte.CREATED_DATE,
         (RAND(CONVERT(INT, cte.[CREATED_DATE])) * {some number}) + cte.[RowNum]
                      AS [ThisSecond]
  FROM   cte
)
 11
Author: Solomon Rutzky,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2017-05-23 12:17:05

El problema que tuvo OP mientras usaba solo rand() se debe a su evaluación una vez por consulta.

Desde el documentación:

Si no se especifica seed, el motor de base de datos SQL Server asigna un valor seed al azar. Para un valor especificado seed, el resultado devuelto es siempre el mismo.

Enfoque que se describe a continuación elimina la optimización y suprime este comportamiento, por lo que {[1] } se evalúa una vez por fila :

dateadd( second
       , rand(cast(newid() as varbinary)) * 43200
       , cast('08:00:00' as time) )
  • newid() genera valor único de tipo uniqueidentifier;
  • el valor se convierte con cast para ser utilizado como semilla en rand([seed]) función para generar un valor pseudo-aleatorio float desde 0 hasta 1, y como seed siempre es único, el valor devuelto también es único .

SQLFiddle

 24
Author: potashin,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2015-09-30 14:09:51

Puedes usar alternativamente:

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

El ABS(CHECKSUM(NewId()) % 43201) genera un número aleatorio entre 0 y 43200. Ver Discusión aquí.

SQL Fiddle

Configuración del esquema de MS SQL Server 2008 :

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

Resultados:

|    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 |
 15
Author: Steve Ford,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2017-05-23 11:53:14

Hay varios métodos:

  • Genere una tabla con números aleatorios por adelantado y utilícela cuando sea necesario. O tomar estos datos de alguna fuente de buena reputación .
  • Varias combinaciones que utilizan NEWID función para proporcionar una semilla para RAND. Debe usarse con precaución, porque no hay garantía sobre la distribución de los valores NEWID. Uno de los mejores métodos para hacerlo más o menos uniformemente distribuido es a través de la CHECKSUM: RAND(CHECKSUM(NEWID())). Lo bueno acerca de este método es que la función NEWID está disponible desde SQL Server 2000.
  • En lugar de NEWID use, digamos, MD5 de alguna columna como semilla para RAND: RAND(CHECKSUM(HASHBYTES('MD5', CAST(SomeID AS varbinary(4))))) O simplemente número de fila: RAND(CHECKSUM(HASHBYTES('MD5', CAST(ROW_NUMBER() OVER(ORDER BY ...) AS varbinary(4))))). Este método está disponible al menos desde SQL Server 2005. La principal diferencia con el método NEWID es que usted tiene control total sobre la secuencia aleatoria. No puede controlar lo que devuelve NEWID y no puede reiniciar la secuencia aleatoria desde el mismo número de nuevo. Si proporciona los mismos conjuntos de, digamos, números de fila usando PARTITION BY obtendrás los mismos conjuntos de números aleatorios. Puede ser útil en los casos en que necesite usar la misma secuencia de números aleatorios varias veces. Es posible obtener el mismo número aleatorio para dos semillas diferentes. Lo probé para números de fila del 1 al 1.000.000. MD5 de ellos son todos diferentes. CHECKSUM de MD5 resultan en 122 colisiones. RAND de esto CHECKSUM resultan en 246 colisiones. Cuando se probó con números de fila de 1 a 100,000 CHECKSUM tuvo 1 colisión, RAND tuvo 3 colisión.
  • Otra posibilidad es simplemente implementar su propia función definida por el usuario en T-SQL que genera un número aleatorio utilizando su algoritmo preferido. En este caso usted tiene el control total de todo. Por lo general, los generadores pseudo aleatorios tienen que almacenar su estado interno entre invocaciones, por lo que puede terminar con una tabla dedicada que almacena estos datos.
  • Puede escribir su función definida por el usuario usando CLR. En este caso puede implementar su propio generador, o utilice funciones integradas en. NET, como Random clase , o RNGCryptoServiceProvider clase .
  • Por fin, desde SQL Server 2008 hay una función incorporadaCRYPT_GEN_RANDOM.

Describiré el último método en detalle, porque creo que es una muy buena solución para SQL Server 2008 y superiores. CRYPT_GEN_RANDOM se llama para cada fila del conjunto de resultados, a diferencia de RAND, que se llama solo una vez.

CRYPT_GEN_RANDOM (Transact-SQL)

Devuelve un número aleatorio criptográfico generado por la API Crypto (CAPI). La salida es un número hexadecimal del número especificado de byte.

Además, CRYPT_GEN_RANDOM debería proporcionar valores aleatorios mucho mejores que RAND. Mejor en términos de distribución y cripto-fuerza. Ejemplo:

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

Esto genera 4 bytes aleatorios como varbinary. Tenemos que lanzarlos explícitamente a int primero. A continuación, el resultado se transforma en un número flotante entre 0 y 1.

So, a la consulta original le gustaría esto:

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

Aquí hay un ejemplo independiente que es fácil de copiar y pegar y probar (usé la consulta de otra respuesta de @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)

Este es el resultado:

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

Adición

Cuando leí la pregunta original no pensé que fuera realmente necesario asegurarse de que todos los números aleatorios generados fueran únicos. Interpreté la palabra "diferente" en la pregunta como un vago opuesto a ver el mismo número en cada fila del resultado que se ve cuando se utiliza un simple SELECT RAND(). Creo que en muchos casos no importa si hay pocos números aleatorios colisionando. En muchos casos, en realidad sería el comportamiento correcto.

Entonces, mi entendimiento es que cuando hay una necesidad de una secuencia de números aleatorios únicos, es en un sentido equivalente a la siguiente tarea. Tenemos un conjunto de algunos valores/filas, por ejemplo, un conjunto de identificadores únicos o todos los 86400 segundos de un día o 2800 filas para un determinado dia. Queremos barajar estos valores / filas. Queremos reorganizar estas filas en un orden aleatorio.

Para barajar el conjunto dado de filas simplemente necesitamos ORDER BY números aleatorios (estos números aleatorios pueden tener una cantidad razonable de colisiones aquí). Los números aleatorios pueden ser generados por cualquier método. Algo como esto:

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

O literalmente

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

Dependiendo de dónde y cómo se use.

 7
Author: Vladimir Baranov,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2015-01-16 04:24:48

Prueba esto:

 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
 3
Author: KumarHarsh,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2015-05-01 23:33:42

Aquí hay otra opción que le da un poco más de control sobre cómo se genera el tiempo. Puede especificar el intervalo entre los tiempos aleatorios. Tampoco hace uso de la función 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)

En una nota al margen:
Si elimina la cláusula WHERE en la subconsulta anterior (WHERE d.Id > 0), se devuelve el mismo valor de tiempo para todas las filas, es decir, el mismo problema que comenzó con

 2
Author: Spock,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2015-01-24 23:44:32

Todos,

Pensé en compartir la respuesta a mi pregunta. No puedo recordar exactamente dónde encontré los detalles, creo que fue a través de uno de los enlaces proporcionados por sgeddes.

Usé la siguiente consulta para obtener un tiempo aleatorio entre las 8 a. m. y las 7:55 p. m. (aproximadamente)

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

La función MyRand está debajo:

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

Espero que esto ayude. No he leído muchas de las respuestas anteriores, así que disculpas si alguien tiene una mejor respuesta , esto es simplemente cómo resolví se.

Gracias

 0
Author: AMC,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2015-12-01 11:55:12