Php 从MySQL数据库中获取5个随机行

Php 从MySQL数据库中获取5个随机行,php,mysql,random,weighted,Php,Mysql,Random,Weighted,我到处寻找答案,虽然人们说不要使用ORDER BY RAND()子句,但我认为就我的目的而言,这是可以的,因为这是一场比赛,每次比赛的记录几乎不超过几百条 所以基本上我需要从一个竞赛条目表中检索5条随机记录。但是,任何忠诚客户都会收到额外的条目,例如: compEntryid | firstName | lastName | compID | 1 | bob | smith | 100 2 | bob | smith |

我到处寻找答案,虽然人们说不要使用ORDER BY RAND()子句,但我认为就我的目的而言,这是可以的,因为这是一场比赛,每次比赛的记录几乎不超过几百条

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

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

因此,我们为忠诚会员提供了更好的获奖机会。然而,我有点担心RAND()的常规ORDER返回的结果可能包含同一个人的两个条目?什么是一种优化方法,以确保我们确实有5个随机记录,但同时给那些额外的参赛者一个更好或(加权)的机会?乐意使用多个查询、子查询,甚至是MySQL和PHP的混合?非常感谢您的建议,谢谢

鲈鱼

编辑:

这两个查询都有效

问题1

  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;

如果您有几百条记录,我认为order by rand()解决方案应该可以: 子查询将对条目数进行排序,但仍保留重复项。父选择将获取前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

我认为如果要返回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。然后针对表进行联接以获得最小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设置了一些SQLFIDLE数据:-

编辑

一个基于@kiks73的解决方案。调整为对计数使用非相关子查询,并消除了一些不确定性。例如,对于他的解决方案,我不太确定MySQL是否会选择通过隐式执行GROUP by来执行DISTINCT,而GROUP by也会在执行限制之前隐式执行结果排序(似乎没有,但我不确定是否定义了此行为)


您可以利用distinct查询的外观?此外,在我的模式中,ID之间可能存在漏洞,但无论ID中有什么,它们都是连续的?我不相信这是重复的。发布的给出链接的响应是针对一个不同的(且简单得多)问题的,它只是获取随机记录。这个问题需要的是没有重复项的随机记录,但会根据条目的重复数对返回的随机记录进行偏移。尽管它与该问题类似,该特定的解决方案只适用于一条记录,如果您要通过PHP循环运行该解决方案,那么在迭代中很可能会再次获得相同的参与者,否则您必须保留每个随机生成的数字的日志,并检查它是否不在您的5个随机数字池中,我觉得单用SQL就可以做到这一点,只是尝试协商一个我希望有意义的答案。正如您在链接问题中所看到的,
orderbyrand()
相对较慢。我理解它较慢,但考虑到表中的记录不超过几百条,这似乎是最微不足道的?除非我使用类似于select cols from table的东西,其中rand()<5/(select count(*)from table),其中compid=100限制5,但即使我尝试了,也没有work@Bass我已经编辑了我的答案。如果没有性能问题,我认为这可能是正确的方法。有趣的解决方案。我很喜欢。它不会带来补偿。您可能可以使用MIN/groupby获得该值,但是groupby隐式地添加了一个顺序,这可能会破坏该限制的随机性。谢谢kiks73!你的答案和Kikstart的答案都很有效,但哪一个更为优化?由于使用ORDER BY RAND(),内部查询是否仍然需要创建该临时表?或者因为它是一个不适用的子查询?此外,我需要一个COMPID的WHERE条款,这样我就不包括其他参赛者了?谢谢谢谢kickstart,有点难以理解,但是我测试了它,它也按照上面的解决方案工作。我还应该提到,我有一个电子邮件字段,它对用户来说是唯一的,所以我们不需要检查firstname和lastname,对吗?最后,我需要在compid上找到一个WHERE?我已经尝试过编写一个存储过程,但是我在将它限制到特定compid时遇到了问题?将其限制为特定compid只需要在内部子查询中为其使用WHERE子句。此查询从内部子查询中传递名字/姓氏。您可以只获取电子邮件地址,但这取决于您是否需要返回其他详细信息。它很麻烦,而且我怀疑它是否有那么快。尽管如此,您的查询仍然可以完美地工作!看起来也很有分量,虽然ran的订单
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
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