【问题标题】:SQL sort by number contained as substringSQL 按包含为子字符串的数字排序
【发布时间】:2015-05-20 01:13:22
【问题描述】:

我有这样的数据

Name     SortOrder
-------------------
Lower 1      3
Lower 10      2
Lower 2      1
Lower 1%     6 
Lower 1.5%   5
Lower 3%     4
Average      7 
Upper 1      10
Upper 10      8
Upper 1%     12
Upper 1.5%   11
Average       10  /* sorted 
Average Poor  11  * alphabetically
Rich          12  * only */
Min_Low10     17
Min_Low20     18
Min_Low30     19
Min_Up10      20
Min_Up20      21
Min_Up30      22

我想更新排序顺序,这样当我按它排序时,它会是这样的

Name     SortOrder
-------------------
Lower 3%     1
Lower 1.5%   2
Lower 1%     3 
Lower 10      4
Lower 2      5
Lower 1      6
Min_Low30     7
Min_Low20     8
Min_Low10     9
Average       10  /* sorted 
Average Poor  11  * alphabetically
Rich          12  * only */
Min_Up10      13
Min_Up20      14
Min_Up30      15
Upper 1      16
Upper 2      17
Upper 10      18
Upper 1%     19
Upper 1.5%   20

即那些带有数字的子组应该按升序或降序排序(取决于它的下限还是上限),其他没有数字的子组应该简单地按字母顺序排序并放在中间。

我想我按每个子组的第一个字符分组,然后按此转换数字和顺序。但是当我尝试转换它时它给了我一个错误消息:Error converting data type varchar to numeric.

SELECT * 
  ,RowNum =
  CASE 
      WHEN Name LIKE 'Lower %[%]' THEN 
          ROW_NUMBER() over (partition by SUBSTRING(Name,1,5) ORDER By CAST(SUBSTRING(Name,7,LEN(Name)-3) as decimal) DESC)
      WHEN Name LIKE 'Lower %' THEN 
          ROW_NUMBER() over (partition by SUBSTRING(Name,1,5) ORDER By SUBSTRING(Name,7,LEN(Name)) DESC)
      WHEN Name LIKE 'Upper %[%]' THEN 
          (ROW_NUMBER() over (partition by SUBSTRING(Name,1,5) ORDER By SUBSTRING(Name,7,LEN(Name)-3)))
      WHEN Name LIKE 'Upper %' THEN 
          (ROW_NUMBER() over (partition by SUBSTRING(Name,1,5) ORDER By SUBSTRING(Name,7,LEN(Name))))

      ELSE -1
  END
  FROM Table1 
  ORDER BY RowNum

这里是sqlfiddle

编辑:更改了数据值,因为它们包括 1 和 10,如果它们是 varchars,则排序错误。

【问题讨论】:

  • 你能选择SUBSTRING(Name,7,LEN(Name)-3) 看看它给出了什么吗?我猜这不是decimal..
  • 不,它是一个 varchar。这就是为什么我想把它转换成小数
  • 我知道,但是SELECT SUBSTRING(Name,7,LEN(Name)-3) FROM Table1 WHERE Name LIKE 'Lower %[%]' 会返回什么?
  • 我会试试LEN(Name)-1
  • 它只返回数字,所以 3, 2.5, 1

标签: sql-server


【解决方案1】:

你可以试试这个:

select *
    , RowNum =
     ROW_NUMBER() over (order by 
        case 
            when Name LIKE 'Lower %[%]' then 1
            when Name LIKE 'Lower %' then 2
            when Name LIKE 'Min_Low%' then 3
            when Name LIKE 'Min_Up%' then 5
            when Name LIKE 'Upper %[%]' then 7
            when Name LIKE 'Upper %' then 6
            else 4
        end
        , convert(numeric(6,2), case 
            when Name LIKE 'Lower %[%]' then replace(replace(Name, 'Lower ',''),'%', '')
            when Name LIKE 'Lower %' then replace(Name, 'Lower ','')
            when Name LIKE 'Min_Low%' then replace(Name, 'Min_Low','')
        end) desc
        , convert(numeric(6,2), case 
            when Name LIKE 'Min_Up%' then replace(Name, 'Min_Up','')
            when Name LIKE 'Upper %[%]' then replace(replace(Name, 'Upper ',''),'%', '')
            when Name LIKE 'Upper %' then replace(Name, 'Upper ','')
        end)
        , Name
    ) 
from #Table1

更新

这里是更新声明:

