【问题标题】:Passing dynamic order by in stored procedure在存储过程中传递动态顺序
【发布时间】:2013-02-26 09:11:16
【问题描述】:

我正在创建下面的存储过程。

declare @PageNum as Int
declare @PerPageResult as Int
declare @StartDate as varchar(25)
declare @EndDate as varchar(25)
declare @SortType as Varchar(50)
declare @SortDirection as Varchar(4)
set @PageNum=1
set @PerPageResult=20
set @StartDate='2008-02-08'
set @EndDate='2015-02-08'
set @SortType='RegDate'
set @SortDirection='Desc'
declare @Temp Table(RowNum int, RegDate Date, Registered int, Female int, Male int, [Join] int, Rebill int, TotalPointsEarned int, Expire int)
declare @sort varchar(50)
Insert into @Temp
    Select ROW_NUMBER() over (order by @SortType+' '+@SortDirection) As RowNum, * From (    
    SELECT 
    CAST(m.registrationdate AS Date) as RegDate,    
    count(m.id) Registered,
    count(CASE WHEN m.gender='F' then 'F' end) As Female,
    count(CASE WHEN m.gender='M' then 'M' end) As Male
    count(CASE WHEN p.paymenttransactiontype='N' then 'N' end) As [Join],
    count(CASE WHEN p.paymenttransactiontype='R' then 'R' end) As Rebill,
    count(m.tokensearned) As TotalPointsEarned,
    count(CASE WHEN p.paymenttransactiontype='E' then 'E' end) As Expire
    from member m
    join payment p on m.id=p.id_member
    join user_role u on u.member_id=m.id
    where u.role_id <> 3
    and CAST(m.registrationdate AS Date) > @StartDate and CAST(m.registrationdate AS Date) < @EndDate
    GROUP BY CAST(m.registrationdate AS Date)
    ) as aa 
    Select * from @Temp Where RowNum>((@PageNum-1)*@PerPageResult) and RowNum<=@PerPageResult * @PageNum
    Order by @SortType+' '+@SortDirection

在上面当我动态传递Order by 子句时,它没有正确排序数据,但是当我显式地写列名时,它工作正常。可能是将@SortType+' '+@SortDirection 视为varchar 而不是Date

我尝试写Order by case when (@Sort='RegDate' and @SortDirection='Desc') Then RegDate End Desc,但没有成功

如何在这里动态传递订单。

编辑: @Andomar:我尝试了您提供的解决方案,并为日期类型添加了一个字段。而且也没用。

下面是我做的。

create table t1 (id int, name varchar(50), dt date);
insert t1 values 
    (1, 'Chihiro Ogino','2009-02-08'), 
    (2, 'Spirit of the Kohaku River','2008-02-08'), 
    (3, 'Yubaba','2012-02-08');



declare @sortColumn varchar(50) = 'dt'
declare @sortOrder varchar(50) = 'ASC'

select  *
from    t1
order by
        case
        when @sortOrder <> 'ASC' then 0
        when @sortColumn = 'id' then id
        end ASC
,       case
        when @sortOrder <> 'ASC' then ''
        when @sortColumn = 'name' then name
        end ASC
,       case
        when @sortOrder <> 'ASC' then ''
        when @sortColumn = 'dt' then name
        end ASC
,       case
        when @sortOrder <> 'DESC' then 0
        when @sortColumn = 'id' then id
        end DESC
,       case
        when @sortOrder <> 'DESC' then ''
        when @sortColumn = 'name' then name
        end DESC
,       case
        when @sortOrder <> 'DESC' then ''
        when @sortColumn = 'dt' then name
        end DESC

【问题讨论】:

  • gender='SH':以前没见过...
  • @MitchWheat:忽略它:P
  • 搜索“ORDER BY CASE”
  • 而不是大小写,我们有更好的解决方案,因为我有 11 列用于排序类型。
  • @archangel76:在客户端排序没有帮助,假设您在数据库中有 10000 条记录,并且一次显示 10 条。客户端排序只会发生在这 10 条记录中,这甚至是不正确的。

标签: sql sql-server sql-server-2008-r2


【解决方案1】:

您可以使用复杂的order by 子句。这需要一个case 用于每个排序方向和每个数据类型。使用此示例数据集:

create table t1 (id int, name varchar(50), created date);
insert t1 values 
    (1, 'Chihiro Ogino', '2012-01-01'), 
    (2, 'Spirit of the Kohaku River', '2012-01-03'), 
    (3, 'Yubaba', '2012-01-02');

您可以使用order by 子句,例如:

declare @sortColumn varchar(50) = 'created'
declare @sortOrder varchar(50) = 'DESC'

select  *
from    t1
order by
        case
        when @sortOrder <> 'ASC' then 0
        when @sortColumn = 'id' then id
        end ASC
,       case
        when @sortOrder <> 'ASC' then ''
        when @sortColumn = 'name' then name
        end ASC
