2017-10-20 180 views
1

有一列ID,我想ORDER以特定的格式。列的数据类型为varchar,并且始终具有字母值,前面通常为P,后面跟着三到四个数字值。可能甚至紧跟着一个下划线或另一个字母值。我尝试过很少的选择,没有人回报我的愿望。ORDER BY字符串中的具体数值[SQL]

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 
+1

问题的根源在于您在单个列中有多条信息。这违反了1NF并导致这样的问题。 –

+1

你实际上并没有清楚订单应该是什么。 – MatBailie

+0

@MatBailie我只是编辑,以显示所需的结果正确,因为你键入:) – DRUIDRUID

回答

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

+0

如果'ID'只有3个数字值在前面的'P'之后,这不会正确排序。示例返回。 'P100,P1001,P101' – DRUIDRUID

+0

@DRUIDRUID好点。对不起,我在我的手机上,没有注意。我已经用数据的最小解决方案来修正它,再加上一个更通用的解决方案*(与使用'getOnlyNumers()'UDF)*相比,它们更短更简单)。 – MatBailie

+0

'ORDER BY CAST(SUBSTRING(id,2,4)as INT), SUBSTRING(id,6,3)'最简单的方法。很好地工作先生,谢谢 – DRUIDRUID

0

我认为最好的方法是创建剥去号码的开出的字符串,如this one功能,然后进行排序,最后。更好的是,像@SeanLange所建议的那样,将使用该函数将数值存储在新列中并按此排序。

+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) 

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

+0

'select ID from MYTABLE cross apply dbo.GetOnlyNumbers(case charindex('_',ID)= 0 then case ID else left(ID,charindex('_',ID) - 1)end)x order by convert(int,x.NumValue)'好的工作@Sean Lange 干杯! – DRUIDRUID

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;