这是教育应用程序向语言学习者教授单词的要求:
这是我们的设计:
Words
- Id
- Word
- Meaning
- PartOfSpeech
LearnerWords
- Id
- LearnerId
- WordId
- TypeOfInteraction (ignored, memorized, memorizing, reviewing, etc.)
Learners
- Id
- Name
这是我们执行的伪查询,用于查找给定学习者的下一个新单词:
select top 1 *
from Words
where Id not in
(
select WordId
from LearnerWords
where LearnerId = @learnerId
)
当然,整体设计和查询要复杂得多,并且在为给定的学习者选择下一个新单词时会有更多细节。
现在,假设单词列表包含100K个单词,并且学习者已经学习了超过5K个单词。使用给定的查询,越多学习者学习更多单词,这会越来越慢,越来越慢。
这些类型的业务需求是否有更好的设计?在这种情况下如何设计规模?
答案 0 :(得分:1)
对于您的LearnerWords
表,我建议您将TypeOfInteraction
列替换为一组可以为空的日期列,这些列指示每次互动何时发生或开始(如果没有,则为null),例如:
CREATE TABLE `LearnerWords` (
`LearnerId` int(11) UNSIGNED NOT NULL ,
`WordId` int(11) UNSIGNED NOT NULL ,
`Ignored` datetime NULL ,
`Memorized` datetime NULL ,
`Memorizing` datetime NULL ,
`Reviewing` datetime NULL ,
PRIMARY KEY (`LearnerId`, `WordId`)
);
这会将您的表从每个学习者/单词/互动的行减少到每个学习者/单词一行,同时记录更多信息。
关于您的查询:
小心NOT IN
和子查询(或任何动态值集)。 NOT IN
如果检查的集合为空或仅包含NULL
,则会返回NULL
,这意味着对于LearnerWords
中没有任何记录的学员,您的查询不会收到任何结果。
更好的方法是使用LEFT JOIN
或NOT EXISTS
,例如:
SELECT TOP 1 *
FROM Words w
WHERE NOT EXISTS (
SELECT 1
FROM LearnerWords lw
WHERE lw.LearnerId = @learnerId
AND lw.WordId = w.WordId
)
另请注意dcieslak的答案 - 索引对性能至关重要。
答案 1 :(得分:0)
只需在LearnerID上的LearnerWords中创建索引(加上复合索引中的TypeOfInteraction),它应该很快。您的数据库查询优化引擎将确定您只需要1行,它将使用索引非常快速地访问所需数据。
答案 2 :(得分:0)
答案取决于实际使用的DBMS。其中一些将以一种方式优化查询,一些方式 - 另一方式。大多数情况下,通过使用反连接模式,您不会失去执行速度:
select top 1 w.*
from Words w
left join LearnerWords lw on lw.LearnerId = @learnerId and lw.WordId = w.Id
where lw.WordId is null