从每个类别中选择至少一个?

时间:2012-09-20 16:02:14

标签: android database sqlite random

SQLFiddle Link

我有一个带有大量测试/考试问题的SQLite数据库。每个问题属于一个问题类别

我的表看起来像这样:
so_questions table

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

例如,输出可能是问题ID 1, 2, 5, 7, 8,或2, 3, 6, 7, 88, 6, 3, 1, 7

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

SELECT ORDER BY category_id, random

基本上,我正在寻找像this这样的SQLite版本。

我想只获得5个结果,但每个类别中只有一个(或更多),结果集中包含所有类别。

恩惠
添加了赏金,因为我很好奇是否可以在SQLite中完成此操作。我可以在SQLite + Java中做到这一点,但有没有办法只在SQLite中执行此操作? :)

SQLFiddle Link

3 个答案:

答案 0 :(得分: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;

答案 1 :(得分:4)

基本上您要找的是选择前N个最大值。我早上花了3-4个小时来搜索它。 (我仍然没有成功,你可能需要再等几个小时)。

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

String strQuery =“SELECT * FROM so_questions group by category_id;”;

输出如下,

enter image description here

将严格按照您的要求返回。

答案 2 :(得分:2)

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

我认为,如果每个类别都有相同的表示,那么你不太可能需要超过3次迭代,这仍然应该低于一秒。

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