SQL 服务器 - 与随机值的相关性?

SQL Server - Correlation with random values?

我的情况的简化示例:

我有一个包含三列的 table:IDCATEGORYTIMESTAMP。每个 IDTIMESTAMP 都是唯一的,但 CATEGORY 不是唯一的。

我对 return table 中的一个伪随机行列表进行了查询(每个 CATEGORY 一个)。

SELECT b.*
FROM
(
  SELECT MIN(RAND(ID)*100000-FLOOR(RAND(ID)*100000)) [RandomID] -- Select random identifier for each row
  FROM MYTABLE
  GROUP BY CATEGORY
) a
INNER JOIN
MYTABLE b
ON a.RandomID = (RAND(b.ID)*100000-FLOOR(RAND(b.ID)*100000))

它似乎工作正常,但我担心的是两个(或更多)不同的 ID 可能对应于相同的 RandomID。如果发生这种情况,那么查询将 return 不准确的结果,因为 table 是基于 RandomID.

编辑的 JOIN

这是一个有效的问题吗?如果有,如何克服?


P.S。一些上下文:

在我的例子中,这个查询的结果将用于每月保留或清除一些记录和文件,所以查询结果的准确性非常重要。

需要说明的是, 行 select 对我来说并不重要,只要保证每一行 [=15] =],有一个条件:我希望selected行"roughly"基于TIMESTAMP均匀分布。这就是为什么我将数据与随机值而不是 TIMESTAMP 相关联的原因。 (例如,通过 MIN(TIMESTAMP) 关联会导致月初的行密度更高。)考虑到我每个月有数千个类别,伪随机选择行通常会导致均匀分布 TIMESTAMPS(这是我的目标)。

请注意:我希望查询是 repeatable(即我希望它始终 select 相同的伪随机值。 ) 因此,涉及诸如 newid() 之类的解决方案是不够的。


应要求,这里是示例数据。

TIMESTAMP       | ID | CATEGORY
-------------------------------
10/21/19 1:00AM | 1  | A
10/21/19 2:00AM | 2  | B
10/21/19 3:00AM | 3  | A
10/21/19 4:00AM | 4  | B
10/21/19 5:00AM | 5  | A
10/21/19 6:00AM | 6  | B

一个可能的输出(完全取决于RAND()的选择)是:

TIMESTAMP       | ID | CATEGORY
-------------------------------
10/21/19 3:00AM | 3  | A
10/21/19 6:00AM | 6  | B

选择哪几行并不特别重要,只要每个类别都有一个行即可。同样,我不想基于 TIMESTAMP 进行关联,因为这保证我会选择示例数据中的前两行,但我希望 TIMESTAMPS 大致均匀分布。

这不是您问题的答案。只是你的方法看起来不太靠谱。

rand() in SQL 服务器对于顺序 ID 不是特别好。考虑这段代码:

 select id, floor(RAND(ID)*100000)
 from (values (1), (2), (3), (4), (5), (6), (7), (8), (9)) v(id);

它returns:

id  (No column name)
1   71359
2   71361
3   71362
4   71364
5   71366
6   71368
7   71370
8   71372
9   71374

(Here 是一个 db<>fiddle。)

这些不是完全重复的。但它们也不是大多数人的 "random" 版本。我问你真正想做什么,因为你的问题可能有解决方案。但是,您的问题并没有清楚地解释代码的用途。

性能可能是这个方法的问题。

declare @mytable table (timestamp datetime, ID int, category varchar(150))

insert into @mytable
values ('10/21/19 1:00AM', 1, 'A'),
    ('10/21/19 2:00AM', 2, 'B'),
    ('10/21/19 3:00AM', 3, 'A'),
    ('10/21/19 4:00AM', 4, 'B'),
    ('10/21/19 5:00AM', 5, 'A'),
    ('10/21/19 6:00AM', 6, 'A'),
    ('10/21/19 7:00AM', 7, 'A'),
    ('10/21/19 8:00AM', 8, 'A'),
    ('10/21/19 9:00AM', 9, 'A'),
    ('10/21/19 10:00AM', 10, 'A'),
    ('10/21/19 11:00AM', 11, 'A'),
    ('10/21/19 12:00AM', 12, 'A'),
    ('10/21/19 1:00PM', 13, 'A'),
    ('10/21/19 2:00PM', 14, 'A'),
    ('10/21/19 3:00PM', 15, 'A'),
    ('10/21/19 4:00PM', 16, 'A'),
    ('10/21/19 5:00PM', 17, 'A'),
    ('10/21/19 6:00PM', 18, 'A'),
    ('10/21/19 7:00PM', 19, 'A'),
    ('10/21/19 8:00PM', 20, 'A'),
    ('10/21/19 6:00PM', 21, 'B')

select timestamp, id, category
from (
   select *, row_number() over (partition by category order by newid()) rown
   from @mytable
) a
where rown=1

我想你也可以使用你的随机代码。我不知道这两种方法在分发方面的比较。 编辑 我在订单中添加了 ID。这使得结果即使在随机代码发生冲突的(非常)不可能的事件中也可以重复。

...
select timestamp, id, category
from (
   select *, row_number() over (partition by category order by RAND(ID)*100000-FLOOR(RAND(ID)*100000),ID) rown
   from @mytable
) a
where rown=1

(回答我自己的问题)

几个小时后,我想出了一个有点奇怪的解决方案,但它解决了问题中列出的问题。

解决方法是将随机生成的数字和ID拼接起来,然后在聚合函数发生后,将字符串中包含随机数的部分剥离出来,以取回原始ID,即

SELECT b.*
FROM
(
  SELECT
  MIN(
    RIGHT(
     CAST(
      CAST(
       RAND(ID)                   -- 1. Get pseudo-random number   (e.g. 0.01234)
      AS decimal(10,10))          -- 2. Get 10 decimal places      (e.g. 0.0123456789)
     AS varchar(20)),             -- 3. Cast it to varchar         (e.g. '0.0123456789')
    4)                            -- 4. Get only the last 4 digits (e.g. '6789')
   + '_' + CAST(ID as varchar(3)) -- 5. Append underscore and ID   (e.g. '6789_1')
  ) [RandomID]
  FROM MYTABLE
  GROUP BY CATEGORY
) a
INNER JOIN
MYTABLE b ON b.ID = 
CAST(SUBSTRING(a.RandomID,6,100) as int) -- Strip away first 5 chars to get ID back

这解决了两个问题:

  1. 使用GROUP BY CATEGORY

  2. 选择伪随机行
  3. 保证JOIN中的ID关联回RandomID对应的原始ID