从SQL Server表中选择n个随机行

从SQL Server表中选择n个随机行,sql,sql-server,random,Sql,Sql Server,Random,我有一个SQL Server表,其中大约有50000行。我想随机选择大约5000行。我想到了一种复杂的方法,创建一个带有“随机数”列的临时表,将我的表复制到该临时表中,在临时表中循环并使用RAND()更新每一行,然后从随机数列

我有一个SQL Server表,其中大约有50000行。我想随机选择大约5000行。我想到了一种复杂的方法,创建一个带有“随机数”列的临时表,将我的表复制到该临时表中,在临时表中循环并使用
RAND()
更新每一行,然后从随机数列<0.1的表中进行选择。我正在寻找一种更简单的方法,如果可能的话,用一句话

建议使用
NEWID()
函数。这看起来很有希望,但我看不出如何可靠地选择一定百分比的行

以前有人这样做过吗?有什么想法吗

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

这样做的代价是值的键扫描加上连接代价,在一个选择百分比很小的大表上,这应该是合理的。

在MySQL中,您可以这样做:

SELECT `PRIMARY_KEY`, rand() FROM table ORDER BY rand() LIMIT 5000;

只需按随机数对表格排序,然后使用
TOP
获得前5000行

SELECT TOP 5000 * FROM [Table] ORDER BY newid();
更新


只要尝试一下,一个
newid()
调用就足够了-不需要所有的强制转换和所有的数学运算。

根据您的需要,
TABLESAMPLE
将使您获得几乎相同的随机性和更好的性能。 这在MS SQL server 2005及更高版本上可用

TABLESAMPLE
将从随机页面而不是随机行返回数据,因此DEO甚至不会检索它不会返回的数据

我在一张很大的桌子上测试

select top 1 percent * from [tablename] order by newid()
花了20多分钟

select * from [tablename] tablesample(1 percent)
花了2分钟

TABLESAMPLE
中的较小样本上,性能也会提高,而在
newid()
中则不会

请记住,这不像
newid()
方法那样随机,但会给您一个像样的采样

请参阅。

newid()/orderby将起作用,但对于大型结果集来说成本非常高,因为它必须为每一行生成一个id,然后对它们进行排序

从性能的角度来看,TABLESAMPLE()是不错的,但您将得到成堆的结果(页面上的所有行都将返回)

为了获得性能更好的真实随机样本,最好的方法是随机筛选出行。我在SQL Server联机丛书文章中找到了以下代码示例:

如果你真的想要一个 在单个行中,将查询修改为 随机筛选出行,而不是 使用TABLESAMPLE。例如 下面的查询使用NEWID 函数返回大约一个 中的行的百分比 Sales.SalesOrderDetail表:

SELECT * FROM Sales.SalesOrderDetail
WHERE 0.01 >= CAST(CHECKSUM(NEWID(),SalesOrderID) & 0x7fffffff AS float)
              / CAST (0x7fffffff AS int)
SalesOrderID列包含在 校验和表达式,以便 NEWID()每行计算一次,以 实现每行采样。 表达式强制转换(校验和(NEWID(), SalesOrderID)&0x7fffffff作为浮点/ 强制转换(0x7FFFFF为int)的计算结果为 介于0和1之间的随机浮点值

当对一个有1000000行的表运行时,以下是我的结果:

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应该是最后的选择。

这对我很有用:

SELECT * FROM table_name
ORDER BY RANDOM()
LIMIT [number]
试试这个:

SELECT TOP 10 Field1, ..., FieldN
FROM Table1
ORDER BY NEWID()
OnMSDN有一个简单、清晰的解决方案,可以解决大规模性能问题

  SELECT * FROM Table1
  WHERE (ABS(CAST(
  (BINARY_CHECKSUM(*) *
  RAND()) as int)) % 100) < 10
从表1中选择*
其中(ABS(CAST(
(二进制校验和(*)*
RAND())作为int))%100)<10
如果您(与OP不同)需要特定数量的记录(这使得校验和方法很困难),并且希望获得比TABLESAMPLE本身提供的更随机的样本,并且还希望获得比校验和更好的速度,您可以合并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不接受变量)

我还没有看到答案中的这种变化。我有一个额外的约束,在给定初始种子的情况下,我需要每次选择相同的行集

对于MS SQL:

最起码的例子:

select top 10 percent *
from table_name
order by rand(checksum(*))
标准化执行时间:1.00

NewId()示例:

标准化执行时间:1.02

NewId()

使用初始种子进行选择:

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 */

如果需要在给定种子的情况下选择相同的集合,这似乎是可行的。

此链接对Orderby(NEWID())和其他用于1、7和13百万行表的方法进行了有趣的比较

通常,当在讨论组中询问有关如何选择随机行的问题时,会提出NEWID查询;它很简单,适用于小表

SELECT TOP 10 PERCENT *
  FROM Table1
  ORDER BY NEWID()
但是,当您将NEWID查询用于大型表时,它有一个很大的缺点。ORDER BY子句会导致表中的所有行都复制到tempdb数据库中,并在其中进行排序。这会导致两个问题:

  • 分拣操作通常具有较高的成本。 排序可以使用大量磁盘I/O,并且可以运行很长时间
  • 在最坏的情况下,tempdb可能会耗尽空间 最佳情况下,tempdb可能会占用大量磁盘空间 如果没有手动收缩命令,将永远无法回收
  • 您需要的是一种随机选择行的方法,该方法不会使用tempdb,也不会随着表的增大而变得更慢。下面介绍一种新的方法:

    SELECT * FROM Table1
      WHERE (ABS(CAST(
      (BINARY_CHECKSUM(*) *
      RAND()) as int)) % 100) < 10
    
    从表1中选择*
    其中(ABS(CAST(
    (二进制校验和(*)*
    RAND())作为int))%100)<10
    
    这个查询背后的基本思想是我们想要生成一个随机数
    SELECT * FROM Table1
      WHERE (ABS(CAST(
      (BINARY_CHECKSUM(*) *
      RAND()) as int)) % 100) < 10
    
    SELECT *
    FROM (
        SELECT *, ABS(CHECKSUM(NEWID())) AS Rnd
        FROM MyTable
    ) vw
    WHERE Rnd % 100 < 10        --10%
    
    SELECT TOP [number] 
    FROM table_name
    ORDER BY RAND(CHECKSUM(*) * RAND())
    
     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
    
    -- 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)
        )
    
    -- 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 ..