我有一个SQL Server表,其中包含大约50,000行。我想随机选择大约5,000行。我想到了一个复杂的方法,创建一个带有“随机数”列的临时表,将我的表复制到其中,循环访问临时表并使用RAND()
更新每一行,然后从该表中选择随机数列< 0.1。我正在寻找一种更简单的方法,如果可能的话,在一个声明中。
This article建议使用NEWID()
功能。这看起来很有希望,但我看不出如何可靠地选择一定比例的行。
以前有人这样做过吗?有什么想法吗?
答案 0 :(得分:355)
select top 10 percent * from [yourtable] order by newid()
回应关于大表的“纯垃圾”评论:你可以这样做以提高性能。
select * from [yourtable] where [yourPk] in
(select top 10 percent [yourPk] from [yourtable] order by newid())
这是成本的关键扫描加上加入成本,在选择小百分比的大表上应该是合理的。
答案 1 :(得分:76)
根据您的需求,TABLESAMPLE
会让您几乎随机且性能更佳。
这在MS SQL Server 2005及更高版本上可用。
TABLESAMPLE
将从随机页面而不是随机行返回数据,因此deos甚至无法检索不会返回的数据。
在我测试过的非常大的桌子上
select top 1 percent * from [tablename] order by newid()
耗时超过20分钟。
select * from [tablename] tablesample(1 percent)
花了2分钟。
TABLESAMPLE
中的较小样本的效果也会提高,而newid()
则不会。
请注意,这不像newid()
方法那样随机,但会为您提供合适的样本。
请参阅MSDN page。
答案 2 :(得分:37)
newid()/ order by将起作用,但对于大型结果集来说非常昂贵,因为它必须为每一行生成一个id,然后对它们进行排序。
从性能的角度来看,TABLESAMPLE()是好的,但你会得到结果的结果(页面上的所有行都会被返回)。
对于性能更好的真随机样本,最好的方法是随机过滤掉行。我在SQL Server联机丛书文章 Limiting Results Sets by Using TABLESAMPLE 中找到了以下代码示例:
如果你真的想要随机抽样 单个行,修改您的查询 随机过滤掉行,而不是 使用TABLESAMPLE。例如, 以下查询使用NEWID 函数返回大约一个 行的百分比 Sales.SalesOrderDetail表:
SELECT * FROM Sales.SalesOrderDetail WHERE 0.01 >= CAST(CHECKSUM(NEWID(),SalesOrderID) & 0x7fffffff AS float) / CAST (0x7fffffff AS int)
SalesOrderID列包含在中 CHECKSUM表达式 NEWID()每行评估一次 实现每行采样。 表达式CAST(CHECKSUM(NEWID(), SalesOrderID)& 0x7fffffff AS float / CAST(0x7fffffff AS int)求值为 随机浮点值介于0和1之间。
对具有1,000,000行的表运行时,以下是我的结果:
SET STATISTICS TIME ON
SET STATISTICS IO ON
/* newid()
rows returned: 10000
logical reads: 3359
CPU time: 3312 ms
elapsed time = 3359 ms
*/
SELECT TOP 1 PERCENT Number
FROM Numbers
ORDER BY newid()
/* TABLESAMPLE
rows returned: 9269 (varies)
logical reads: 32
CPU time: 0 ms
elapsed time: 5 ms
*/
SELECT Number
FROM Numbers
TABLESAMPLE (1 PERCENT)
/* Filter
rows returned: 9994 (varies)
logical reads: 3359
CPU time: 641 ms
elapsed time: 627 ms
*/
SELECT Number
FROM Numbers
WHERE 0.01 >= CAST(CHECKSUM(NEWID(), Number) & 0x7fffffff AS float)
/ CAST (0x7fffffff AS int)
SET STATISTICS IO OFF
SET STATISTICS TIME OFF
如果您可以使用TABLESAMPLE,它将为您提供最佳性能。否则使用newid()/ filter方法。如果你有一个大的结果集,newid()/ order by应该是最后的选择。
答案 3 :(得分:21)
Selecting Rows Randomly from a Large Table有一个简单明了的解决方案,可以解决大规模的性能问题。
SELECT * FROM Table1
WHERE (ABS(CAST(
(BINARY_CHECKSUM(*) *
RAND()) as int)) % 100) < 10
答案 4 :(得分:9)
只需按随机数对表格进行排序,然后使用TOP
获取前5,000行。
SELECT TOP 5000 * FROM [Table] ORDER BY newid();
<强>更新强>
刚尝试过,newid()
来电就足够了 - 不需要所有演员和所有数学。
答案 5 :(得分:9)
如果您(与OP不同)需要特定数量的记录(这使得CHECKSUM方法难以实现)并且希望获得比TABLESAMPLE本身提供的更随机的样本,并且还希望比CHECKSUM更快的速度,您可以使用合并TABLESAMPLE和NEWID()方法,如下所示:
DECLARE @sampleCount int = 50
SET STATISTICS TIME ON
SELECT TOP (@sampleCount) *
FROM [yourtable] TABLESAMPLE(10 PERCENT)
ORDER BY NEWID()
SET STATISTICS TIME OFF
在我的情况下,这是随机性(它不是真的,我知道)和速度之间最直接的妥协。根据需要改变TABLESAMPLE百分比(或行) - 百分比越高,样本越随机,但预计速度会线性下降。 (注意,TABLESAMPLE不接受变量)
答案 6 :(得分:8)
这个链接在Orderby(NEWID())和其他方法(包含1,7和13百万行的表)之间进行了有趣的比较。
通常,当在讨论组中询问有关如何选择随机行的问题时,建议使用NEWID查询;它很简单,适用于小桌子。
SELECT TOP 10 PERCENT *
FROM Table1
ORDER BY NEWID()
但是,当您将NEWID查询用于大型表时,它有一个很大的缺点。 ORDER BY子句将表中的所有行复制到tempdb数据库中,并对它们进行排序。这导致两个问题:
您需要的是一种随机选择不使用tempdb的行的方法,并且随着表变大而不会慢得多。以下是关于如何做到这一点的新想法:
SELECT * FROM Table1
WHERE (ABS(CAST(
(BINARY_CHECKSUM(*) *
RAND()) as int)) % 100) < 10
此查询背后的基本思想是,我们要为表中的每一行生成0到99之间的随机数,然后选择随机数小于指定百分比值的所有行。在这个例子中,我们希望随机选择大约10%的行;因此,我们选择随机数小于10的所有行。
请阅读MSDN中的完整文章。
答案 7 :(得分:4)
在MySQL中你可以这样做:
SELECT `PRIMARY_KEY`, rand() FROM table ORDER BY rand() LIMIT 5000;
答案 8 :(得分:4)
这是初始种子构思和校验和的组合,它让我在没有NEWID()成本的情况下给出正确的随机结果:
SELECT TOP [number]
FROM table_name
ORDER BY RAND(CHECKSUM(*) * RAND())
答案 9 :(得分:2)
尚未在答案中看到这种变化。在给定初始种子时,我需要一个额外的约束,每次都选择相同的行集。
对于MS SQL:
最小例子:
select top 10 percent *
from table_name
order by rand(checksum(*))
标准化执行时间:1.00
NewId()示例:
select top 10 percent *
from table_name
order by newid()
标准化执行时间:1.02
NewId()
明显慢于rand(checksum(*))
,因此您可能不希望将其用于大型记录集。
选择初始种子:
declare @seed int
set @seed = Year(getdate()) * month(getdate()) /* any other initial seed here */
select top 10 percent *
from table_name
order by rand(checksum(*) % @seed) /* any other math function here */
如果您需要选择给定种子的同一组,这似乎有效。
答案 10 :(得分:2)
试试这个:
SELECT TOP 10 Field1, ..., FieldN
FROM Table1
ORDER BY NEWID()
答案 11 :(得分:0)
看来newid()不能在where子句中使用,所以这个解决方案需要一个内部查询:
SELECT *
FROM (
SELECT *, ABS(CHECKSUM(NEWID())) AS Rnd
FROM MyTable
) vw
WHERE Rnd % 100 < 10 --10%
答案 12 :(得分:0)
我在子查询中使用它,它在子查询中返回了相同的行
SELECT ID ,
( SELECT TOP 1
ImageURL
FROM SubTable
ORDER BY NEWID()
) AS ImageURL,
GETUTCDATE() ,
1
FROM Mytable
然后我解决了包含父表变量的地方
SELECT ID ,
( SELECT TOP 1
ImageURL
FROM SubTable
Where Mytable.ID>0
ORDER BY NEWID()
) AS ImageURL,
GETUTCDATE() ,
1
FROM Mytable
请注意condtition的位置
答案 13 :(得分:0)
没有指定正在使用的服务器端处理语言(例如PHP,.net等),但如果是PHP,请抓取所需的数字(或所有记录),而不是随机化在查询中使用PHP的shuffle函数。我不知道.net是否具有相同的功能但如果确实如此,那么如果您使用.net
ORDER BY RAND()可能会有相当大的性能损失,具体取决于所涉及的记录数量。
答案 14 :(得分:0)
从表中选择* 在( 从表中选择ID 按random()排序 限制((从表格中选择count(*))* 55/100))
//随机选择55%的行
答案 15 :(得分:0)
这是一种更新和改进的抽样形式。它基于使用 CHECKSUM
/ BINARY_CHECKSUM
和模数的其他一些答案的相同概念。
使用与此类似的实现而不是其他答案的原因:
CHECKSUM(*)
/ BINARY_CHECKSUM(*)
问题。使用 CHECKSUM(*)
方法时,可以在“块”而不是“随机”!这是因为CHECKSUM 更喜欢速度而不是分发。NEWID()
的方法(例如 CHECKSUM(NEWID()) % 100
)永远不会稳定/可重复。CHECKSUM
仅返回 int
值。ORDER BY NEWID()
,因为排序可能成为大型输入集的重大瓶颈。避免排序还可以减少内存和 tempdb 的使用.TABLESAMPLE
,因此与 WHERE
预过滤器一起使用。缺点/限制:
CHECKSUM(*)
。使用哈希字节,如下所示,每百万行增加大约 3/4 秒的开销。这是我的数据,在我的数据库实例上:YMMV。 如果使用来自 HASHBYTES 的结果“良好分布”bigint
值的持久计算列,则可以消除这种开销。SELECT TOP n .. ORDER BY NEWID()
不同,不能保证返回“正好 N”行。相反,它返回一个 percentage 行,其中预先确定了这样的值。对于非常小的样本量,这可能会导致选择 0 行。此限制与 CHECKSUM(*)
方法相同。这里是要点:
-- Allow a sampling precision [0, 100.0000].
declare @sample_percent decimal(7, 4) = 12.3456
select
t.*
from t
where 1=1
and t.Name = 'Mr. No Questionable Checksum Usages'
and ( -- sample
@sample_percent = 100
or abs(
-- Choose appropriate identity column(s) for hashbytes input.
-- For demonstration it is assumed to be a UNIQUEIDENTIFIER rowguid column.
convert(bigint, hashbytes('SHA1', convert(varbinary(32), t.rowguid)))
) % (1000 * 100) < (1000 * @sample_percent)
)
注意事项:
bigint
的转换至关重要,因为它允许 2^63 位的“随机空间”应用模数运算符;这远远超过 CHECKSUM 结果的 2^31 范围。这降低了模数误差的极限,尤其是随着精度的提高。1000 *
用于说明 @sample_percent
中允许的 4 位精度。bigint
值乘以 RAND()
以在每次运行时返回不同的行样本。这有效地改变了固定哈希值的排列。@sample_percent
为 100,查询规划器可以完全消除较慢的计算代码。记住“参数嗅探”规则。无论是否启用采样,这都允许将代码保留在查询中。计算具有下限/上限的 @sample_percent
,并在查询中添加 TOP
“提示”,因为当样本用于派生表时可能很有用上下文。
-- Approximate max-sample and min-sample ranges.
-- The minimum sample percent should be non-zero within the precision.
declare @max_sample_size int = 3333333
declare @min_sample_percent decimal(7,4) = 0.3333
declare @sample_percent decimal(7,4) -- [0, 100.0000]
declare @sample_size int
-- Get initial count for determining sample percentages.
-- Remember to match the filter conditions with the usage site!
declare @rows int
select @rows = count(1)
from t
where 1=1
and t.Name = 'Mr. No Questionable Checksum Usages'
-- Calculate sample percent and back-calculate actual sample size.
if @rows <= @max_sample_size begin
set @sample_percent = 100
end else begin
set @sample_percent = convert(float, 100) * @max_sample_size / @rows
if @sample_percent < @min_sample_percent
set @sample_percent = @min_sample_percent
end
set @sample_size = ceiling(@rows * @sample_percent / 100)
select *
from ..
join (
-- Not a precise value: if limiting exactly at, can introduce more bias.
-- Using 'option optimize for' avoids this while requiring dynamic SQL.
select top (@sample_size + convert(int, @sample_percent + 5))
from t
where 1=1
and t.Name = 'Mr. No Questionable Checksum Usages'
and ( -- sample
@sample_percent = 100
or abs(
convert(bigint, hashbytes('SHA1', convert(varbinary(32), t.rowguid)))
) % (1000 * 100) < (1000 * @sample_percent)
)
) sampled
on ..
答案 16 :(得分:-2)
这对我有用:
SELECT * FROM table_name
ORDER BY RANDOM()
LIMIT [number]