SQL Server - 与随机值的相关性?
Posted
技术标签:
【中文标题】SQL Server - 与随机值的相关性?【英文标题】:SQL Server - Correlation with random values? 【发布时间】:2019-10-21 15:11:33 【问题描述】:我的情况的简化示例:
我有一个包含三列的表:ID
、CATEGORY
和 TIMESTAMP
。每个ID
和TIMESTAMP
都是唯一的,但CATEGORY
不是唯一的。
我进行此查询以返回表中行的伪随机列表(每个 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
。如果发生这种情况,那么查询将返回不准确的结果,因为这些表是基于RandomID
的JOIN
ed。
这是一个有效的担忧吗?如果有,如何克服?
附:一些上下文:
在我的例子中,这个查询的结果将用于每月保留或清除一些记录和文件,因此查询结果的准确性非常重要。
需要明确的是,选择哪些行对我来说并不重要,只要保证每个CATEGORY
都有一个行,有一个条件:我想要选择的行根据TIMESTAMP
“大致”均匀分布。这就是为什么我要关联来自随机值而不是来自TIMESTAMP
的数据。 (例如,通过MIN(TIMESTAMP)
关联会导致月初的行密度更高。)考虑到我每月有数千个类别,伪随机选择行通常会导致均匀分布TIMESTAMPS
(即我的目标)。
另外一点:我希望查询是可重复的(即我希望它始终选择相同的伪随机值。)因此,涉及诸如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()
的每次调用都会在每一行上产生相同的值。样本数据和期望的结果会有所帮助。
@GordonLinoff RAND()
没有参数返回相同的结果到每一行,是的,但我传递一个唯一的 ID
作为参数,它返回不同的结果到每一行(或“几乎当然不同”——这就是问题的原因)。我还乘以一个大数,然后只保留小数部分以“进一步随机化”选定的随机数。
如果是日期时间,可以根据最小微秒来选择。这可能会也可能不会产生足够好的分布,具体取决于数据输入中的任何特质。
@avery_larry 我的数据没有毫秒(或微秒)分量。但即使是这样,我仍然会遇到同样的问题。如果您仅基于时间戳的微秒分量进行关联,则无法保证结果准确无误,因为两行(或更多)行的时间戳可能具有相同的微秒分量。
那你可以加倍吗?找到具有最小秒数和最小 ID 的单行?除非 ID 与时间戳顺序相关。使用newid()
而不是rand()
怎么样?它被认为是足够不可复制的。
【参考方案1】:
这种方法可能会导致性能问题。
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
【讨论】:
其实我刚刚意识到,newid()
行不通,因为我希望这个结果是可重复的。因为newid()
每次被调用时都会得到一些新的东西,所以这是行不通的。不过,很好的答案。 +1
答案中的第二个查询是可重复的。
@ImaginaryHuman072889 我在第二个查询中将 ID 添加到 order by。这应该是可重复的,包括在 RAND 代码中发生冲突的可能性非常小。请注意,当然,表中的新数据会改变结果。
是的,当然理解,如果数据发生变化,结果也会发生变化。 RAND 代码中发生冲突的可能性很小,但仍然是一个可能的问题。
我所有的查询都消除了冲突。更新后的第二个查询也以一致可重复的方式消除了冲突。【参考方案2】:
这不是您问题的答案。只是你的方法似乎没有希望。
SQL Server 中的rand()
对顺序 ID 不是特别好。考虑这段代码:
select id, floor(RAND(ID)*100000)
from (values (1), (2), (3), (4), (5), (6), (7), (8), (9)) v(id);
返回:
id (No column name)
1 71359
2 71361
3 71362
4 71364
5 71366
6 71368
7 71370
8 71372
9 71374
(Here 是一个 dbfiddle。)
这些并不完全是重复的。但它们也不是大多数人的“随机”版本。我问你真正想做的是什么,因为你的问题可能有解决方案。但是,您的问题并没有清楚地解释代码的用途。
【讨论】:
请仔细看看我的问题。我没有选择FLOOR(RAND(ID)*100000)
。我选择(RAND(ID)*100000)-FLOOR(RAND(ID)*100000)
。就像您指出的那样,RAND
输入的微小变化实际上并不会改变输出。这就是为什么我乘以一个大数并只保留小数部分,以“进一步随机化”它。【参考方案3】:
(回答我自己的问题)
几个小时后,我想出了一个有点奇怪的解决方案,但它解决了问题中列出的问题。
解决方法是将随机生成的数字与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
相关。
【讨论】:
以上是关于SQL Server - 与随机值的相关性?的主要内容,如果未能解决你的问题,请参考以下文章
SQL Server ->> 与SQL Server服务配置相关的DMV
PowerShell分析SQL Server待创建索引的字段与已有索引之间的相关性
sql server 2008 在与 SQL Server 提示建立连接时出现与网络相关的或特定于实例的错误
PHP sqlsrv_connect 到 SQL Server:建立与 SQL Server 的连接时发生与网络相关或特定于实例的错误