【问题标题】:SQL Row_Number() function in Where ClauseWhere 子句中的 SQL Row_Number() 函数
【发布时间】:2010-11-30 19:38:39
【问题描述】:

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

“消息 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

【问题讨论】:

  • ROW_NUMBER() OVER (ORDER BY employee_id) > 0 将始终评估为 TRUE
  • 是的,没错。我不担心这种情况,我可以随时改变。我希望查询首先工作,然后考虑将行号保持在 500 到 800 之间...谢谢
  • @Joseph:你为什么要避免使用 CTE?
  • @rexem - 我不是 SQL Server 专家。我正在尝试在一个大型项目中帮助一个团队,他们面临着很多性能问题。他们正在使用 UDF 和 CTE。在其中一个表中,他们只有 5000 条记录,如果有 5 个用户访问搜索,则需要一分钟以上的时间来检索。有时,它会失败并超时。因此,我试图避免 CTE 和 UDF,并尝试提出一个可以解决性能问题的直接 SQL 查询。
  • 大家好,请查看我在下面发布的链接,该链接以不同的方式使用 row_number() 进行了回答。有人可以将我的初始查询与链接中的查询进行比较吗?感谢帮助..

标签: sql sql-server tsql analytic-functions


【解决方案1】:

要解决此问题,请将您的 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

【讨论】:

  • 我试图避免 CTE。那是我正在寻找的更糟糕的情况。谢谢
  • 如果您使用子查询而不是 CTE,它可能会运行得更快。在某些情况下,我看到性能提高了 1.5 倍
  • CTE SELECT 中也应该有 TOP,否则 SQL 2008 Server 将不会执行查询,因为 ORDER BY(除非使用 TOP,否则不支持)
  • 我正在使用 SQL2005(呃)——我可以通过在 FROM 之后删除“ORDER BY”来避免使用“TOP”。无论如何,它与 OVER 之后的 ( Order By ) 是多余的。
  • 我希望有一种方法可以在没有 CTE 的情况下在 WHERE 子句中使用 ROW_NUMBER() :(
【解决方案2】:
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

【讨论】:

  • @DavideChicco.it:在 SQL Server 中,派生表需要别名(我应该改写为 AS q,但这也可以)。
  • 可读性是我命名别名时的重点。您可以将 rn 写为 RowNumber,将 q 写为 DerivedTable,将 where 子句写为 where DerivedTable.RowNumber > 0。在我看来,当您对代码不熟悉时,这在 6 个月内不会那么混乱。
  • @EdwardComeau:rn 是当今普遍接受的行号首字母缩写词。尝试在谷歌搜索字符串中输入“row_number over as...”,看看它会给你什么建议。
  • @Quassnoi,可读性是良好编码的关键,翻译 rn(或其他缩写别名)的认知努力为您自己和维护您的代码的人加起来。注意,微软第一次击中,SELECT ROW_NUMBER() OVER(ORDER BY SalesYTD DESC) AS Row,...我之前也没有遇到过 rn 所以你在“通用”中的里程可能会有所不同。
  • @Quassnoi,第二次点击,SO 文章 - stackoverflow.com/questions/961007/how-do-i-use-row-number 几个变体,而不是 rn ;-)
【解决方案3】:
Select * from 
(
    Select ROW_NUMBER() OVER ( order by Id) as 'Row_Number', * 
    from tbl_Contact_Us
) as tbl
Where tbl.Row_Number = 5

