7

SQLFiddle 链接

我有一个 SQLite 数据库,里面有一堆测试/考试问题。每个问题属于一个问题类别

我的表如下所示:
so_questions 表


我要做的目标是选择 5 个随机问题,但结果必须至少包含每个类别中的一个。目标是从每个类别中随机选择一组问题。

例如,输出可以是问题 ID1, 2, 5, 7, 82, 3, 6, 7, 88, 6, 3, 1, 7

ORDER BY category_id, RANDOM()
我可以通过执行下面的 SQL 从 SQLite 获取随机的问题列表,但是我如何确保结果包含来自我的每个类别的问题?

SELECT ORDER BY category_id,随机

基本上,我正在寻找这样的东西 SQLite 版本。

我想只得到 5 个结果,但每个类别都有一个(或多个),结果集中表示所有类别。

Bounty
添加了赏金,因为我很好奇是否可以仅在 SQLite 中完成此任务。我可以在 SQLite+Java 中做到这一点,但有没有办法只在 SQLite 中做到这一点?:)

SQLFiddle 链接

4

3 回答 3

6

答案的关键在于结果中有两种问题:对于每个类别,必须限制一个问题来自该类别;和一些剩余的问题。

首先,受限问题:我们只从每个类别中选择一条记录:

SELECT id, category_id, question_text, 1 AS constrained, max(random()) AS r
FROM so_questions
GROUP BY category_id

(此查询依赖于 SQLite 3.7.11(在 Jelly Bean 或更高版本中)中引入的功能:在查询SELECT a, max(b)中,保证的值a来自具有最大值的记录b。)

我们还必须得到非约束问题(过滤掉已经在约束集中的重复项将在下一步发生):

SELECT id, category_id, question_text, 0 AS constrained, random() AS r
FROM so_questions

当我们将这两个查询与 组合UNION然后按 分组时id,我们将所有重复项放在一起。然后选择max(constrained)确保对于具有重复项的组,仅保留受约束的问题(而所有其他问题无论如何每组只有一条记录)。

最后,该ORDER BY子句确保首先出现受限问题,然后是一些随机的其他问题:

SELECT *, max(constrained)
FROM (SELECT id, category_id, question_text, 1 AS constrained, max(random()) AS r
      FROM so_questions
      GROUP BY category_id
      UNION ALL
      SELECT id, category_id, question_text, 0 AS constrained, random() AS r
      FROM so_questions)
GROUP BY id
ORDER BY constrained DESC, r
LIMIT 5

对于较早的 SQLite/Android 版本,我还没有找到不使用临时表的解决方案(因为受约束问题的子查询必须多次使用,但不会保持不变,因为random()):

BEGIN TRANSACTION;

CREATE TEMPORARY TABLE constrained AS
SELECT (SELECT id
        FROM so_questions
        WHERE category_id = cats.category_id
        ORDER BY random()
        LIMIT 1) AS id
FROM (SELECT DISTINCT category_id
      FROM so_questions) AS cats;

SELECT ids.id, category_id, question_text
FROM (SELECT id
      FROM (SELECT id, 1 AS c
            FROM constrained
            UNION ALL
            SELECT id, 0 AS c
            FROM so_questions
            WHERE id NOT IN (SELECT id FROM constrained))
      ORDER BY c DESC, random()
      LIMIT 5) AS ids
JOIN so_questions ON ids.id = so_questions.id;

DROP TABLE constrained;
COMMIT TRANSACTION;
于 2012-10-16T21:01:25.923 回答
4

基本上你正在寻找的是选择 top N max values。我早上花了 3-4 个小时来搜索它。(我仍然没有成功,你可能需要再等几个小时)。

对于临时解决方案,您可以使用group by选项,如下所示,

String strQuery = "SELECT * FROM so_questions group by category_id;";

输出如下,

在此处输入图像描述

将返回您的确切要求。

于 2012-09-20T16:06:40.180 回答
2

因为它是 sqlite(因此是本地的):在你有 5 个答案和四个不同的类别之前查询会有多慢,每次迭代都删除重复的类别行。

我认为,如果每个类别都具有相同的代表性,那么您需要超过 3 次迭代的可能性很小,而迭代次数仍应低于 1 秒。

它在算法上并不好,但对我来说,在 SQL 语句中使用 random() 无论如何在算法上并不好。

于 2012-09-21T07:06:22.387 回答