如何在SQL Server中对MonthName(varchar)进行排序?

时间:2017-08-20 07:17:10

标签: sql sql-server

我在按下面的查询顺序排序月份名称时遇到困难。实际上,此处Period是别名,如果用户希望每天,每周,每月或每年查看结果,则可以选择该别名。排序适用于除月份以外的其他情况。由于它只将Month作为字符串,如果我尝试将其转换为月份,则会给出异常。

请看一看并指导我。

开始返回如:

April
August
December

期望的输出:

January
February
March
April
December

代码:

Declare @Period char = 'M'
Declare @FinalDateId int = 20170101;

SELECT
    Period, SUM(CAST(TotalAmount AS bIGINT)) AS Value
FROM            
    (SELECT        
         CASE 
            WHEN @Period = 'D' 
               THEN CAST(d.DateName AS VARCHAR(50)) 
            WHEN @Period = 'W' 
               THEN CAST(d.WeekOfYear AS VARCHAR(50))
            WHEN @Period = 'M' 
               THEN CAST(d.MonthName AS VARCHAR(50)) 
            WHEN @Period = 'Y' 
               THEN CAST(d.CalendarYear AS VARCHAR(50)) 
         END AS Period, 
         Tr.TotalAmount
     FROM 
         Revenue AS Tr 
     INNER JOIN  
         Dates AS d ON Tr.DateId = d.Id
     WHERE        
         (Tr.DateId BETWEEN 
                    CASE 
                        WHEN @Period = 'D' 
                           THEN CONVERT(varchar, CONVERT(datetime, CONVERT(char(8), @FinalDateId)) - 30, 112) 
                        WHEN @Period = 'W' 
                           THEN CONVERT(varchar, CONVERT(datetime, CONVERT(char(8), @FinalDateId)) - 364, 112) 
                        WHEN @Period = 'M' 
                           THEN CONVERT(varchar, CONVERT(datetime, CONVERT(char(8), @FinalDateId)) - 365, 112) 
                        WHEN @Period = 'Y' 
                           THEN CONVERT(varchar, CONVERT(datetime, CONVERT(char(8), @FinalDateId)) - 3652, 112) END AND @FinalDateId) 
         ) AS TotalRevenue
GROUP BY 
    Period
ORDER BY
    CASE 
       WHEN @Period = 'M' 
          THEN CONVERT(month, Period) 
    END

输出没有排序返回:

Period     Value
--------------------
April         4750
August        4750
December    187250
February      4000
January      12500

对月份进行排序后的所需输出:

Period      Value 
-----------------------
January     12500
February     4000
April        4750
August       4750
December   187250

https://1drv.ms/u/s!Ak02JpVbvOzehy0Hs2r8Rxh6mhSR

分享样本数据/表格

5 个答案:

答案 0 :(得分:1)

无法理解代码的工作原理。 “CONVERT(month,Period)”无效(至少在我的sql server机器中它说“mont不是系统数据类型”)。

除此之外,我认为最好的解决方案是订购另一个字段:

       CASE 
            WHEN @Period = 'D' 
               THEN DATEPART(DAY,d.id)
            WHEN @Period = 'W' 
               THEN DATEPART(WEEK,d.id)
            WHEN @Period = 'M' 
               THEN DATEPART(MONTH,d.id) 
            WHEN @Period = 'Y' 
               THEN DATEPART(YEAR,d.id) 
         END AS PeriodNumber 

答案 1 :(得分:1)

不要乱用日期部分的名称

使用月份名称和某个日期片段构建完整日期的任何方法都会失败,如果执行系统有不同的话,将会失败,以获取月份的号码语言。如果您的应用程序可能在国际环境中运行,那么您必须找到一种方法来处理它而不会出现问题。

如果期间是某个地方的实际日期,您可以简单地使用YEAR()MONTH()来提取年份和月份,并按两者排序。两者都带有数字索引。

但解决方案似乎更简单:

显然你正在使用日期表(这是一件非常好的事情!)

从我发布的示例中我看到,其列[MonthOfYear]已获得数字月份索引。你也得到了FiscalYear。因此,请在查询中包含这些列,然后使用ORDER BY FiscalYear,MonthOfYear

答案 2 :(得分:1)

我把它作为第二个答案,因为这是一个笑话的一半:

查看此表_

select * from sys.syslanguages

您可以使用带编号的 string-split-approach 来获取给定月份名称的月份索引。这使您可以动态地处理不同的文化:

SELECT ROW_NUMBER() OVER(ORDER BY (SELECT NULL)) AS MonthIndex
      ,B.m.value('text()[1]','nvarchar(max)') AS MonthName
FROM sys.syslanguages AS l
CROSS APPLY(SELECT CAST('<x>' + REPLACE(months,',','</x><x>') + '</x>' AS XML) AS Casted) AS A
CROSS APPLY A.Casted.nodes('/x') AS B(m)
WHERE l.name='us_english';

