从MySQL DB中获取5个随机行

时间:2014-06-05 12:45:55

标签: php mysql random weighted

我已经搜遍了所有的答案,尽管人们说不使用ORDER BY RAND()条款,但我认为对于我的目的来说这是好的,因为这是一场几乎没有超过几百条记录的比赛PER时间比赛。

所以基本上我需要从竞赛条目表中检索5个随机记录。但是,任何忠诚度客户都会收到额外的EXTRA条目,例如:

compEntryid | firstName | lastName | compID |
1           |  bob      |  smith   | 100
2           |  bob      | smith    | 100
3           |  jane     | doe      | 100
4           |  sam      | citizen  | 100

因此,我们为忠诚会员提供了获奖的更好机会。但是我有点担心通常的ORDER BY RAND()返回的结果可能包括同一个人的2个条目?什么是优化方法,以确保我们真正拥有5个随机记录,但同时为这些额外的进入者提供更好或(加权)的机会?很高兴使用多个查询,子查询甚至是MySQL和PHP的混合?非常感谢任何建议,谢谢!

低音

编辑:

这两个查询都有效!

QUERY1

  SELECT concat(firstName, " ", lastName) name,id, email 
    FROM t WHERE 
      RAND()<(SELECT ((5/COUNT(id))*10) FROM t) 
      group by email ORDER BY RAND()  limit 5;

QUERY2

 select distinct 
    email, id, firstName, lastName from 
    (
    select id ,
    email, firstName , lastName , compID, rand()/(select count(*) from t where 
                                             email=t1.email 
                                             ) as rank
    from t t1 
    where compID = 100 
    order by rank) t2 limit 5;

http://sqlfiddle.com/#!2/73470c/2

2 个答案:

答案 0 :(得分:1)

如果你有几百条记录,我认为rand()解决方案的顺序应该没问题: 子查询将命令加权条目数,但重复项仍然存在。父SELECT将采用前5个不同的行。

SELECT DISTINCT firstName , 
                lastName , 
                compID 
FROM
( SELECT compEntryid ,firstName , lastName , compID, rand()/(select count(*) 
  FROM   t 
  WHERE  firstName=t1.firstName AND
         lastName = t1.lastName) AS rank
  FROM   t t1 
  WHERE  compID = 100 
  ORDER BY rank) t2 
LIMIT 5

Fiddle

答案 1 :(得分:0)

如果你想返回一个compEntryid,我想你需要使用一个子查询。

SELECT t.firstName, t.lastName, t.compID, MIN(compEntryid)
FROM t
INNER JOIN
(
    SELECT DISTINCT firstName, lastName, compID
    FROM t
    ORDER by rand() 
    LIMIT 5
) t2
ON t.firstName = t2.firstName
AND t.lastName = t2.lastName
AND t.compID = t2.compID
GROUP BY t.firstName, t.lastName, t.compID;

这使用子查询获得5个随机firstName / lastName / compID。然后连接到表以获得MIN compEntryId。

但是对此不确定。认为它会在执行订单/限制之前消除子查询中的重复项,这会阻止有更多条目的人有更多机会。

修改

更多的游戏,我想我找到了解决方案。虽然效率不是其优点之一。

SELECT MIN(compEntryid), firstName, lastName, compID
FROM
(
    SELECT firstName, lastName, compID, compEntryid, @seq:=@seq+1 AS seq
    FROM
    (
        SELECT firstName, lastName, compID, compEntryid
        FROM t
        ORDER by rand()
    ) sub0
    CROSS JOIN (SELECT @seq:=0) sub1
) sub2
GROUP BY sub2.firstName, sub2.lastName, sub2.compID
ORDER BY MIN(seq)
LIMIT 5

这有一个内部子查询,以随机顺序获取所有记录。在另一个子查询周围添加序列号到记录。外部查询按名称等分组,并按该名称的最小序列号排序。 compEntryId只是作为名称/竞赛的MIN获取(我假设你不太关心这个)。

这样,如果有人有5个条目,则内部子查询会将它们混合在列表中。下一个子查询将添加序列号。在这个阶段,这5个条目可以是序列号1到5.外部序列号将按名称的最低序列号排序,而忽略其他序列号,因此在这5个序列中只使用序列号1,忽略2到5个,下一个选定的人是序列号为6的人。

这样他们拥有的参赛作品越多,他们就越有可能成为胜利者,但不能成为5名获胜者中的2名。

感谢kiks73设置了一些sqlfiddle数据: -

http://sqlfiddle.com/#!2/cd777/1

修改

@ kiks73基于上述解决方案。调整以使用非相关子查询计数,并消除一些不确定性。例如,在他的解决方案中,我不太确定MySQL是否会通过隐式执行GROUP BY来选择执行DISTINCT,这也会在执行限制之前隐式地执行结果的撤销(它似乎不是,但是我我不确定是否已定义此行为。

SELECT t.firstName , 
        t.lastName , 
        t.compID,
        MIN(rand() / t1.entry_count) AS rank
FROM
(
    SELECT firstName, lastName, compID, COUNT(*) AS entry_count
    FROM   t 
    GROUP BY firstName, lastName, compID
) t1
INNER JOIN t
ON  t.firstName=t1.firstName 
AND t.lastName = t1.lastName
AND t.compID = t1.compID
GROUP BY t.firstName, t.lastName, t.compID
ORDER BY rank
LIMIT 5