SQL采样:每个存储桶中的一个元素

时间:2016-10-16 16:27:27

标签: sql sql-server random

这是我所拥有的基本设置的模拟:每个人可以拥有多个财产 人员表:

id  name
1   Carl
2   Sam
3   Tom
4   Jack

财产表:

possession personId
car        2
shoes      2
shovel     2
tent       3
matches    3
axe        4

我想生成一组随机的属于一组人的财产,每人一次拥有

因此,在非SQL世界中,我会生成一组N个随机人员,然后为该组中的每个人选择一个随机拥有。但是我如何在SQL语义中实现它?

我想过随机抽取一些物品:

SELECT * FROM Posessions WHERE 0.01 >= RAND()

然后过滤掉重复的人,但这并不好,因为它最终有利于拥有大量财产的人,我希望每个人都有平等的机会被选中。

有解决这个问题的规范方法吗?

P.S。 Person包含~50000个实体,Possession包含~2500000个实体,但我只需要执行一次这样的采样,因此它可能有点慢。

3 个答案:

答案 0 :(得分:0)

为什么不随机抽取一组人并加入随机排名的分数。像下面的东西。很抱歉,如果它包含任何拼写错误,但我现在没有DB检查它:

    select * from (
    (select top 1 percent * from persons order by newid()) a
    inner join
    (select p.*, ROW_NUMBER() OVER (partition by personId order by newid()) r from posessions p) b
    on (a.personId = b.personId) 
)
    where r = 1;

答案 1 :(得分:0)

一种方式是(每人2人,每人1人)

DECLARE @PeopleCount INT = 2, 
        @PossessionsPerPersonCount INT = 1;

SELECT *
FROM (SELECT TOP (@PeopleCount) *
FROM Persons
ORDER BY CRYPT_GEN_RANDOM(4)) RandomPersons 
OUTER APPLY (SELECT TOP (@PossessionsPerPersonCount) * FROM Posessions p
             WHERE RandomPersons.id = p.personId
             ORDER BY CRYPT_GEN_RANDOM(4)) RandomPosessions

希望Possession在personId上有一个索引,以便它可以搜索每个人的相关行数(平均50),而不是扫描每个人的表格中的所有2,500,000。

我上面使用了OUTER APPLY,因为并非你示例数据中的所有人都拥有财产(即Carl没有)。

如果您只想包含拥有财产且想要每人拥有一个人的财产,那么您可以使用此权限。

DECLARE @PeopleCount INT = 2;

SELECT TOP (@PeopleCount) *
FROM Persons
CROSS APPLY (SELECT TOP (1) * FROM Posessions p
             WHERE Persons.id = p.personId
             ORDER BY CRYPT_GEN_RANDOM(4)) RandomPosessions
ORDER BY CRYPT_GEN_RANDOM(4);

答案 2 :(得分:0)

以下查询将为您生成3个随机样本


    SELECT p.id,
    (SELECT posession FROM posessions p1 where p1.id=p.id ORDER BY RAND() LIMIT 1) as posession
    FROM posessions p
    GROUP BY p.id
    ORDER BY RAND()
    LIMIT 3

子查询生成每个人的随机位置,而外部查询生成随机人。