update #Table1 set SortOrder = t2.RowNum
from #Table1 t1
  join (
select *
    , RowNum =
     ROW_NUMBER() over (order by 
        case 
            when Name LIKE 'Lower %[%]' then 1
            when Name LIKE 'Lower %' then 2
            when Name LIKE 'Min_Low%' then 3
            when Name LIKE 'Min_Up%' then 5
            when Name LIKE 'Upper %[%]' then 7
            when Name LIKE 'Upper %' then 6
            else 4
        end
        , convert(numeric(6,2), case 
            when Name LIKE 'Lower %[%]' then replace(replace(Name, 'Lower ',''),'%', '')
            when Name LIKE 'Lower %' then replace(Name, 'Lower ','')
            when Name LIKE 'Min_Low%' then replace(Name, 'Min_Low','')
        end) desc
        , convert(numeric(6,2), case 
            when Name LIKE 'Min_Up%' then replace(Name, 'Min_Up','')
            when Name LIKE 'Upper %[%]' then replace(replace(Name, 'Upper ',''),'%', '')
            when Name LIKE 'Upper %' then replace(Name, 'Upper ','')
        end)
        , Name
    ) 
from #Table1) t2 on t1.Name=t2.Name

【讨论】:

  • 谢谢,这似乎适用于输出,但我将如何更新 SortOrder 列以设置为 RowNum?
  • 谢谢,更新几乎成功了。不过最后应该是t1.Name = t2.Name
【解决方案2】:

以下是可用于更新表或直接获取结果的基本 SELECT

使用以下方法修复了问题:

SELECT Name, ROW_NUMBER() over (ORDER By RowNumber) SortOrder
FROM (
SELECT TOP 100 PERCENT Name, ROW_NUMBER() over (ORDER By RowName, Value DESC) RowNumber
FROM (SELECT TOP 100 PERCENT Name,RowName = CASE 
        WHEN Name LIKE 'Lower %[%]%' THEN 'LowerP'
        WHEN Name LIKE 'Lower %' THEN 'LowerS'
        WHEN Name LIKE 'Min_Low%' THEN 'LowerX'
        ELSE 'LowerZ' + Name
    END, Value = CASE 
        WHEN Name LIKE 'Lower %[%]%' THEN CONVERT(float, SUBSTRING(Name, 6, LEN(Name)-6))
        WHEN Name LIKE 'Lower %' THEN CONVERT(float, SUBSTRING(Name, 6, 10))
        WHEN Name LIKE 'Min_Low%' THEN CONVERT(float, SUBSTRING(Name, 8, 10))
        ELSE 100
        END
    FROM Sorting 
    WHERE Name NOT LIKE '%Up%'
    ORDER BY RowName, Value DESC) a
UNION
SELECT TOP 100 PERCENT Name, ROW_NUMBER() over (ORDER By RowName, Value ASC) + 1000 RowNumber 
FROM (
    SELECT TOP 100 PERCENT Name,RowName = CASE 
        WHEN Name LIKE 'Min_Up%' THEN 'UpperA'
        WHEN Name LIKE 'Upper %' AND NOT SUBSTRING(Name, LEN(Name), 1) = '%' THEN 'UpperB'
        WHEN SUBSTRING(Name, LEN(Name), 1) = '%' THEN 'UpperC'
        ELSE 'Middle'
    END, Value = CASE 
        WHEN Name LIKE 'Upper %[%]%' THEN CONVERT(float, SUBSTRING(Name, 6, LEN(Name)-6))
        WHEN Name LIKE 'Upper %' THEN CONVERT(float, SUBSTRING(Name, 6, 10))
        WHEN Name LIKE 'Min_Up%' THEN CONVERT(float, SUBSTRING(Name, 7, 10))
        ELSE 100
        END
FROM Sorting 
WHERE Name LIKE '%Up%'
ORDER BY RowName, Value ASC ) b
ORDER BY RowNumber ) c

【讨论】:

  • 这看起来不错,只是中间的条目没有按字母顺序排序
  • 我刚刚意识到它不适用于我的实际数据集,该数据集具有 Lower 3 和 Lower 30%,它们会被错误地排序。我更新了原帖
  • 添加了一个新版本来克服这些问题
  • 我将 Gabor 的答案标记为正确,因为它似乎更容易阅读。
【解决方案3】:

稍微压缩的版本:

SELECT Name 
FROM (SELECT Name, SUBSTRING(Name, PATINDEX('%[0-9]%', Name), LEN(Name)
                                   - PATINDEX('%[0-9]%', Name)
                                   - PATINDEX('%[0-9]%', REVERSE(Name)) + 2) n FROM @t) t
ORDER BY ROW_NUMBER() OVER 
(ORDER BY CASE WHEN Name LIKE 'lower %[%]' THEN 1
               WHEN Name LIKE 'lower %' THEN 2
               WHEN Name LIKE 'min_low%' THEN 3
               WHEN Name LIKE 'min_up%' THEN 5
               WHEN Name LIKE 'upper %[%]' THEN 7
               WHEN Name LIKE 'upper %' THEN 6
               ELSE 4 END, 
          CASE WHEN Name LIKE 'lower%' OR Name LIKE 'min_low%' 
               THEN CAST(n AS DECIMAL(10, 2)) END DESC, 
          CASE WHEN Name LIKE 'upper%' OR Name LIKE 'min_up%' 
               THEN CAST(n AS DECIMAL(10, 2)) END, 
          Name)

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2013-11-03
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2020-05-14
    • 2019-01-13
    相关资源
    最近更新 更多