但我的强烈建议是:不要这样做:-D

Inx Name
------------
1   January
2   February
3   March
4   April
5   May
6   June
7   July
8   August
9   September
10  October
11  November
12  December

答案 3 :(得分:1)

在这里,我使用了@froxon和@Shnugo提供的评论。我非常感谢那些帮助我做出最终答案的人们。

Declare @Period char = 'M'
Declare @FinalDateId int = 20170101;

SELECT
    Period, SUM(CAST(TotalAmount AS bIGINT)) AS Value
FROM            
    (SELECT        
         CASE 
            WHEN @Period = 'D' 
               THEN CAST(d.DateName AS VARCHAR(50)) 
            WHEN @Period = 'W' 
               THEN CAST(d.WeekOfYear AS VARCHAR(50))
            WHEN @Period = 'M' 
               THEN CAST(d.MonthName AS VARCHAR(50)) 
            WHEN @Period = 'Y' 
               THEN CAST(d.CalendarYear AS VARCHAR(50)) 
         END AS Period, 
         Tr.TotalAmount,
         d.CalendarYear,
        CASE 
            WHEN @Period = 'D' 
               THEN DATEPART(DAY,d.DateName)
            WHEN @Period = 'W' 
               THEN DATEPART(WEEK,d.DateName)
            WHEN @Period = 'M' 
               THEN DATEPART(MONTH,d.DateName) 
            WHEN @Period = 'Y' 
               THEN DATEPART(YEAR,d.DateName) 
         END AS PeriodNumber 
     FROM 
         Revenue AS Tr 
     INNER JOIN  
         Dates AS d ON Tr.DateId = d.Id
     WHERE        
         (Tr.DateId BETWEEN 
                    CASE 
                        WHEN @Period = 'D' 
                           THEN CONVERT(varchar, CONVERT(datetime, CONVERT(char(8), @FinalDateId)) - 30, 112) 
                        WHEN @Period = 'W' 
                           THEN CONVERT(varchar, CONVERT(datetime, CONVERT(char(8), @FinalDateId)) - 364, 112) 
                        WHEN @Period = 'M' 
                           THEN CONVERT(varchar, CONVERT(datetime, CONVERT(char(8), @FinalDateId)) - 365, 112) 
                        WHEN @Period = 'Y' 
                           THEN CONVERT(varchar, CONVERT(datetime, CONVERT(char(8), @FinalDateId)) - 3652, 112) END AND @FinalDateId) 
         ) AS TotalRevenue
GROUP BY 
    Period,
    PeriodNumber,
    CalendarYear
Order by 
    CalendarYear,
    PeriodNumber

答案 4 :(得分:0)

基本思路是将月份名称转换为月份,并相应地进行排序。 我试过一种方法,如果它有效,有多种转换检查方法。

Declare @Period char = 'M'
Declare @FinalDateId int = 20170101;

SELECT
    Period, SUM(CAST(TotalAmount AS bIGINT)) AS Value
FROM            
    (SELECT        
         CASE 
            WHEN @Period = 'D' 
               THEN CAST(d.DateName AS VARCHAR(50)) 
            WHEN @Period = 'W' 
               THEN CAST(d.WeekOfYear AS VARCHAR(50))
            WHEN @Period = 'M' 
               THEN CAST(d.MonthName AS VARCHAR(50)) 
            WHEN @Period = 'Y' 
               THEN CAST(d.CalendarYear AS VARCHAR(50)) 
         END AS Period, 
         Tr.TotalAmount
     FROM 
         Revenue AS Tr 
     INNER JOIN  
         Dates AS d ON Tr.DateId = d.Id
     WHERE        
         (Tr.DateId BETWEEN 
                    CASE 
                        WHEN @Period = 'D' 
                           THEN CONVERT(varchar, CONVERT(datetime, CONVERT(char(8), @FinalDateId)) - 30, 112) 
                        WHEN @Period = 'W' 
                           THEN CONVERT(varchar, CONVERT(datetime, CONVERT(char(8), @FinalDateId)) - 364, 112) 
                        WHEN @Period = 'M' 
                           THEN CONVERT(varchar, CONVERT(datetime, CONVERT(char(8), @FinalDateId)) - 365, 112) 
                        WHEN @Period = 'Y' 
                           THEN CONVERT(varchar, CONVERT(datetime, CONVERT(char(8), @FinalDateId)) - 3652, 112) END AND @FinalDateId) 
         ) AS TotalRevenue
GROUP BY 
    Period
ORDER BY
    CASE 
       WHEN @Period = 'M' 
          THEN DATEPART(MM,convert(datetime,Period +'01 2017',110))
   END

output:-

Period     Value
January     12500
February    4000
March       2300
April       4750
May         8560
June        4400
July        4550
August      4750
September   5960
October     7350
November    5250
December    187250