从有序CTE中检索X行,TOP与范围

时间:2016-11-08 14:21:22

标签: sql-server tsql common-table-expression query-performance

目的:

想要知道在尝试从已订购的CTE中检索有限数量的行时,哪种性能更快/更好。

示例:

假设我有一个CTE(故意简化),看起来像这样,我只想要前5行:

WITH    cte
          AS (
               SELECT  Id =  RANK() OVER (ORDER BY t.ActionID asc)
                      , t.Name
               FROM     tblSample AS t -- tblSample is indexed on Id              
             )

enter image description here

哪个更快:

SELECT TOP 5 * FROM cte

OR

SELECT * FROM cte WHERE Id BETWEEN 1 AND 5 ?

备注:

  1. 我不是DB程序员,所以对我来说TOP解决方案似乎更好 一旦SS找到第5行,它将停止执行并且"返回" (100% 假设)在另一种方法中,我觉得它会不必要地 处理整个cte。
  2. 我的问题是CTE,如果它是一张桌子,这个问题的答案是否相同?

3 个答案:

答案 0 :(得分:1)

最重要的是要注意两个查询都不会始终生成相同的结果集。请考虑以下数据:

CREATE TABLE #tblSample (ActionId int not null, name varchar(10) not null);
INSERT #tblSample VALUES (1,'aaa'),(2,'bbb'),(3,'ccc');

这两个都会产生相同的结果:

WITH CTE AS 
(
  SELECT id = RANK() OVER (ORDER BY t.ActionID asc), t.name
  FROM #tblSample t
)
SELECT TOP(2) * FROM CTE;

WITH CTE AS 
(
  SELECT id = RANK() OVER (ORDER BY t.ActionID asc), t.name
  FROM #tblSample t
)
SELECT * FROM CTE WHERE id BETWEEN 1 AND 2;

现在让我们进行更新:

UPDATE #tblSample SET ActionId = 1;

在此更新之后,第一个查询仍返回两行,第二个查询返回3.请记住,在TOP查询中没有ORDER BY,结果无法保证,因为SQL中没有默认顺序。

有了这个 - 哪个表现更好?这取决于。它取决于您的索引,统计信息,行数以及SQL引擎的执行计划。

答案 1 :(得分:0)

前5个按照表中定义的索引选择任意5行,而1到5之间的Id尝试根据Id列获取数据,无论是索引搜索还是扫描取决于所选属性。两者都是两个不同的查询..如果您没有Id上的任何索引,则“查询之间的ID”可能会很慢,

让我试着用一个例子来解释......

请考虑这是您的数据..

create index nci_name on yourcte(id) include(name)
--drop index nci_name on yourcte

;with cte as (
select * from yourcte )
select top 5 * from cte

;with cte as (
select * from yourcte )
select * from cte where id between 1 and 5

首先我要创建包含名称的id的索引,现在如果你看到你的第二个查询做索引搜索,第一个进行索引扫描并选择前5,所以在这种情况下第二种方法更好

参见执行计划:

enter image description here

现在我正在删除索引 执行      - 在yourtable上的drop index nci_name

现在它对两种方法进行表扫描

enter image description here

如果您在两个表扫描中都注意到,在第一个扫描中它只读取5行,第二个方法读取10行并应用谓词

请参阅第一个计划的执行计划属性

enter image description here

对于第二种方法,它读取10行

enter image description here

现在第一种方法更好..

在你的情况下,这个索引需要在ActionId上,它决定了id。

因此,性能取决于您对基表的索引方式。

答案 2 :(得分:0)

为了获得您在cte中计算的RANK(),必须按t.ActionID对所有数据进行排序。排序是一种阻塞操作:必须在输出单行之前处理整个输入。

因此,在这种情况下,您是否选择任意五行,或者如果您选择排在堆顶部的五行可能无关紧要。