ORDER BY字符串[SQL]中的特定数值

时间:2017-10-20 16:54:34

标签: sql sql-server sql-server-2008

以特定格式提供IDORDER。列具有varchar数据类型,并且始终具有字母值,前面通常为P,后跟三到四个数值。可能甚至后跟下划线或其他字母值。我尝试了很少的选择,没有人回复我想要的东西。

SELECT [ID] FROM MYTABLE
                        ORDER BY
                             (1)   LEN(ID), ID  ASC
                      /      (2)   LEFT(ID,2)
         OPTIONS TRIED       (3)   SUBSTRING(ID,2,4) ASC
                      \      (4)   ROW_NUMBER() OVER (ORDER BY SUBSTRING(ID,2,4))
                             (5)   SUBSTRING(ID,PATINDEX('%[0-9]%',ID),LEN(ID))
                             (6)   LEFT(ID, PATINDEX('%[0-9]%', ID)-1)

选项1似乎最接近我要查找的内容,除非_或字母值遵循数值。参见下面选项1的结果

P100
P208
P218
P301
P305
P306
P4200
P4510
P4511
P4512
P5011
P1400A
P4125H
P4202A
P4507L
P4706A
P1001_2
P2103_B
P4368_RL

想看看..

P100
P208
P218
P301
P305
P306
P1001_2
P1400A
P2103_B
P4125H
P4200
P4202A
P4368_RL
P4507L
P4510
P4511
P4512
P4706A
P5011

4 个答案:

答案 0 :(得分:2)

ORDER BY
  CAST(SUBSTRING(id, 2, 4) AS INT),
  SUBSTRING(id, 6, 3)

http://sqlfiddle.com/#!6/9eecb7db59d16c80417c72d1e1f4fbf1/9464

还有一个比getOnlyNumbers() UDF复杂的,但应对不同长度的数字部分。

CROSS APPLY
(
  SELECT
    tail_start = PATINDEX('%[0-9][^0-9]%', id + '_')
)
  stats
CROSS APPLY
(
  SELECT
    numeric = CAST(SUBSTRING(id, 2, stats.tail_start-1) AS INT),
    alpha   = RIGHT(id, LEN(id) - stats.tail_start)
)
  id_tuple
ORDER BY
  id_tuple.numeric,
  id_tuple.alpha

http://sqlfiddle.com/#!6/9eecb7db59d16c80417c72d1e1f4fbf1/9499

最后,一个可以应付根本没有数字的人(但仍假设第一个角色存在且应该被忽略)

CROSS APPLY
(
  SELECT
    tail_start = NULLIF(PATINDEX('%[0-9][^0-9]%', id + '_'), 0)
)
  stats
CROSS APPLY
(
  SELECT
    numeric = CAST(SUBSTRING(id, 2, stats.tail_start-1) AS INT),
    alpha   = RIGHT(id, LEN(id) - ISNULL(stats.tail_start, 1))
)
  id_tuple
ORDER BY
  id_tuple.numeric,
  id_tuple.alpha

http://sqlfiddle.com/#!6/9eecb7db59d16c80417c72d1e1f4fbf1/9507

答案 1 :(得分:2)

这是一种相当奇怪的排序方式,但现在我理解它,我找到了一个解决方案。我在这里使用表值函数来从字符串中去掉数字。由于函数返回所有数字字符,我还需要检查_并且只传递字符串中的部分。

这是功能。

create function GetOnlyNumbers
(
    @SearchVal varchar(8000)
) returns table as return

    with MyValues as
    (
        select substring(@SearchVal, N, 1) as number
            , t.N
        from cteTally t 
        where N <= len(@SearchVal)
            and substring(@SearchVal, N, 1) like '[0-9]'
    )

    select distinct NumValue = STUFF((select number + ''
                from MyValues mv2
                order by mv2.N
                for xml path('')), 1, 0, '')
    from MyValues mv

此功能正在使用计数表。如果你有一个,你可以略微调整该代码以适应。这是我的理货表。我保留它作为一种观点。

create View [dbo].[cteTally] as

WITH
    E1(N) AS (select 1 from (values (1),(1),(1),(1),(1),(1),(1),(1),(1),(1))dt(n)),
    E2(N) AS (SELECT 1 FROM E1 a, E1 b), --10E+2 or 100 rows
    E4(N) AS (SELECT 1 FROM E2 a, E2 b), --10E+4 or 10,000 rows max
    cteTally(N) AS 
    (
        SELECT  ROW_NUMBER() OVER (ORDER BY (SELECT NULL)) FROM E4
    )
select N from cteTally
GO

接下来我们当然需要一些数据才能运作。在这种情况下,我刚刚创建了一个表变量来表示您的实际表。

declare @Something table
(
    SomeVal varchar(10)
)

insert @Something values
('P100')
, ('P208')
, ('P218')
, ('P301')
, ('P305')
, ('P306')
, ('P4200')
, ('P4510')
, ('P4511')
, ('P4512')
, ('P5011')
, ('P1400A')
, ('P4125H')
, ('P4202A')
, ('P4507L')
, ('P4706A')
, ('P1001_2')
, ('P2103_B')
, ('P4368_RL')

通过我们所有的腿部和设置,我们可以获得完成此任务所需的实际查询。

select s.SomeVal 
from @Something s
cross apply dbo.GetOnlyNumbers(case when charindex('_', s.SomeVal) = 0 then s.SomeVal else left(s.SomeVal, charindex('_', s.SomeVal) - 1) end) x
order by convert(int, x.NumValue)

这将按您在问题中列出的顺序返回行。

答案 2 :(得分:1)

您可以逐步分解ID以提取数字。然后,按数字和ID排序。我喜欢使用CROSS APPLY将长字符串操作分解为多个步骤。你可以内联(它很长)或将其捆绑成内联TVF。

SELECT t.*
FROM MYTABLE t
     CROSS APPLY (SELECT NoP = STUFF(ID, 1, 1, '')) nop
     CROSS APPLY (SELECT FindNonNumeric = LEFT(NoP, ISNULL(NULLIF(PATINDEX('%[^0-9]%', NoP)-1, -1), LEN(NoP)))) fnn
     CROSS APPLY (SELECT Number = CONVERT(INT, FindNonNumeric)) num
ORDER BY Number
       , ID;

答案 3 :(得分:0)

我认为最好的办法是创建一个从字符串中删除数字的函数,如this one,然后按此排序。更好的是,正如@SeanLange建议的那样,将使用该函数将数值存储在新列中并按此排序。