如何在单独的数据集中获取cte的rowcount?

时间:2011-05-24 14:48:49

标签: performance sql-server-2008 common-table-expression

我已经确定了一种使用CTE和Row_Number函数从数据库中获取快速分页结果的方法,如下所示......

DECLARE @PageSize INT = 1
DECLARE @PageNumber INT = 2

DECLARE @Customer TABLE (
  ID       INT IDENTITY(1, 1),
  Name     VARCHAR(10),
  age      INT,
  employed BIT)

INSERT INTO @Customer
    (name,age,employed)
SELECT 'bob',21,1
    UNION ALL
SELECT 'fred',33,1
    UNION ALL
SELECT 'joe',29,1
    UNION ALL
SELECT 'sam',16,1
    UNION ALL
SELECT 'arthur',17,0;


WITH cteCustomers
     AS ( SELECT
            id,
            Row_Number( ) OVER(ORDER BY Age DESC) AS Row
          FROM   @Customer
          WHERE  employed = 1 
     /*Imagine I've joined to loads more tables with a really complex where clause*/
     )       

SELECT
  name,
  age,
  Total = ( SELECT
              Count( id )
            FROM   cteCustomers )
FROM       cteCustomers
INNER JOIN @Customer cust
  /*This is where I choose the columns I want to read, it returns really fast!*/
  ON cust.id = cteCustomers.id
WHERE      row BETWEEN ( @PageSize * @PageNumber - 1 ) AND ( @PageSize * ( @PageNumber ) )
ORDER      BY row ASC

使用这种技术,即使在复杂的连接和过滤器上,返回的结果也非常快。

要执行分页,我需要知道完整CTE返回的总行数。我通过放置一个包含它的列来“装箱”这个

Total = ( SELECT
              Count( id )
            FROM   cteCustomers )

有没有更好的方法可以在不同的结果集中返回总数而不将其放入列中?因为它是CTE,我似乎无法将其纳入第二个结果集。

2 个答案:

答案 0 :(得分:5)

首先不使用临时表,我会使用CROSS JOIN来降低COUNT上逐行评估的风险

要获得总行数,需要单独进行WHERE

WITH cteCustomers
     AS ( SELECT
            id,
            Row_Number( ) OVER(ORDER BY Age DESC) AS Row
          FROM   @Customer
          WHERE  employed = 1 
     /*Imagine I've joined to loads more tables with a really complex where clause*/
     )       

SELECT
  name,
  age,
  Total
FROM       cteCustomers
INNER JOIN @Customer cust
  /*This is where I choose the columns I want to read, it returns really fast!*/
  ON cust.id = cteCustomers.id

CROSS JOIN

(SELECT Count( *) AS Total FROM   cteCustomers ) foo

WHERE      row BETWEEN ( @PageSize * @PageNumber - 1 ) AND ( @PageSize * ( @PageNumber ) )
ORDER      BY row ASC

然而,这并不能保证给出准确的结果,如下所示:
can I get count() and rows from one sql query in sql server?

编辑:发表一些评论后。

如何避免交叉加入

WITH cteCustomers
     AS ( SELECT
            id,
            Row_Number( ) OVER(ORDER BY Age DESC) AS Row,
            COUNT(*) OVER () AS Total --the magic for this edit
          FROM   @Customer
          WHERE  employed = 1 
     /*Imagine I've joined to loads more tables with a really complex where clause*/
     )       

SELECT
  name,
  age,
  Total
FROM       cteCustomers
INNER JOIN @Customer cust
  /*This is where I choose the columns I want to read, it returns really fast!*/
  ON cust.id = cteCustomers.id
WHERE      row BETWEEN ( @PageSize * @PageNumber - 1 ) AND ( @PageSize * ( @PageNumber ) )
ORDER      BY row ASC

注意:YMMV的性能取决于2005或2008,Service Pack等

编辑2:

SQL Server Central显示了另一种技术,你有反向ROW_NUMBER。看起来很有用

答案 1 :(得分:1)

@Digiguru

OMG,这真的是圣杯!

WITH cteCustomers
AS ( SELECT id,
         Row_Number() OVER(ORDER BY Age DESC) AS Row,
         Row_Number() OVER(ORDER BY id ASC)
            + Row_Number() OVER(ORDER BY id DESC) - 1 AS Total /*<- voodoo here*/
      FROM   @Customer
      WHERE  employed = 1
      /*Imagine I've joined to loads more tables with a really complex where clause*/
   )
SELECT  name,  age,  Total
  /*This is where I choose the columns I want to read, it returns really fast!*/
FROM cteCustomers
INNER JOIN @Customer cust
ON cust.id = cteCustomers.id
WHERE row BETWEEN ( @PageSize * @PageNumber - 1 ) AND ( @PageSize * ( @PageNumber ) )
ORDER BY row ASC

现在很明显。