在 SQL Server 上为每个类别随机选择 10% 的行

use*_*990 1 sql t-sql sql-server

有一张销售产品表。

行号 顾客 产品 售出日期
1 客户_1 东西 2023年1月1日
2 客户_12 何西何西 2023年1月3日
3 客户_1 手表 2023年1月4日
4 客户_4 何西何西 2023年1月6日
... ... ... ...

每一项总是一行。

假设customer_1总共订购了 100 件商品。customer_2总共订购了 50 件商品。customer_3总共订购了 17 件商品。如何为每个客户随机选择 10% 的行?所选行的分数应向上舍入(例如,所选的 2 行总共结果为 12 行)。这意味着至少购买一件商品的每位客户都应该出现在结果表中。在这种情况下customer_1, ,customer_2和的结果表customer_3将有 10 + 5 + 2 = 17 行。

我最初的方法是创建一个临时表,计算每个客户所需的行数,然后循环遍历临时表并为每个客户选择行。然后将它们插入到另一个表中并从该表中进行选择:

drop table if exists #row_counts

select
    customer
    ceiling(convert(decimal(10, 2), count(product)) / 10) as row_count
into #row_counts
from products_sold
group by customer

-- then use cursor to loop over #row_counts and insert into the final table
-- for randomness an 'order by newid()' will be used
Run Code Online (Sandbox Code Playgroud)

但这感觉不是正确的解决方案......

sig*_*nen 5

您需要知道您想要的总计数和行数。像这样的东西也许有用:

编辑由于它没有被正确随机化:


select *
from ( 
   select row_number() over(partition by customerid order by  newid()) as sortOrder
   , COUNT(*) OVER(PARTITION BY customerID) AS cnt
   , *
   FROM products
 ) p
-- Now, we want 10% of total count rounded upwards
WHERE sortOrder <= CEILING(cnt * 0.1)
Run Code Online (Sandbox Code Playgroud)

  • 这很好奇。快速的 [dbfiddle](https://dbfiddle.uk/7B9dR1NG) 似乎表明使用“order by (select newid())”会导致每次运行返回相同的结果。使用“order by newid()”返回不同的结果,即“随机”结果。两个查询的实际执行计划有所不同([dbfiddle](https://dbfiddle.uk/O_qN9qnl)),但我无法以有意义的方式解释差异。 (2认同)
  • 非常有趣@HABO。更有趣的是,如果您将 (SELECT NEWID()) 作为单独的列包含在内,它会随机化它!OP,你需要注意这一点 (2认同)
  • 抱歉@user15634990,我有点不清楚。我的意思是 (select newid()) 作为一个单独的列为选择的每一行生成新的 GUID 值。但它似乎在 ROW_NUMBER() OVER(ORDER BY (select newid())) 内不起作用,它不会随机化顺序。这对我来说有点可疑 (2认同)