【问题标题】:Generate Random Test Data with ORDER BY NEWID() , include duplicate rows使用 ORDER BY NEWID() 生成随机测试数据,包括重复行
【发布时间】:2019-05-22 07:25:45
【问题描述】:

我需要从表中选择随机行作为测试数据。有时我需要的测试数据行数可能多于表中的记录数。重复没问题。如何构建我的选择以便我可以获得重复的行?

CREATE TABLE [Northwind].[dbo].[Persons]
(PersonID int, LastName varchar(255))

INSERT INTO [Northwind].[dbo].[Persons] 
VALUES
(1, 'Smith'), 
(2, 'Jones'),
(3, 'Washington')

SELECT TOP 5 *
FROM [Northwind].[dbo].[Persons]  
ORDER BY NEWID()

如何让 Select 语句以随机顺序给我五个记录,并重复?目前只返回三个随机顺序。

我希望能够扩展它以获得 100 行或 1000 行或我需要的任意数量。

【问题讨论】:

  • 这里有几种解决方案。它们都有点奇怪,因为在关系数据世界中,您尝试做的事情相当奇怪。您可以使用另一个具有更多行的表并加入您的表,基本上创建一个笛卡尔。您可以使用交叉连接将表连接到自身。但无论你如何分割它,你最终可能会出现一些重复的行和一些根本没有返回的行。
  • 是否有要求只有在没有足够的行来管理没有重复的情况下才应该返回重复?
  • 行重复但有些行根本不返回也没关系。我希望能够扩展它以获得 100 行或 1000 行,或者我需要的任何数量。
  • 听起来像是 Tally Table 的工作,然后您可以 CROSS JOIN 处理您的数据以生成数百万行;然后只选择前 5 个...
  • 这里有几个答案。如果其中任何一个满足您的需求,请记住将其标记为已接受。

标签: sql sql-server tsql


【解决方案1】:

使用递归 CTE 合并足够多的行,使它们比您想要的更大。然后像以前一样从中选择。

declare
    @desired int = 5,
    @actual int = (select count(*) from persons);

with

    persons as (

        select    personId,
                  lastName,
                  batch = 0
        from      Persons

        union all
        select    personId,
                  lastName,
                  batch = batch + 1
        from      persons
        where     (batch + 1) * @actual < @desired

    )

    select    
    top (@desired) personId, lastName
    from           persons
    order by       newid()

【讨论】:

    【解决方案2】:

    如前所述。您可以改为使用统计表,然后获取随机行;

    WITH N AS(
        SELECT N
        FROM (VALUES(NULL),(NULL),(NULL),(NULL),(NULL),(NULL),(NULL),(NULL),(NULL),(NULL)))N(N)),
    Tally AS(
        SELECT ROW_NUMBER() OVER (ORDER BY (SELECT NULL)) AS I
        FROM N N1, N N2, N N3, N N4) --Repeat for more
    SELECT TOP 500 YT.*
    FROM Tally T
         CROSS JOIN YourTable YT
    ORDER BY NEWID();
    

    【讨论】:

      【解决方案3】:

      我在考虑如何在不对所有记录进行排序的情况下解决这个问题,尤其是多次。

      一种方法是生成随机数并使用这些随机数在数据中查找值:

      with n as (
            select rand(checksum(newid())) as r, 1 as n
            union all
            select rand(checksum(newid())) as r, n + 1
            from n
            where n < 10
           ),
           tt as (
            select t.*, lag(tile_end, 1, 0) over (order by tile_end) as tile_start
            from (select t.*, row_number() over (order by newid()) * 1.0 / count(*) over () as tile_end
                  from t
                 ) t
           )
      select tt.*, n.r, (select count(*) from n)
      from n left join
           tt
           on n.r >= tt.tile_start and n.r < tt.tile_end;
      

      Here 是一个 dbfiddle。 row_number() 不需要使用order by newid()。它可以通过具有索引的键进行排序——这使得该组件更加高效。

      对于超过 100 行,您将需要 OPTION (MAXRECURSION 0)

      【讨论】:

        【解决方案4】:

        我添加了一个临时结果表并循环查询并将结果推送到临时表中。

        declare  @results table(
        SSN varchar(10),
        Cusip   varchar(10),
        ...
        EndBillingDate  varchar(10))
        
        DECLARE @cnt INT = 0;
        
        WHILE @cnt < @trades
        BEGIN
        INSERT INTO @results 
           Select   ...
        
         set @cnt = @cnt + 10
        END
        
        select * from @results
        

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2013-11-09
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多