,       case
        when @sortOrder <> 'ASC' then cast(null as date)
        when @sortColumn = 'created' then created
        end ASC
,       case
        when @sortOrder <> 'DESC' then 0
        when @sortColumn = 'id' then id
        end DESC
,       case
        when @sortOrder <> 'DESC' then ''
        when @sortColumn = 'name' then name
        end DESC
,       case
        when @sortOrder <> 'DESC' then cast(null as date)
        when @sortColumn = 'created' then created
        end DESC

Working example at SQL Fiddle.

另一种选择是动态创建查询,并使用exec 运行它。例如:

declare @sql nvarchar(max)
set @sql = 'select * from YourTable order by ' + @sortColumn + ' ' + @sortDir
exec (@sql)

【讨论】:

  • 而不是大小写,我们有更好的解决方案,因为我有 11 列用于排序类型。
  • 我尝试在 (@Sort='RegDate' 和 @SortDirection='Desc') Then RegDate End Desc 时按大小写排序,但没有成功
  • @JitendraPancholi:如果变量设置正确,应该可以。
  • 您可以查看我上面的查询。它没有正确排序。
  • 需要注意的是动态SQL是一个注入漏洞。如果参数由客户端控制,则不要使用动态 SQL。即使只是多选,如果字符串是从客户端发送的,他们可以使用自定义 POST 来更改发送的字符串。
【解决方案2】:

@Andomar 的回答有助于解决类似的问题。我需要以任意顺序对任意数量的 23 个不同的列进行排序。我最终得到以下结果:

create table sorting(ID int, columnName varchar(50), sort varchar(10), position int)
insert into sorting 
values(1,'column1','DESC',1),
      (1,'column2','ASC',2),
      ...              
      (1,'columnN','DESC',N)

在SP中添加参数@sort,以识别排序中的条目:

ORDER BY ISNULL(STUFF((SELECT ', ' + a.columnName + ' ' +  a.sort 
                    FROM sorting a
                    WHERE a.ID = @sort
                    ORDER BY a.position ASC
            FOR XML PATH('')), 1, 2, ''),NULL)

【讨论】:

  • 多列排序的好主意
【解决方案3】:

有两种构建动态可排序存储过程的基本方法:

  1. ORDER BY 子句作为参数传递给存储过程。在存储过程中,在字符串中构建 SQL 语句,然后使用EXECsp_ExecuteSql 执行该语句。

    -- This Method is used when your Column names are dynamic 
    -- We need to create a dynamic query and Execute it as shown below.
    
    CREATE PROCEDURE getEmployees ( @OrderByClause varchar(100) ) AS
    
    -- Create a variable @SQLStatement
    DECLARE @SQLStatement varchar(255)
    
    -- Enter the dynamic SQL statement into the
    -- variable @SQLStatement
    SELECT @SQLStatement =  'SELECT EmployeeID, FirstName, LastName, SSN, Salary  
     FROM Employees ORDER BY '+ @OrderByClause+''
    
    -- Execute the SQL statement
    EXEC(@SQLStatement)
    
  2. 传入要排序的列,然后在ORDER BY 子句中使用CASE 语句根据输入参数值对结果进行排序。

    --This method is used when you column name is not dynamic 
    
    SELECT EmployeeID, FirstName, LastName, SSN, Salary
    FROM Employees
    ORDER BY
      CASE WHEN @ColumnName='LastName' THEN LastName
           WHEN @ColumnName='Salary' THEN CONVERT(varchar(50), Salary)
           WHEN @ColumnName='SSN' THEN SSN
      END
    

【讨论】:

  • 你至少应该把这个网站从4guysfromrolla.com/webtech/010704-1.shtml
  • 不完整,没有解释动态排序方向。
  • Salary 列是一个数字,CONVERT 将使其左对齐。你可能需要做 RIGHT(SPACE(20) + FORMAT(Salary, '#.00'), 20)
【解决方案4】:

这样的事情应该可以工作:

ORDER BY
  CASE WHEN @SortDirection = 'ASC'  THEN @SortType END ASC,
  CASE WHEN @SortDirection = 'DESC' THEN @SortType END DESC

【讨论】:

  • 通过SortType 订购将不起作用。 SQL Server 不允许使用变量代替列名。
  • 而不是大小写,我们有更好的解决方案,因为我有 11 列用于排序类型。
  • @Andomar 不,Sql server 2008 r2 接受它,我刚试过(只是忘了把@放在名字的第一个)
  • @JitendraPancholi 您希望它们在 SortDirection 中彼此不同吗?
  • @MaryamArshi:它会接受它,但它不会像你期望的那样工作。它将根据@SortType 的常量值进行排序,即根本不会排序
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2017-04-03
  • 1970-01-01
  • 1970-01-01
  • 2011-07-16
相关资源
最近更新 更多