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

如何在SQL Server中对MonthName(varchar)进行排序?,sql,sql-server,Sql,Sql Server,在下面的查询中,我在按顺序排列月份名称时遇到了问题。实际上这里的Period是一个别名,用户可以选择它来查看每日、每周、每月或每年的结果。除本月外,其余案件的分拣工作仍在进行中。因为它只将月份作为字符串,如果我试图将它转换为月份,它就会给出异常 请看一看,给我引路 它是这样开始的: April August December 期望输出: January February March April December 代码: 输出返回时未进行排序: Period Value -------

在下面的查询中,我在按顺序排列月份名称时遇到了问题。实际上这里的
Period
是一个别名,用户可以选择它来查看每日、每周、每月或每年的结果。除本月外,其余案件的分拣工作仍在进行中。因为它只将月份作为字符串,如果我试图将它转换为月份,它就会给出异常

请看一看,给我引路

它是这样开始的:

April
August
December
期望输出:

January
February
March
April
December
代码:

输出返回时未进行排序:

Period     Value
--------------------
April         4750
August        4750
December    187250
February      4000
January      12500
当月排序后的期望输出:

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

中共享示例数据/表格的基本思想是将月份名称转换为月份编号并进行相应排序。 我尝试了一种方法-有多种方法用于检查转换是否有效

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

无法理解代码的工作方式。“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 

不要乱动约会部分的名字

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

如果某个时间段是真实的日期,您可以简单地使用
year()
month()
提取年份和月份,并按两者排序。两者都返回一个数字索引

但解决方案似乎更简单:

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


从您发布的示例中,我认为其列
[MonthOfYear]
具有数字月份索引。你还有
财政部
。因此,请将这些列包含到您的查询中,并使用MonthOfYear,FiscalYear的顺序

我将此作为第二个答案,因为这只是半个笑话:

看看这张桌子_

select * from sys.syslanguages
您可以使用带编号的字符串拆分方法将月份索引获取到给定月份的名称。这允许您动态处理不同的文化:

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';

这里我使用了@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


谢谢你的关心。。添加了输出数据直到等待生成样本输出的样本数据,以及您得到的异常。原理图也很好。感谢@marc_s正确设置问题的格式。我正在尝试从management studio获取数据。添加了示例数据以生成示例输出。是的,这是唯一不允许转换月份的问题。。让我也试试你的方法。你必须将日期的
月()
与其
年相结合,因为你可能希望在1月17日之前得到12月16日。如果我从提供的示例中正确地得到了这一点,则日期表中已经存在所有需要的信息。获取此异常-->Msg 8115,级别16,状态2,第5行算术溢出将表达式转换为数据类型datetime时出错。由于d.id的格式为20170101。。但是如果我把它改成d.DateName,它就会工作。。但是我觉得Shnugo给了这个问题更好的答案。。我要感谢你的支持froxon@Subodh不能将
INT
值20170101强制转换为日期,但可以在将其强制转换为
VARCHAR(8)
后再强制转换。仍然是一种奇怪的方法……我再次尝试了这种方法,发现如果我有2016年的一半数据和2017年的一半数据,它就不起作用了。。由于12月的周数是52..53,并且它又从1月开始,因此它将是1,2。。我添加了脚本来创建表并插入数据以进行验证。您正在使用现有名称构建一个日期,并将其与日期片段(如
'January'+'01 2017'
)组合,以便将其转换为键入的日期?这将在不同的区域性设置中失败。。。顺便说一句:
2017
也必须是动态的,不是吗?嗨,顺戈。。毫无疑问,它已经工作了一个月了。。但如果我们选择Y、W或D的周期,则它不起作用。。这是因为我们按年份和月份分组(@Subodh)它应该很容易创建(连接)每个案例的字符串标准…请详细说明您的想法…您的意思是这样的:-案例期间--当“一月”然后是1--当“二月”然后是2--当“三月”然后是3--当“四月”然后是4--当“五月”然后是5--当“六月”然后是6--当“七月”然后是7--当“八月”然后是8--当“九月”时然后是9号,10号,11号,12号,END@Subodh不,类似于
CASE@Period当'D'然后…当'W'然后…
,就像它一样。但是你必须创建一个字母数字可排序的字符串值(如ISO8601中的日期)。一个想法可能是,在日期表中添加具有永久排序值的列(每种类型的
@Period
)很好,您找到了一个解决方案,但请允许我提示一点:您说谢谢非常感谢,但在(最佳)答案的投票计数器下方勾选接受检查会更友好。这将是1)将此问题标记为已解决2)让追随者更容易找到最佳解决方案3)回答者获得积分4)你获得积分。因为你自己已经越过了15点的边界,你被要求对捐款进行投票。这就是说谢谢的方式。快乐编码!是的,Shnugo,我想这么做。。但在这里,我在两个回复中找到了答案。。所以我认为如果我选择了一个,对其他人来说是不公平的。。但我还是这么做了,因为理论上你的答案更正确。。
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
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