在subselect中选择n个最新行的性能问题

时间:2015-08-19 11:09:14

标签: sql sql-server group-by top-n

我有一个包含课程的数据库。每个课程都包含一组节点,一些节点包含一组学生的答案。答案表看起来(简化)如下:

答案

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,这在这种情况下会导致糟糕的性能。

那么,如何重写查询才能有效?

2 个答案:

答案 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中的相同,但与潜在完全不同,可以获得不同的执行计划。

或者(并且不是很好),只需将内部查询的结果放入表变量中,然后对表变量运行外部查询。

然而,我仍然期望添加索引将是最差的选择。

相关问题