答えの鍵は、結果に 2 種類の質問があることです。カテゴリごとに、そのカテゴリから来るように制約する必要がある 1 つの質問。そしていくつかの残りの質問。
まず、制約付きの質問: 各カテゴリから 1 つのレコードを選択するだけです。
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
これら 2 つのクエリを で結合しUNION
、 でグループ化するとid
、すべての重複がまとめられます。次に選択max(constrained)
すると、重複するグループに対して、制約付きの質問のみが残ります (他のすべての質問は、とにかくグループごとに 1 つのレコードしかありません)。
最後に、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;