【问题标题】:How to sort varchar string properly with numeric values on both ends?如何使用两端的数值正确排序 varchar 字符串?
【发布时间】:2013-05-29 21:39:51
【问题描述】:

我正在SQL Server 2008 中构建一个Common Table Expression (CTE) 以在PIVOT 查询中使用。

我很难对输出进行正确排序,因为中间存在将字符串数据夹在中间的数值。可以这样做吗?

这是一个快速而肮脏的例子,真正的查询将跨越数年的价值。

例子:

Declare @startdate as varchar(max);
Declare @enddate as varchar(max);
Set @startdate = cast((DATEPART(yyyy, GetDate())-1) as varchar(4))+'-12-01';
Set @enddate = cast((DATEPART(yyyy, GetDate())) as varchar(4))+'-03-15';

WITH DateRange(dt) AS
    (
        SELECT CONVERT(datetime, @startdate) dt
        UNION ALL
        SELECT DATEADD(dd,1,dt) dt FROM DateRange WHERE dt < CONVERT(datetime, @enddate)
    )
    SELECT DISTINCT ',' + QUOTENAME((cast(DATEPART(yyyy, dt) as varchar(4)))+'-Week'+(cast(DATEPART(ww, dt) as varchar(2)))) FROM DateRange

电流输出:

,[2012-Week48]
,[2012-Week49]
,[2012-Week50]
,[2012-Week51]
,[2012-Week52]
,[2012-Week53]
,[2013-Week1]
,[2013-Week10]
,[2013-Week11]
,[2013-Week2]
,[2013-Week3]
,[2013-Week4]
,[2013-Week5]
,[2013-Week6]
,[2013-Week7]
,[2013-Week8]
,[2013-Week9]

期望的输出:

,[2012-Week48]
,[2012-Week49]
,[2012-Week50]
,[2012-Week51]
,[2012-Week52]
,[2012-Week53]
,[2013-Week1]
,[2013-Week2]
,[2013-Week3]
,[2013-Week4]
,[2013-Week5]
,[2013-Week6]
,[2013-Week7]
,[2013-Week8]
,[2013-Week9]
,[2013-Week10]
,[2013-Week11]

编辑

当然,在我发布问题后,我的大脑开始工作。我将 DATEADD 更改为添加 1 周而不是 1 天,然后在选择中取出 DISTINCT 并且它起作用了。

DECLARE @startdate AS VARCHAR(MAX);
DECLARE @enddate AS VARCHAR(MAX);
SET @startdate = CAST((DATEPART(yyyy, GetDate())-1) AS VARCHAR(4))+'-12-01';
SET @enddate = CAST((DATEPART(yyyy, GetDate())) AS VARCHAR(4))+'-03-15';

WITH DateRange(dt) AS
    (
            SELECT CONVERT(datetime, @startdate) dt
            UNION ALL
            SELECT DATEADD(ww,1,dt) dt FROM DateRange WHERE dt < CONVERT(datetime, @enddate)
    )
    SELECT ',' + QUOTENAME((CAST(DATEPART(yyyy, dt) AS VARCHAR(4)))+'-Week'+(CAST(DATEPART(ww, dt) AS VARCHAR(2)))) FROM DateRange

【问题讨论】:

  • 看起来是个糟糕的方法。您从字符串开始,将它们转换为日期时间,运行查询,然后转换回字符串。为什么不从日期时间开始,运行查询并输出格式化的日期,而是按实际日期排序?
  • 因为我使用的数据透视表存储实际日期,但采用修改后的朱利安格式。我需要创建一个跨越数年的按周总计的枢轴。这只是整体解决方案的第一季度,但这正是我所需要的。我不太关心性能,因为这将每月运行一次并在合理的时间内完成。可能有更好的方法来做到这一点,但这就是我现在的解决方案。

标签: sql sql-server-2008 sql-order-by common-table-expression dynamic-pivot


【解决方案1】:

我看不到示例 SQL 代码(该站点在我所在的位置被列入黑名单)。

以下是按正确顺序对数据进行排序的技巧,即先使用长度,然后使用值:

select col
from t
order by left(col, 6), len(col), col;

【讨论】:

  • 这种方法的问题是年份可能不同但长度相同。不过我喜欢这个概念。
  • @sgeddes 。 . .呃。你说的对。这最初将所有 week1 放在一起,无论年份如何。我通过只取字符串的前 6 个字符来解决这个问题。
【解决方案2】:

您是否考虑过对两个临时列进行排序(smallint 中的年和 tinyint 中的周以节省空间......或者如果空间对您来说不是问题并且您更喜欢快速运行,则直接使用 datepart 整数)以及使用“按年、周排序”?

如果您使用更合适的类型(我的建议)存储日期,那么它将变为:

WITH [Define the CTE expression name and column list]
AS
  (
    SELECT CAST(DATEPART(yyyy, dt) as smallint(4)) year, cast(DATEPART(ww, dt) as tinyint(2)) week, [your columns here]
    FROM DateRange WHERE dt < @enddate
  )
[Define the outer query referencing the CTE name]
ORDER BY year, week;
GO

另外,请注意字符串操作会减慢您的查询速度,因此请尽可能避免使用它们!

【讨论】:

    【解决方案3】:

    我喜欢 Gordon 的回答,但如果您一心想要按顺序处理文本:

    ORDER BY     CAST(REPLACE(LEFT('[2012-Week48]',5),'[','')AS INT)
                ,CAST(REPLACE(RIGHT('[2012-Week48]',CHARINDEX('Week','[2012-Week48]')-4),']','') AS INT)
    

    【讨论】:

      【解决方案4】:

      这是另一个将列的开头和结尾部分转换为整数的选项。

      SELECT *
      FROM YourTable
      ORDER BY CAST(SUBSTRING(yourcolumn,1,4) as int), 
        CAST(SUBSTRING(yourcolumn,CHARINDEX('Week',yourcolumn)+4,len(yourcolumn)) as int)
      

      假设数据的格式始终相同,这将起作用。

      【讨论】:

        【解决方案5】:

        由于您使用dt 生成字符串,因此您应该使用日期的部分进行排序:

        WITH DateRange(dt) ...
        SELECT DISTINCT ',' + QUOTENAM...
        ORDER BY DATEPART(yyyy, dt), DATEPART(ww, dt)
        

        【讨论】:

        • 它不喜欢这样,因为我没有在 SELECT 中选择 yyyy 和 ww,它包含一个 DISTINCT。不过,我确实修复了它,已在上面发布。
        【解决方案6】:

        我需要更改查询的DATEADD 部分并删除DISTINCT。一旦更改了顺序,就会自行正确排序

        DECLARE @startdate AS VARCHAR(MAX);
        DECLARE @enddate AS VARCHAR(MAX);
        SET @startdate = CAST((DATEPART(yyyy, GetDate())-1) AS VARCHAR(4))+'-12-01';
        SET @enddate = CAST((DATEPART(yyyy, GetDate())) AS VARCHAR(4))+'-03-15';
        
        WITH DateRange(dt) AS
            (
                    SELECT CONVERT(datetime, @startdate) dt
                    UNION ALL
                    SELECT DATEADD(ww,1,dt) dt FROM DateRange WHERE dt < CONVERT(datetime, @enddate)
            )
            SELECT ',' + QUOTENAME((CAST(DATEPART(yyyy, dt) AS VARCHAR(4)))+'-Week'+(CAST(DATEPART(ww, dt) AS VARCHAR(2)))) FROM DateRange
        

        【讨论】:

          猜你喜欢
          • 2017-04-17
          • 2012-01-20
          • 1970-01-01
          • 2011-08-23
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多