【问题标题】:Dynamic order by in Paging SQL Query分页 SQL 查询中的动态排序依据
【发布时间】:2017-09-04 18:49:31
【问题描述】:

我想在存储过程中使用分页和动态orderby 子句(多于一列)。我确实尝试过,但给了我一个错误

看来我不能使用 Row_number() 而不是 rank()

窗口函数不能在另一个窗口函数或聚合的上下文中使用。

除了 linq to sql 之外,有没有其他方法可以实现这一目标

SELECT  [t8].[AssetId], 
            [t8].[WorkOrderId], 
            [t8].[IssueDescription] AS [WorkOrderDescription], 
            [t8].[value] AS [Type], 
            [t8].[WorkOrderStatusTypeName] AS [Status],
            [t8].[value2] AS [StartDate], 
            [t8].[CompletedDate] AS [CompleteDate], 
            [t8].[value22] AS [CompletedBy], 
            ISNULL([t8].[value3],0) AS [Hours]
    FROM    (
                SELECT  ROW_NUMBER() 
                    OVER (ORDER BY  CASE WHEN @sortColumnName = 'default' THEN (RANK() over( order by [t7].[WorkOrderStatusTypeId] ASC, [t7].[WorkOrderId])) END ,
                                    CASE WHEN @sortColumnName = 'WorkOrderId' AND @sortOrder = 'asc' THEN [t7].[WorkOrderId] END ASC,
                                    CASE WHEN @sortColumnName = 'WorkOrderId' AND @sortOrder = 'desc' THEN [t7].[WorkOrderId] END DESC
                    ) AS [ROW_NUMBER],  
                    [t7].[AssetId], 
                    [t7].[WorkOrderId], 
                    [t7].[IssueDescription], 
                    [t7].[value], 
                    [t7].[WorkOrderStatusTypeName], 
                    [t7].[value2], 
                    [t7].[CompletedDate],
                    [t7].[value22], 
                    [t7].[value3]
            from    --Different tables      
            ) as t8     
    WHERE       [t8].[ROW_NUMBER] BETWEEN ((@pageIndex-1) * @pageSize)+ 1 AND @pageIndex * @pageSize
    ORDER BY    [t8].[ROW_NUMBER]

