如何随机从表中选择唯一的行对?

时间:2011-01-27 23:32:06

标签: sql mysql

我有两张这样的表:

CREATE TABLE people (
    id INT NOT NULL,
    PRIMARY KEY (id)
)

CREATE TABLE pairs (
    person_a_id INT,
    person_b_id INT,
    FOREIGN KEY (person_a_id) REFERENCES people(id),
    FOREIGN KEY (person_b_id) REFERENCES people(id) 
)

我想从人员表中随机选择一对人,在选择它们之后,我将随机选择对添加到配对表中。 person_a_id始终指的是该对id较低的人(因为该对的顺序不相关)。

问题是我从不想两次选择同一对,所以在返回随机选择的对之前我需要检查对表。

是否可以以合理有效和优雅的方式使用单个SQL查询来执行此操作?

(我正在使用Java Persistence API执行此操作,但希望我能够将任何答案翻译成JPA代码)

2 个答案:

答案 0 :(得分:4)

select a.id, b.id
from people1 a
inner join people1 b on a.id < b.id
where not exists (
    select *
    from pairs1 c
    where c.person_a_id = a.id
      and c.person_b_id = b.id)
order by a.id * rand()
limit 1;
如果你一次“抽签”,

Limit 1只返回一对。否则,将你需要的数量增加到极限。

以上查询假定您可以

1 - 2
2 - 7

并且配对2 - 7有效,因为它不存在,即使2再次出现。如果你只希望一个人在only one对中出现,那么

select a.id, b.id
from people1 a
inner join people1 b on a.id < b.id
where not exists (
    select *
    from pairs1 c
    where c.person_a_id in (a.id, b.id))
  and not exists (
    select *
    from pairs1 c
    where c.person_b_id in (a.id, b.id))
order by a.id * rand()
limit 1;

如果要在一个查询中生成multiple pairs AND 目标表仍为空,则可以使用此单个查询。请注意LIMIT 6仅返回3对。

select min(a) a, min(b) b
from
(
    select
      case when mod(@p,2) = 1 then id end a,
      case when mod(@p,2) = 0 then id end b,
      @p:=@p+1 grp
    from (
        select id
        from (select @p:=1) p, people1
        order by rand()
        limit 6
    ) x
) y
group by floor(grp/2)

答案 1 :(得分:1)

这不能在基于单查询集的方法中完成,因为您的集合不知道在对表中插入了哪些对。

相反,你应该循环

WHILE EXISTS(SELECT * FROM people 
    WHERE id NOT IN (SELECT person_a_id FROM pairs) 
    AND id NOT IN (SELECT person_b_id FROM pairs) 

当有无与伦比的人时,这将循环。 那么你应该从1到该表的CNT(*)两个随机数 它为您提供了无与伦比的人数...如果您获得两次相同的数字,请再次滚动。 (如果你担心这个问题,可以从集合的两半中随机化数字......但是你会根据你的排序标准丢失一些随机性)

配对那些人。

洗涤,冲洗,重复...... 你唯一的“重做”将是你生成相同的随机数两次...更有可能因为你得到的人很少,但最多只有25%的几率(远远超过1 / n ^ 2)