答案的关键在于结果中有两种问题:对于每个类别,一个问题必须约束来自该类别;以及一些剩余的问题。
首先,限制性问题:我们只从每个类别中选择一条记录:
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 的值保证来自具有最大 @987654324 的记录@值。)
我们还必须得到非约束问题(过滤掉已经在约束集中的重复项将在下一步发生):
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;