【问题标题】:SQL Sampling: one element from each bucketSQL 采样:每个存储桶中的一个元素
【发布时间】:2017-02-25 14:55:15
【问题描述】:

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

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

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

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

附: Person 包含约 50000 个实体,Possession 包含约 2500000 个实体,但我只需要执行一次此采样,因此可能会有些慢。

【问题讨论】:

    标签: sql sql-server random


    【解决方案1】:

    你为什么不随机选择一组人并加入随机排列的财产。像下面的东西。对不起,如果它包含任何拼写错误,但我现在没有数据库来检查它:

        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;
    

    【讨论】:

    • SQL Server 中的 RAND() 被评估为运行时常量,而不是每行。 0.01 >= RAND() 对所有行都为真,对所有行都为假。
    • @MartinSmith 谢谢。我已经编辑接受 orderby newid()
    【解决方案2】:

    一种方式是(以下 2 人,每人一件物品)

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

    【讨论】:

    【解决方案3】:

    以下查询将为您生成 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
    

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

    【讨论】:

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