Where子句中的SQL Row_Number()函数


90

Row_Number()在where子句中找到了一个用函数回答的问题。当我尝试一个查询时,出现以下错误:

“消息4108,级别15,状态1,第1行窗口函数只能出现在SELECT或ORDER BY子句中。”

这是我尝试过的查询。如果有人知道如何解决此问题,请告诉我。

SELECT employee_id 
FROM V_EMPLOYEE 
WHERE row_number() OVER ( ORDER BY employee_id ) > 0 
ORDER BY Employee_ID

9
ROW_NUMBER() OVER (ORDER BY employee_id) > 0始终会TRUE
得出

3
是的,没错。我不担心这种情况,我可以随时更改。我希望查询先运行,然后再考虑将行号保持在500到800之间...谢谢

2
@Joseph:为什么要避免使用CTE?
OMG小马

1
@rexem-我不是SQL Server方面的专家。我正在尝试帮助一个大型项目中的团队,他们面临着很多性能问题。他们正在使用UDF和CTE。在一张表中,他们只有5000条记录,如果有5位用户访问搜索,则检索将花费一分钟以上的时间。一段时间,它会失败并超时。因此,我试图避免使用CTE和UDF,并试图提出一种可以解决性能问题的简单SQL查询。

1
大家好,请查看我在下面发布的链接,其中以不同的方式使用row_number()进行回答。有人可以将我的初始查询与链接中的查询进行比较吗?感谢您的帮助

Answers:


91

要解决此问题,请将您的select语句包装在CTE中,然后可以查询CTE并在where子句中使用窗口函数的结果。

WITH MyCte AS 
(
    select   employee_id,
             RowNum = row_number() OVER ( order by employee_id )
    from     V_EMPLOYEE 
    ORDER BY Employee_ID
)
SELECT  employee_id
FROM    MyCte
WHERE   RowNum > 0

7
我正在努力避免CTE。那是我要找的最坏的情况。谢谢

3
如果您使用子查询而不是CTE,它可能会运行得更快。在某些情况下,我看到的性能提高了1.5倍
Brian Webster

3
CTE SELECT中也应该有TOP,否则SQL 2008 Server将由于ORDER BY(除非使用TOP,否则不支持)而不会执行查询
Muflix 2015年

2
我正在使用SQL2005(ugh)-通过在FROM后面删除“ ORDER BY”,可以避免使用“ TOP”。无论如何,在OVER之后用(Order By)是多余的。
乔B

我希望有一种方法可以ROW_NUMBER()WHERE没有CTE的情况下使用in 子句:(
Jalal

61
SELECT  employee_id
FROM    (
        SELECT  employee_id, ROW_NUMBER() OVER (ORDER BY employee_id) AS rn
        FROM    V_EMPLOYEE
        ) q
WHERE   rn > 0
ORDER BY
        Employee_ID

请注意,此过滤器是多余的:ROW_NUMBER()从开始1并始终大于0


2
@ DavideChicco.it:在SQL Server中,派生表需要一个别名(我应该写一个别名AS q,但这也可以)。
Quassnoi

2
在命名别名时,可读性是我的重点。您可以将rn编写为RowNumber,将q编写为DerivedTable,将where子句编写为where DerivedTable.RowNumber>0。在我看来,这在6个月的时间里不会使您感到困惑,这将大大减少混乱。
爱德华·科摩

2
@EdwardComeau:rn这些天是行号的一种普遍接受的缩写。尝试在Google搜索字符串中输入“ row_number as as ...”,看看它能为您提供什么建议。
Quassnoi

3
@Quassnoi,可读性是良好编码的关键,翻译rn(或其他缩写的别名)的认知工作将为您自己和维护代码的人们加分。注意,Microsoft首先命中,SELECT ROW_NUMBER()OVER(ORDER BY SalesYTD DESC)AS Row,...在此之前,我也没有遇到过,所以您在“通用”级别的里程可能会有所不同。
爱德华·科摩

1
@Quassnoi,和第二命中,SO文章- stackoverflow.com/questions/961007/how-do-i-use-row-number若干变化和不RN ;-)
爱德华科莫

