简体   繁体   English

如何使用两端的数值正确排序varchar字符串?

[英]How to sort varchar string properly with numeric values on both ends?

I'm building a Common Table Expression (CTE) in SQL Server 2008 to use in a PIVOT query. 我正在SQL Server 2008构建一个Common Table Expression (CTE)以在PIVOT查询中使用。

I'm having difficulty sorting the output properly because there are numeric values that sandwich the string data in the middle. 我无法正确排序输出,因为有数字值将字符串数据夹在中间。 Is it possible to do this? 是否有可能做到这一点?

This is a quick and dirty example, the real query will span several years worth of values. 这是一个快速而肮脏的例子,真正的查询将跨越几年的价值。

Example: 例:

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

Current Output: 电流输出:

,[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]

Desired Output: 期望的输出:

,[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]

EDIT 编辑

Of course after I post the question my brain started working. 当然,在我发布问题后,我的大脑开始工作了。 I changed the DATEADD to add 1 week instead of 1 day and then took out the DISTINCT in the select and it worked. 我更改了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

I can't see the sample SQL code (that site is blacklisted where I am). 我看不到示例SQL代码(该网站被列入黑名单我所在的位置)。

Here is a trick for sorting that data in the proper order is to use the length first and then the values: 以下是按正确顺序排序数据的技巧,首先使用长度,然后使用值:

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

Have you considered to sort on two temporary columns (year in smallint and week in tinyint to save space … or directly using the datepart integer if space is not a problem to you and you prefer fast run) along with the use of "order by year, week" ? 您是否考虑过对两个临时列进行排序(smallint中的年份和tinyint中的一周以节省空间...或者直接使用datepart整数,如果空间对您来说不是问题而您更喜欢快速运行)以及使用“按年排序” ,一周“?

If you store dates using a more suitable type (what I suggest), it would then become : 如果您使用更合适的类型(我建议的)存储日期,它将变为:

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

Also, please note that string operations will slow your queries so avoid them when possible ! 此外,请注意字符串操作会减慢您的查询速度,以便尽可能避免使用它们!

I like Gordon's answer, but if you were hell-bent on text manipulation in your order by: 我喜欢戈登的答案,但是如果你对你的订单中的文字处理感到害怕:

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

Here is another option converting the beginning and ending parts of the column to integer. 这是另一个选项,将列的开头和结尾部分转换为整数。

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

This will work assuming the format of the data is always the same. 假设数据格式始终相同,这将起作用。

Since you are using dt to generate the string, you should sort by using the date's parts: 由于您使用dt生成字符串,因此您应该使用日期的部分进行排序:

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

I needed to change the DATEADD portion of the query and remove the DISTINCT . 我需要更改查询的DATEADD部分并删除DISTINCT Once changed the order sorted properly on it's own 一旦更改,订单就会自行排序

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

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM