【问题标题】:Better way than current query to assemble random categorized entries?比当前查询组装随机分类条目更好的方法?
【发布时间】:2016-07-21 23:22:15
【问题描述】:

我试图准确地显示 6 个随机“娱乐”条目,但在我当前的查询中,它得到一个介于 1 和 6 之间的随机数,并显示该条目数。如何更新此查询以使其准确显示我的文章表中的 6 个随机娱乐条目?另外,我不想做 ORDER BY RAND() 因为我的桌子加班会变大。这是我当前的查询:

SELECT
    r1.*
FROM
    Articles AS r1
    INNER JOIN (SELECT(RAND() * (SELECT MAX(id) FROM Articles)) AS id) AS r2
WHERE
    r1.id >= r2.id
    AND r1.category = 'entertainment'
LIMIT 6;

表结构:

table Articles
- id (int)
 - category (varchar)
 - title (varchar)
 - image (varchar)
 - link (varchar)
 - Counter (int)
 - dateStamp (datetime)

【问题讨论】:

  • 您的id 列中是否存在空白?
  • @PaulSpiegel 是的,例如 id:1 归类为生活方式,id:2 归类为娱乐,id:3 归类为科学,等等。当查询收集所有娱乐类别时,所有这些 id 都是数字,它们之间有间隔。

标签: php mysql database-schema


【解决方案1】:

select floor(rand() * m.maxId + 1) as randomId
from Articles a
join (SELECT MAX(id) maxId FROM Articles) m
limit 100

您将创建 100 个随机 ID。我取 100,因为您的 id 列中有空白,因此没有获得足够的现有 id 的可能性将(非常)小。然后您可以使用该结果仅选择具有这些 id 的 6 行:

select distinct a.*
from (
    select id, floor(rand() * m.maxId + 1) as randomId
    from Articles a
    join (SELECT MAX(id) maxId FROM Articles) m
    limit 100
) r
join Articles a on a.id = r.randomId
order by r.id -- only need it for small tables. will slow down the query on big tables
limit 6

子选择中LIMIT 的最佳值取决于您的 id 中的空白百分比。 100 应该足够快。

更新

如果您需要按category 过滤,您可以在ORDER BYLIMIT 之前添加WHERE a.category = 'entertainment' 子句。但在这种情况下,您将需要调整生成的随机 ID 的数量。

例如:如果您插入了 100 万篇文章,但其中 10% 被删除,那么平均 90 个随机生成的 id 确实存在。如果现在 10% 的文章有 category = 'entertainment',那么平均 9 个随机行将匹配条件。 平均 意味着 - 它可能是 3,也可能是 16。因此您需要生成更多随机 ID 以确保您至少获得 6 篇文章。在子选择中使用LIMIT 1000,您将平均获得 90 篇随机娱乐文章。这样你就不太可能得到少于 6 个的。所以你需要知道你的表的统计数据才能选择一个好的LIMIT

WHERE 子句的另一个问题是 MySQL 可能会反转连接顺序以使用索引进行过滤。对于少量生成的随机 id,这可能会更快,但如果子选​​择中的 LIMIT 很大,则可能会更慢。您可以使用STRIGHT_JOIN 而不是JOIN 来强制加入顺序 - 但在我使用LIMIT 10000 进行的测试中,它没有产生 可测量的差异。

如果您的条件过于挑剔(例如,只有 1% 的文章有 category='entertainment'),简单的 ORDER BY RAND() 会更快,因为否则您需要创建太多随机 ID。但最多 10K 行符合您的条件ORDER BY RAND() 就足够快了。

【讨论】:

  • 所以它只会得到前100个id?如果我有超过 100 个 id 怎么办?另外,我想获取娱乐条目,此查询未在 WHERE 中指定
  • No.. 它会生成 100 个随机 ID。对于至少有 100 行的子选择,您可以使用除 Articles 之外的任何 Other 表。你只需要它来创建一组 100 个虚拟行来生成一个随机数。
  • 查询中是否包含@preLimit 集?另外,查询中的 m 是什么意思?
  • 抱歉,LIMIT 不适用于 IN (..) 子句。 LIMIT 的会话变量也没有。所以我将其更改为使用JOIN
  • 空白页消失,显示 6 个条目,但也显示重复条目
【解决方案2】:

您的“娱乐”条目都应该有唯一的 id,应该是整数。

如果是这种情况,您可以使用 PHP 的 rand() 函数在 1 和您拥有的条目数量之间生成 6 个随机整数。这是我编写的一个可能有用的函数。

function selectSixRandomEntries() {
    $queryWhere = "";
    $i = 0;

    while($i < 6) {
        $randomNumber = rand(1, 200);
        if (strpos($queryWhere, $randomNumber) == -1)
            continue;

        $queryWhere .= "r1.id = " . rand(1, 200);
        if ($i != 5)
            $queryWhere .= " OR ";

        $i++;
    }

    return $queryWhere
}

你可以尝试使用它

$query = "SELECT
    r1.*
FROM
    Articles AS r1
    INNER JOIN (SELECT(RAND() * (SELECT MAX(id) FROM Articles)) AS id) AS r2
WHERE
    " . selectSixRandomEntries() . " 
    AND r1.category = 'entertainment'
LIMIT 6";

【讨论】:

  • 由于某种原因,在添加代码并对其进行调整后,我得到了一个空白页,查询中显示“WHERE”的部分。selectSixRandomEntries()。“这正是它应该的方式要布局吗?
  • 应该是= 而不是==OR 而不是AND
  • 谢谢你,保罗,我现在已经修好了。
  • 但是,您只能获得 1 到 200 之间的 id,并且您可以点击已删除的 id。
  • 对不起 user2896120,现在尝试添加它。看看我更新后它是否有效。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2012-11-22
  • 1970-01-01
  • 1970-01-01
  • 2013-03-26
  • 2014-06-12
  • 2023-03-24
相关资源
最近更新 更多