【问题标题】:Get 5 random rows from MySQL DB [duplicate]从 MySQL DB 中获取 5 个随机行 [重复]
【发布时间】:2014-07-26 11:19:09
【问题描述】:

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

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

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 的混合?任何建议都非常感谢谢谢!

贝斯

编辑:

这两个查询都有效!

查询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;

查询2

 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

【问题讨论】:

  • 你可以使用DISTINCT()
  • 使用 distinct 的查询会是什么样子?同样在我的架构中,ID 之间可能存在漏洞,但是无论 ID 是什么,它们都是连续的?
  • 我不相信这是重复的。提供链接的已发布响应是针对不同(且更简单)的问题,即仅获取随机记录。这个问题想要没有重复的随机记录,但会根据条目的重复数返回随机记录。
  • 虽然它与那个问题相似,但该特定解决方案适用于 ONE 记录,如果您要通过 PHP 循环运行它,那么在您的迭代中很有可能再次获得相同的参赛者否则您将不得不记录每个随机生成的数字并检查它是否已经在您的 5 个随机数字池中,我觉得这可以单独使用 SQL 完成,并且只需尝试协商一个答案,我希望这是有道理的.

标签: php mysql random weighted


【解决方案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

【讨论】:

  • 正如您在链接问题中看到的那样,ORDER BY rand() 相对较慢。
  • 我知道它很慢,但考虑到该表不会超过几百条记录,它似乎最微不足道?除非我使用 select cols from table where rand()
  • @Bass 我已经编辑了我的答案。如果您没有性能问题,我认为这可能是正确的方法。
  • 有趣的解决方案。我很喜欢这样。它不会带回 compEntryId。您也许可以使用 MIN / GROUP BY 来获得它,但 GROUP BY 隐式添加了一个可能会破坏限制随机性的顺序。
  • 感谢 kiks73 !你的答案和 kikstarts 的答案都很有用,但是哪个更优化?由于使用 ORDER BY RAND() ,您的内部查询是否仍需要创建该临时表?还是因为它是一个不适用的子查询?我还需要 COMPID 的 WHERE 子句,这样我就不会包括其他参赛者?谢谢!
【解决方案2】:

如果你想返回一个 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

【讨论】:

  • 谢谢你的kickstart,有点理解但是我测试了它并且它也按照上述解决方案工作。我还应该提到我有一个电子邮件字段,该字段对用户来说是唯一的,所以我们不需要检查名字和姓氏是否正确?最后,我需要在 compid 上找到一个 WHERE 吗?
  • 我曾尝试编写一个存储过程,但我在将其限制为特定的 compid 时遇到了问题! ?
  • 将其限制为特定的 compid 只需要内部子查询中的 WHERE 子句。此查询通过内部子查询中的名字/姓氏进行。您可以只获取电子邮件地址,但取决于您是否需要返回其他详细信息。这很麻烦,我怀疑它是否那么快。
  • 不过您的查询完美无缺!似乎也是加权的,尽管按 rand() 限制 5 的顺序和按 yield 分组的结果相似,但它似乎没有加权,对吗?
  • Kickstart 你绝对是个天才!我在您的个人资料中注意到您的艺术能力为零!在我不设计时,我是专业的艺术总监和程序员爱好者,如果您在该部门需要任何帮助,我非常乐意为我的朋友提供帮助:)
猜你喜欢
  • 1970-01-01
  • 2017-05-13
  • 1970-01-01
  • 1970-01-01
  • 2020-02-23
  • 2018-03-13
  • 2012-02-05
  • 2011-06-23
  • 1970-01-01
相关资源
最近更新 更多