我有一个包含课程的数据库。每个课程都包含一组节点,一些节点包含一组学生的答案。答案表看起来(简化)如下:
答案
id | courseId | nodeId | answer
------------------------------------------------
1 | 1 | 1 | <- text ->
2 | 2 | 2 | <- text ->
3 | 1 | 1 | <- text ->
4 | 1 | 3 | <- text ->
5 | 2 | 2 | <- text ->
.. | .. | .. | ..
当教师打开课程(即courseId = 1)时,我想选择最近收到最多答案的节点。我可以使用以下查询执行此操作:
with Answers as
(
select top 50 id, nodeId from Answer A where courseId=1 order by id desc
)
select top 1 nodeId from Answers group by nodeId order by count(id) desc
或同样使用此查询:
select top 1 nodeId from
(select top 50 id, nodeId from Answer A where courseId=1 order by id desc)
group by nodeId order by count(id) desc
在两个查询中,选择最新的50个答案(具有最高ID),然后按nodeId分组,以便我可以选择频率最高的答案。但问题是,查询速度很慢。如果我只运行子选择,它只需不到一秒钟,分组50行应该很快,但是当我运行整个查询时,大约需要10秒!我的猜测是sql server首先进行选择和分组,然后进行前50和前1,这在这种情况下会导致糟糕的性能。
那么,如何重写查询才能有效?
答案 0 :(得分:2)
您可以添加索引以提高查询效率。对于此查询:
with Answers as (
select top 50 id, nodeId
from Answer A
where courseId = 1
order by id desc
)
select top 1 nodeId
from Answers
group by nodeId
order by count(id) desc;
最佳指数为Answer(courseId, id, nodeid)
。
答案 1 :(得分:0)
为了更具洞察力,我们需要查看该表上的索引以及您正在获取的执行计划(内部查询的一个计划,一个完整查询计划)
我甚至建议在添加本页其他地方提到的索引时再次进行相同的分析。
如果没有这些信息,我们唯一可以推荐的是反复试验。
例如,尝试避免使用TOP
(这应该无关紧要,但我们在猜测时看不到您的索引和执行计划)
WITH
Answers AS
(
SELECT
ROW_NUMBER() OVER (ORDER BY id DESC) AS rowId,
id,
nodeId
FROM
Answer
WHERE
courseId = 1
),
top50 AS
(
SELECT
nodeId,
COUNT(*) AS row_count
FROM
Answers
WHERE
rowId <= 50
GROUP BY
nodeId
),
ranked AS
(
SELECT
ROW_NUMBER() OVER (ORDER BY row_count DESC, nodeId DESC) AS ordinal,
nodeID
FROM
top50
)
SELECT
nodeID
FROM
ranked
WHERE
oridinal = 1
其中大部分都在顶部,但在功能上与您在OP中的相同,但与潜在完全不同,可以获得不同的执行计划。
或者(并且不是很好),只需将内部查询的结果放入表变量中,然后对表变量运行外部查询。
然而,我仍然期望添加索引将是最差的选择。