32
Select * from 
(
    Select ROW_NUMBER() OVER ( order by Id) as 'Row_Number', * 
    from tbl_Contact_Us
) as tbl
Where tbl.Row_Number = 5

19

我想你想要这样的东西:

SELECT employee_id 
FROM  (SELECT employee_id, row_number() 
       OVER (order by employee_id) AS 'rownumber' 
       FROM V_EMPLOYEE) TableExpressionsMustHaveAnAliasForDumbReasons
WHERE rownumber > 0

4
如果上述查询对您不起作用,请为该表创建别名。修改倒数第二行,From V_EMPLOYEE) A因为将A添加为别名。
Hammad Khan

7

为了回应关于rexem答案的评论,关于内联视图或CTE是否更快,我重新编写了查询以使用表I,并且每个人都可以使用:sys.objects。

WITH object_rows AS (
    SELECT object_id, 
        ROW_NUMBER() OVER ( ORDER BY object_id) RN
    FROM sys.objects)
SELECT object_id
FROM object_rows
WHERE RN > 1

SELECT object_id
FROM (SELECT object_id, 
        ROW_NUMBER() OVER ( ORDER BY object_id) RN
    FROM sys.objects) T
WHERE RN > 1

生成的查询计划完全相同。我希望在所有情况下,查询优化器都会提出相同的计划,至少是用内联视图简单地替换CTE,反之亦然。

当然,请在您自己的系统上尝试自己的查询以查看是否存在差异。

另外,row_number()在where子句中,Stack Overflow给出的答案是一个常见错误。row_number()在处理select子句之前,逻辑上不可用。人们会忘记这一点,当他们在不测试答案的情况下回答问题时,答案有时是错误的。(我本人已犯有罪。)


1
Thx Shannon。您使用的是哪个版本的SQL Server?
OMG小马

1
这意味着,该链接中提供的答案是错误的吗?但是,发布问题的人同意它的工作..令人惊讶.. :-)

2
@Joseph,但是如果您查看OP在链接的问题中发布的另一个答案,您会发现他链接到的代码版本与接受的答案中的代码版本不同。我不知道他为什么接受答案,即使它不能像输入的那样运行。也许在被接受后的某个时候就对其进行了编辑,也许即使没有完全正确,也足以使他继续前进。
Shannon Severance

1
@Rexem:SQL Server 2005和SQL Server 2008两者。早期版本不支持CTE或ROW_NUMBER()
Shannon Severance 2009年

6

我觉得所有显示使用CTE或子查询的答案都足以解决此问题,但我看不出有人对OP为何会出现问题深感兴趣。OP建议不起作用的原因是由于此处的逻辑查询处理顺序:

  1. 加入
  2. 哪里
  3. 通过...分组
  4. 与多维数据集/汇总
  5. 拥有
  6. 选择
  7. 不同
  8. 订购
  9. 最佳
  10. 偏移/ FETCH

我相信这对答案很有帮助,因为它解释了为什么会发生此类问题。WHERESELECT对许多功能进行必要的CTE或子查询之前,始终会处理。您将在SQL Server中看到很多。


4

使用CTE(SQL Server 2005+):

WITH employee_rows AS (
  SELECT t.employee_id,
         ROW_NUMBER() OVER ( ORDER BY t.employee_id ) 'rownum'
    FROM V_EMPLOYEE t)
SELECT er.employee_id
  FROM employee_rows er
 WHERE er.rownum > 1

使用内联视图/非CTE等效替代方法:

SELECT er.employee_id
  FROM (SELECT t.employee_id,
               ROW_NUMBER() OVER ( ORDER BY t.employee_id ) 'rownum'
          FROM V_EMPLOYEE t) er
 WHERE er.rownum > 1

1
哪个表现更好?使用CTE还是子查询?谢谢

1
看到香农的答案-在他的测试中,他们是平等的。
OMG小马

