【问题标题】:Dynamic order by without using dynamic sql?不使用动态sql的动态订单?
【发布时间】:2011-03-03 22:56:15
【问题描述】:

我有以下存储过程,可以对 TemplateName、CreatedOn 和 UploadedBy 进行升序和降序排序。以下 SP 运行时不会对记录进行排序。如果我将 2、3、4 替换为列名,则会收到错误消息“将 nvarchar 值 'Test Template' 转换为数据类型 int 时转换失败。”。请建议如何实现排序.

CREATE PROCEDURE [dbo].[usp_SEL_GetRenderingTemplate]
(
  @facilityID INT,
  @sortOrder VARCHAR(5),
  @sortExpression VARCHAR(100),
  @errorCode INT OUTPUT 
)
AS 
BEGIN       
    SET NOCOUNT ON ;
    BEGIN TRY   
        SET @sortOrder = CASE @sortOrder
                           WHEN 'Ascending' THEN 'ASC'
                           WHEN 'Descending' THEN 'DESC'
                           ELSE 'ASC'
                         END
        SELECT  TemplateID,
                TemplateName,
                CreatedOn,
                ( [user].LastName + ' ' + [user].FirstName ) AS UploadedBy
        FROM    Templates
                INNER JOIN [user] ON [user].UserID = Templates.CreatedBy
        WHERE   facilityid = @facilityID
        ORDER BY CASE WHEN @sortExpression = 'TemplateName'
                           AND @sortOrder = 'ASC' THEN 2
                      WHEN @sortExpression = 'CreatedOn'
                           AND @sortOrder = 'ASC' THEN 3
                      WHEN @sortExpression = 'UploadedBy'
                           AND @sortOrder = 'ASC' THEN 4
                 END ASC,
                CASE WHEN @sortExpression = 'TemplateName'
                          AND @sortOrder = 'DESC' THEN 2
                     WHEN @sortExpression = 'CreatedOn'
                          AND @sortOrder = 'DESC' THEN 3
                     WHEN @sortExpression = 'UploadedBy'
                          AND @sortOrder = 'DESC' THEN 4
                END DESC
        SET @errorCode = 0


    END TRY
    BEGIN CATCH
        SET @errorCode = -1                            
        DECLARE @errorMsg AS VARCHAR(MAX)  
        DECLARE @utcDate AS DATETIME   
        SET @errorMsg = CAST(ERROR_MESSAGE() AS VARCHAR(MAX))  
        SET @utcDate = CAST(GETUTCDATE() AS DATETIME)  
        EXEC usp_INS_LogException 'usp_SEL_GetFacilityWorkTypeList',
            @errorMsg, @utcDate  
    END CATCH 


END

【问题讨论】:

    标签: sql sql-server-2005 dynamic-sql


    【解决方案1】:

    动态排序必须是相同的数据类型。这是我用来对三种不同数据类型进行大小写排序的示例——整数、日期(升序和降序)和字符串。这可能不适用于您的情况,但至少您可以看到一些转换为通用数据类型的技术。

    ...

    ORDER BY
        Case Parent.RankTypeID
          When 0 Then dbo.Documents.Rank
          When 1 Then Convert(int, dbo.Documents.DateStart, 112)
          When 2 Then (1 - Convert(int, dbo.Documents.DateStart, 112))
          When 3 Then Cast(dbo.Documents.Title as sql_variant)
        End
    

    注意:

    112 是 YYYYMMDD 的日期格式 - 用于订购。

    【讨论】:

      【解决方案2】:

      我们在我们的一种产品中执行类似的动态排序。 与您的代码唯一真正不同的是,首先,我们不使用实时连接。 我们创建一个临时表,因此我们可以执行分页,然后应用订单。 我们还使用整数进行排序,减少了字符串比较的开销。

      它确实使您的 SQL 更长一些,但这种方法对于查询优化器来说肯定更快。另外,更重要的是,我认为您不能在 Switch 块中混合类型以进行排序,因此要遵循原始代码,您必须同时将所有数据转换为同一时间,这会破坏对象:(

      所以你有

      DECLARE @temp TABLE(ID int identity(1,1), TemplateID int, TemplateName nvarchar(100), CreatedOn datetime, UploadedBy nvarchar(100))
      INSERT INTO @temp(TemplateID, TemplateName, CreatedOn, UploadedBy)
      SELECT TemplateID,
             TemplateName,
             CreatedOn,
             ( [user].LastName + ' ' + [user].FirstName ) AS UploadedBy
      FROM Templates
        INNER JOIN [user] ON [user].UserID = Templates.CreatedBy
      WHERE   facilityid = @facilityID
      

      然后:

      IF @SortOrder = 1 --'ASC'
        BEGIN
           IF @sort = 2
             Select *
             From @Temp
             Order by TemplateName ASC
           ELSE IF @sort = 3
             Select *
             From @Temp
             Order By CreatedBy ASC
           -- and so on...
        END
      
      ELSE -- descending
        BEGIN
          -- Ad. Inf.
        END
      
      Delete
        From @Temp
        WHERE ID < @pageStart or ID > @pageStart + @pageSize
      

      【讨论】:

      • Russ C - 这看起来不是一个非常有效的技术。
      • 通过查询分析器运行它,它高效且易于调试。它还回答了您在下面提供的相同细节,但我没有将日期转换为整数。
      • 与内联排序案例相比,创建一个临时表并有条件地返回其中的部分是否有效?我对此表示怀疑。我怀疑编译器甚至可以得到一个好的查询计划。
      • 我会问 - 除了用于分页的临时表(我不喜欢),您能否提供有关速度的统计信息。意见与事实……
      【解决方案3】:

      以下 SP 运行时不会 排序记录。如果我将 2,3,4 替换为 列名,

      您不应将 ORDER BY 子句中的 2、3、4 替换为列名。当您调用该过程时,只需将要排序的列作为第三个参数传递。

      EXEC [dbo].[usp_SEL_GetRenderingTemplate] 1,'Ascending','CreatedOn',@vErrorCode
      

      CASE 会将查询评估为类似

      ...ORDER BY 3 DESC
      

      按 SELECT 子句中的第 3 列对查询进行排序(即 CreatedOn)

      【讨论】:

        【解决方案4】:

        问题在于,在 CASE 子句中,返回值必须具有相同的数据类型。

        您可以通过使用多个 CASE 语句http://www.extremeexperts.com/sql/articles/CASEinORDER.aspx来解决此问题

        或者将所有内容都转换为相同的数据类型。

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 1970-01-01
          • 2010-11-11
          • 1970-01-01
          • 1970-01-01
          • 2012-04-03
          • 2016-04-17
          • 2011-07-05
          • 2013-12-07
          相关资源
          最近更新 更多