【讨论】:

    【解决方案4】:

    我想你想要这样的东西:

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

    【讨论】:

    • 如果上述查询对您不起作用,请为该表创建一个别名。修改倒数第二行为From V_EMPLOYEE) A,即添加A为别名。
    【解决方案5】:

    针对 rexem 的 cmets 回答,关于内联视图或 CTE 是否会更快,我重新编写查询以使用我和每个人都可用的表: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 时,反之亦然。

    当然,在你自己的系统上尝试你自己的查询,看看是否有区别。

    此外,where 子句中的row_number() 是 Stack Overflow 上给出的答案中的常见错误。逻辑上 row_number() 在处理 select 子句之前不可用。人们忘记了这一点,当他们在没有测试答案的情况下回答时,答案有时是错误的。 (我自己也曾犯过一项罪名。)

    【讨论】:

    • 谢谢香农。您使用的是什么版本的 SQL Server?
    • 也就是说,该链接中提供的答案是错误的?但是,发布问题的人同意它的工作..令人惊讶.. :-)
    • @Joseph,但是如果您查看 OP 在链接问题中发布的另一个答案,您会看到他链接到的代码版本与接受的答案不同。我不知道他为什么接受答案,即使它不会按输入的方式运行。也许它在被接受后的某个时候被编辑过,也许这足以让他继续前进,即使没有完全正确。
    • @Rexem:SQL Server 2005 和 SQL Server 2008。早期版本不支持 CTE 或 ROW_NUMBER()
    【解决方案6】:

    我觉得所有显示使用 CTE 或 Sub Query 的答案都足以解决此问题,但我没有看到有人深入了解 OP 为何存在问题。 OP建议不起作用的原因是这里的逻辑查询处理顺序:

    1. 来自
    2. 开启
    3. 加入
    4. 在哪里
    5. 分组依据
    6. 带有多维数据集/汇总
    7. 选择
    8. 不同
    9. 订购人
    10. 顶部
    11. 偏移/获取

    我相信这对答案有很大帮助,因为它解释了为什么会出现这样的问题。 WHERE 总是在 SELECT 之前处理,使 CTE 或 Sub Query 成为许多功能所必需的。您将在 SQL Server 中经常看到这一点。

    【讨论】:

      【解决方案7】:
      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
      

      【讨论】:

        【解决方案8】:

        使用 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
        

        【讨论】:

        • 哪一个性能更好?使用 CTE 还是子查询?谢谢
        • 查看香农的答案 - 在他的测试中他们是平等的。
        • 不,它并不快。在SQL ServerCTE 和内联视图中是相同的东西并且具有相同的性能。当在CTE 中使用非确定性函数时,它会在每次调用时重新评估。必须使用肮脏的技巧来强制实现CTE。在我的博客中查看这些文章:explainextended.com/2009/07/28/…explainextended.com/2009/05/28/generating-xml-in-subqueries
        【解决方案9】:

        基于 OP 对问题的回答:

        请查看此链接。它有一个 不同的解决方案,看起来 为提出要求的人工作 题。我试图找出一个 像这样的解决方案。

        Paginated query using sorting on different columns using ROW_NUMBER() OVER () in SQL Server 2005

        ~约瑟夫

        “方法 1”类似于 OP 从链接问题中的查询,“方法 2”类似于来自所选答案的查询。您必须查看此answer 中链接的代码以了解实际情况,因为已修改所选答案中的代码以使其正常工作。试试这个:

        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)
        

        【讨论】:

        • fyi,当使用 SET SHOWPLAN_ALL ON 时,方法 1 的 TotalSubtreeCost 为 0.08424953,而方法 2 为 0.02627153。方法 2 好 3 倍以上。
        • @rexem,方法 1 和 2 都使用 CTE,它们分页和排序行的方式不同。我不确定为什么这个实际问题与 OP 链接到的问题如此不同(在 OP 对这个问题的回答中),但我的答案根据 OP 引用的链接创建了工作代码
        • 谢谢,我正在尝试比较旧帖子和这个答案。 [我不知道如何格式化] 这是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 在我的回答中,我使用表变量提供了每个版本的工作版本
        【解决方案10】:
         select salary from (
         select  Salary, ROW_NUMBER() over (order by Salary desc) rn from Employee 
         ) t where t.rn = 2
        

        【讨论】:

        • 欢迎来到 Stack Overflow!虽然此代码 sn-p 可能是解决方案,但including an explanation 确实有助于提高您的帖子质量。请记住,您是在为将来的读者回答问题,而这些人可能不知道您提出代码建议的原因。
        • 请在代码 sn-p 中添加一些上下文,以方便未来的读者。
        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2014-03-12
        • 2010-10-24
        • 1970-01-01
        • 1970-01-01
        • 2012-03-25
        相关资源
        最近更新 更多