6
不,不是更快。在中SQL ServerCTE和的内联视图是同一件事,并且具有相同的性能。当在中使用非确定性函数时CTE,将在每次调用时对其进行重新评估。必须使用肮脏的技巧来强制实现CTE。见我的博客这些文章:explainextended.com/2009/07/28/... explainextended.com/2009/05/28/generating-xml-in-subqueries
Quassnoi

2

根据OP对问题的回答:

请查看此链接。它有不同的解决方案,对于提出该问题的人来说似乎很有用。我正在尝试找出这样的解决方案。

在SQL Server 2005中使用ROW_NUMBER()OVER()对不同的列进行排序的分页查询

约瑟夫

“方法1”就像来自链接问题的OP查询,而“方法2”就像来自所选答案的查询。您必须查看此答案中链接的代码以查看实际情况,因为修改了所选答案中的代码以使其起作用。试试这个:

DECLARE @YourTable table (RowID int not null primary key identity, Value1 int, Value2 int, value3 int)
SET NOCOUNT ON
INSERT INTO @YourTable VALUES (1,1,1)
INSERT INTO @YourTable VALUES (1,1,2)
INSERT INTO @YourTable VALUES (1,1,3)
INSERT INTO @YourTable VALUES (1,2,1)
INSERT INTO @YourTable VALUES (1,2,2)
INSERT INTO @YourTable VALUES (1,2,3)
INSERT INTO @YourTable VALUES (1,3,1)
INSERT INTO @YourTable VALUES (1,3,2)
INSERT INTO @YourTable VALUES (1,3,3)
INSERT INTO @YourTable VALUES (2,1,1)
INSERT INTO @YourTable VALUES (2,1,2)
INSERT INTO @YourTable VALUES (2,1,3)
INSERT INTO @YourTable VALUES (2,2,1)
INSERT INTO @YourTable VALUES (2,2,2)
INSERT INTO @YourTable VALUES (2,2,3)
INSERT INTO @YourTable VALUES (2,3,1)
INSERT INTO @YourTable VALUES (2,3,2)
INSERT INTO @YourTable VALUES (2,3,3)
INSERT INTO @YourTable VALUES (3,1,1)
INSERT INTO @YourTable VALUES (3,1,2)
INSERT INTO @YourTable VALUES (3,1,3)
INSERT INTO @YourTable VALUES (3,2,1)
INSERT INTO @YourTable VALUES (3,2,2)
INSERT INTO @YourTable VALUES (3,2,3)
INSERT INTO @YourTable VALUES (3,3,1)
INSERT INTO @YourTable VALUES (3,3,2)
INSERT INTO @YourTable VALUES (3,3,3)
SET NOCOUNT OFF

DECLARE @PageNumber     int
DECLARE @PageSize       int
DECLARE @SortBy         int

SET @PageNumber=3
SET @PageSize=5
SET @SortBy=1


--SELECT * FROM @YourTable

--Method 1
;WITH PaginatedYourTable AS (
SELECT
    RowID,Value1,Value2,Value3
        ,CASE @SortBy
             WHEN  1 THEN ROW_NUMBER() OVER (ORDER BY Value1 ASC)
             WHEN  2 THEN ROW_NUMBER() OVER (ORDER BY Value2 ASC)
             WHEN  3 THEN ROW_NUMBER() OVER (ORDER BY Value3 ASC)
             WHEN -1 THEN ROW_NUMBER() OVER (ORDER BY Value1 DESC)
             WHEN -2 THEN ROW_NUMBER() OVER (ORDER BY Value2 DESC)
             WHEN -3 THEN ROW_NUMBER() OVER (ORDER BY Value3 DESC)
         END AS RowNumber
    FROM @YourTable
    --WHERE
)
SELECT
    RowID,Value1,Value2,Value3,RowNumber
        ,@PageNumber AS PageNumber, @PageSize AS PageSize, @SortBy AS SortBy
    FROM PaginatedYourTable
    WHERE RowNumber>=(@PageNumber-1)*@PageSize AND RowNumber<=(@PageNumber*@PageSize)-1
    ORDER BY RowNumber