【问题讨论】:

    标签: sql-server-2008 linq-to-sql


    【解决方案1】:

    二手

    SELECT  CASE    
            WHEN @sortColumnName ='default' AND @sortOrder = 'asc'  then row_number() over (order by [t7].[WorkOrderStatusTypeId], [t7].[CompletedDate] ASC) 
            WHEN @sortColumnName ='WorkOrderId' AND @sortOrder = 'asc'  then row_number() over (order by [t7].[WorkOrderId] ASC) 
            WHEN @sortColumnName ='WorkOrderId' AND @sortOrder = 'desc' then row_number() over (order by [t7].[WorkOrderId] DESC)
            END AS [ROW_NUMBER], 
    

    而不是

    SELECT  ROW_NUMBER() 
            OVER (ORDER BY  CASE WHEN @sortColumnName = 'default' THEN (RANK() over( order by [t7].[WorkOrderStatusTypeId] ASC, [t7].[WorkOrderId])) END ,
                            CASE WHEN @sortColumnName = 'WorkOrderId' AND @sortOrder = 'asc' THEN [t7].[WorkOrderId] END ASC,
                            CASE WHEN @sortColumnName = 'WorkOrderId' AND @sortOrder = 'desc' THEN [t7].[WorkOrderId] END DESC
            ) AS [ROW_NUMBER],
    

    不能将 RowNumber() 和 Rank() 等嵌套窗口函数放在一起,而是可以在 case 语句中使用。

    【讨论】:

      【解决方案2】:

      似乎您希望默认顺序为两列,并且您尝试使用 RANK() 试图将两个默认排序条件放在一个 CASE 中。

      显然,这是不可能的。一个明显的选择似乎是将两列分成两个 CASE,重复 @sortColumnName = 'default' 条件:

      ROW_NUMBER() 
      OVER (ORDER BY  CASE WHEN @sortColumnName = 'default' THEN [t7].[WorkOrderStatusTypeId] END ASC,
                      CASE WHEN @sortColumnName = 'default' THEN [t7].[WorkOrderId] END ASC,
                      CASE WHEN @sortColumnName = 'WorkOrderId' AND @sortOrder = 'asc' THEN [t7].[WorkOrderId] END ASC,
                      CASE WHEN @sortColumnName = 'WorkOrderId' AND @sortOrder = 'desc' THEN [t7].[WorkOrderId] END DESC
      

      另一方面,很容易看出,第二个和第三个 CASE 可以合并为一个,因为当相应的条件为真时,两者都返回相同的表达式。所以,你可以这样做:

      ROW_NUMBER() 
      OVER (ORDER BY  CASE WHEN @sortColumnName = 'default' THEN [t7].[WorkOrderStatusTypeId] END ASC,
                      CASE WHEN @sortColumnName = 'default' OR @sortColumnName = 'WorkOrderId' AND @sortOrder = 'asc' THEN [t7].[WorkOrderId] END ASC,
                      CASE WHEN @sortColumnName = 'WorkOrderId' AND @sortOrder = 'desc' THEN [t7].[WorkOrderId] END DESC
      

      如您所见,第二个 CASE 用于设置WorkOrderId ASC 的排序,无论是在参数中明确指定该列还是指定'default' 时。

      【讨论】:

      • +1 ,嵌套窗口函数中的 order by 子句中存在多个列的问题
      • 感谢您的反馈并感谢您发布您自己的可能答案版本。不过,我认为您应该考虑将其从问题中删除并将其重新发布为答案,因为可能会发生您最终实际使用它的情况,在这种情况下,如果您接受自己的建议,那绝对可以适合您的答案。
      • 谢谢 Andriy M,我不知道这个
      【解决方案3】:

      创建 Web API 时,进行动态排序的唯一方法,尤其是在多个 colmun 上,是构建 Sql 字符串并使用内置的 sprop 'SP_EXECUTESQL' 运行它。下面是我的解决方案示例,它返回一个分页列表,按多列字符串排序,并通过输出参数返回总行数。

      请注意,这样使用字符串直接拼接时,一定要先检查sql注入恶意代码的参数。这里我必须检查 SearchTerm 和 SortTerm

      alter PROCEDURE [dbo].[usp_Ranking_SearchForPlayerByName]
          @SearchTerm varchar(100),
          @WeekNumber int,
          @Gender nchar(1),
          @SortTerm nvarchar(100),
          @PageSize int,
          @PageNumber int,
          @TotalCount int OUT
      AS
      BEGIN
          SET ANSI_NULLS, QUOTED_IDENTIFIER ON
      
          DECLARE @PreviousWeek int = dbo.PreviousWeekNo(@WeekNumber)
          DECLARE @Offset int = (@PageNumber - 1) * @PageSize
      
      
      
          DECLARE @sql nvarchar(max) = N'Select 
              h.PlayerID
              ,p.Forename as FirstName
              ,p.Surname as Lastname
              ,p.Gender as Gender
              ,''return country of residence'' as Country
              ,r.Name as Region
              ,h.Position as RankThisWeek
              ,previousWk.Position as RankLastWeek
              ,(previousWk.Position - h.Position) as Change
          From PlayerHistories h
              left outer join
                  (Select * from PlayerHistories
                      where WeekNumber = @PreviousWeek) previousWk on h.PlayerId = previousWk.PlayerId
              inner join Players p on h.PlayerId = p.Id
                  left join Countries c on p.CountryId = c.Id
                  left join Regions r on c.RegionId = r.Id
          Where h.WeekNumber = @WeekNumber And
              p.Gender = CASE When @Gender = ''M'' Then ''M'' When @Gender = ''F'' Then ''F'' Else p.gender End And
              p.FullName like ''%' + @SearchTerm + '%'' 
          Order by ' + @SortTerm + '
          OFFSET @offset ROWS FETCH NEXT @PageSize ROWS ONLY
      
          '   
          EXEC SP_EXECUTESQL @Sql,
              N'@PreviousWeek int, @WeekNumber int, @Gender nchar(1), @offset int, @PageSize int', 
              @PreviousWeek, @WeekNumber, @Gender, @offset, @PageSize;
      
          SELECT @TotalCount = 
              (SELECT Count(h.Id)
              From PlayerHistories h
                  left outer join
                      (Select * from PlayerHistories
                          where WeekNumber = @PreviousWeek) previousWk on h.PlayerId = previousWk.PlayerId
                  inner join Players p on h.PlayerId = p.Id
              Where h.WeekNumber = @WeekNumber And
                  p.Gender = CASE When @Gender = 'M' Then 'M' When @Gender = 'F' Then 'F' Else p.gender End And
                  p.FullName like '%' + @SearchTerm + '%');
      
      
      END
      

      使用以下语句测试调用它:

      DECLARE @op int
      
      exec dbo.usp_Ranking_SearchForPlayerByName 'smith',201715,'','LastName DESC, Gender, FirstName',10,1, @op OUTPUT
      
      select @op
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2021-05-29
        • 1970-01-01
        • 2013-09-14
        • 1970-01-01
        相关资源
        最近更新 更多