SQL 服务器 - 与随机值的相关性?
SQL Server - Correlation with random values?
我的情况的简化示例:
我有一个包含三列的 table:ID
、CATEGORY
和 TIMESTAMP
。每个 ID
和 TIMESTAMP
都是唯一的,但 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
这解决了两个问题:
使用GROUP BY CATEGORY
选择伪随机行
保证JOIN
中的ID
关联回RandomID
对应的原始ID
。
我的情况的简化示例:
我有一个包含三列的 table:ID
、CATEGORY
和 TIMESTAMP
。每个 ID
和 TIMESTAMP
都是唯一的,但 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
这解决了两个问题:
使用
GROUP BY CATEGORY
选择伪随机行
保证
JOIN
中的ID
关联回RandomID
对应的原始ID
。