--------------------------------------------
--Method 2
;WITH PaginatedYourTable AS (
SELECT
    RowID,Value1,Value2,Value3
        ,ROW_NUMBER() OVER
         (
             ORDER BY
                 CASE @SortBy
                     WHEN  1 THEN Value1
                     WHEN  2 THEN Value2
                     WHEN  3 THEN Value3
                 END ASC
                ,CASE @SortBy
                     WHEN -1 THEN Value1
                     WHEN -2 THEN Value2
                     WHEN -3 THEN Value3
                 END DESC
         ) RowNumber
    FROM @YourTable
    --WHERE  more conditions here
)
SELECT
    RowID,Value1,Value2,Value3,RowNumber
        ,@PageNumber AS PageNumber, @PageSize AS PageSize, @SortBy AS SortBy
    FROM PaginatedYourTable
    WHERE 
        RowNumber>=(@PageNumber-1)*@PageSize AND RowNumber<=(@PageNumber*@PageSize)-1
        --AND more conditions here
    ORDER BY
        CASE @SortBy
            WHEN  1 THEN Value1
            WHEN  2 THEN Value2
            WHEN  3 THEN Value3
        END ASC
       ,CASE @SortBy
            WHEN -1 THEN Value1
            WHEN -2 THEN Value2
            WHEN -3 THEN Value3
        END DESC

输出:

RowID  Value1 Value2 Value3 RowNumber  PageNumber  PageSize    SortBy
------ ------ ------ ------ ---------- ----------- ----------- -----------
10     2      1      1      10         3           5           1
11     2      1      2      11         3           5           1
12     2      1      3      12         3           5           1
13     2      2      1      13         3           5           1
14     2      2      2      14         3           5           1

(5 row(s) affected

RowID  Value1 Value2 Value3 RowNumber  PageNumber  PageSize    SortBy
------ ------ ------ ------ ---------- ----------- ----------- -----------
10     2      1      1      10         3           5           1
11     2      1      2      11         3           5           1
12     2      1      3      12         3           5           1
13     2      2      1      13         3           5           1
14     2      2      2      14         3           5           1

(5 row(s) affected)

1
fyi,当使用SET SHOWPLAN_ALL ON时,方法1的TotalSubtreeCost为0.08424953,而方法2为0.02627153。方法2改善了三倍以上。
KM。

1
@rexem,方法1和2都使用CTE,它们对页进行分页和排序的方式不同。我不知道为什么这个问题实际是从问题的不同而使得OP链接(在回答由OP这个问题),但我的答案创建工作基础链接代码的OP是指
KM。

1
谢谢,我正在尝试比较旧帖子和此答案。[我不知道如何格式化]这是Tomalak提供的答案。stackoverflow.com/questions/230058?sort=votes#sort-top 这是错误的吗?如果他仅给出答案的一半,我将如何继续使用其性能更好的查询方式?请给我更多的光来进行..谢谢

@Joseph,您提供的链接中的选定答案(stackoverflow.com/questions/230058?sort=votes#sort-top)与提问者在其答案中提供的工作代码不同:stackoverflow.com/ questions / 230058 /…如果您阅读该答案,您将在其答案中看到其代码的链接:pastebin.com/f26a4b403和其Tomalak版本的链接:pastebin.com/f4db89a8e ,我使用以下方法提供每个版本的工作版本表变量
KM。

2
WITH MyCte AS 
(
    select 
       employee_id,
       RowNum = row_number() OVER (order by employee_id)
    from V_EMPLOYEE 
)
SELECT  employee_id
FROM    MyCte
WHERE   RowNum > 0
ORDER BY employee_id

-1
 select salary from (
 select  Salary, ROW_NUMBER() over (order by Salary desc) rn from Employee 
 ) t where t.rn = 2

3
欢迎使用Stack Overflow!尽管此代码段可能是解决方案,但包括说明确实有助于提高帖子的质量。请记住,您将来会为读者回答这个问题,而这些人可能不知道您提出代码建议的原因。
约翰

请在代码段中添加一些上下文,以使将来的读者受益。
DebanjanB
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.