SQL大于自定义排序

时间:2015-06-23 09:09:56

标签: sql sql-server

我有一个包含软件发行版本的数据库,我希望能够撤回所有版本,这些版本大于当前版本,按版本号排序。但是,版本以自定义(但标准)的方式排序 - 从alpha版本到beta版本到main版本到补丁。所以这是一个排序的例子:

100a1
100a4
100b1
100
100p1
101
101p3
etc.

是否可以形成一个SQL查询,根据自定义顺序拉回此数据,或者>只适用于整数和日期等给定的排序?我正在使用MSSQL,如果这有任何区别。

3 个答案:

答案 0 :(得分:1)

只要您能够实际描述排序应该如何工作,当然。

两种基本方法是:

  • 将值转换为有序的值。例如,您可以使用order by left([Version] + '__', 5)之类的内容。从更复杂的值中生成单个整数也可以。
  • 将值分隔为每个序数的多个值,并以您想要的任何顺序使用order by中的所有值。这是在SQL中处理此问题的更惯用的方式 - 基本上,当您在逻辑上使用101p1时,为什么要使用一个值101, p, 1

在SQL中解析有点棘手,因为SQL实际上是为规范化数据集设计的 - 而且你有效地将多个值存储在一列中。但是,如果你的规则不是太复杂,那么这仍然是可行的。不过,它不会非常漂亮:D

对于固定长度值,这当然非常简单 - 这相当于使用例如001p01作为文件系统中的文件名 - 字母顺序正确的顺序。然后,您可以在整个值上使用order by,或者根据substring将其拆分为多个部分。对于带分隔符的值,它有点丑陋,但仍然非常简单 - 1.p.1可以相对容易地拆分,然后您可以按顺序按每个部分排序。

然而,你的系统似乎比机器更适合人类 - 没有真正的提示可以遵循。基本上,似乎你正在寻找“数字,字母,数字......将数字视为数字,将字母视为字母”的模式。在T-SQL中处理这实际上非常棘手。引入CLR的帮助,特别是正则表达式可能是值得的 - 尽管如此,我不确定你是否能够处理无限量的数字/字母组。 / p>

到目前为止,最简单的方法似乎是简单地将版本列分成多个列,每个列只有一个值 - 类似MajorVersion, Level, Revision或类似的内容,对应101, Alpha, 3

答案 1 :(得分:0)

我假设前三个是数字。

select * from tablename order by convert(int,left(Columnname,3))

答案 2 :(得分:0)

这是我的代码示例。不是最短的,但它拥有许多演示输入/输出,如果你理解我想要的东西,可以进一步简化。

CREATE TABLE #versions(version nvarchar(10))

INSERT INTO #versions(version)
VALUES(N'100a1'),(N'100a4'),(N'100b1'),(N'100p1'),(N'100'),(N'101'),(N'101p3')

-- Just an example using substrings etc. how to get the 
SELECT version,
    SUBSTRING(version,1,
        CASE 
            WHEN PATINDEX(N'%[a-z]%',version) > 0 
            THEN PATINDEX(N'%[a-z]%',version)-1 
            ELSE LEN(version) 
        END
    ) as version_number,
    SUBSTRING(version,
        CASE 
            WHEN PATINDEX(N'%[a-z]%',version) > 0 
            THEN PATINDEX(N'%[a-z]%',version)
            ELSE 0
        END, PATINDEX(N'%[0-9]%',
            SUBSTRING(version,1,
                CASE 
                    WHEN PATINDEX(N'%[a-z]%',version) > 0 
                    THEN PATINDEX(N'%[a-z]%',version)-1 
                    ELSE LEN(version) 
                END
            )
        )
    ) as version_suffix,
    SUBSTRING(version,
        PATINDEX(N'%[a-z]%',
            SUBSTRING(version,
                CASE 
                    WHEN PATINDEX(N'%[a-z]%',version) > 0 
                    THEN PATINDEX(N'%[a-z]%',version)
                    ELSE LEN(version) 
                END, LEN(version)
            )
        ),
        PATINDEX(N'%[0-9]%',
            SUBSTRING(version,1,
                CASE 
                    WHEN PATINDEX(N'%[a-z]%',version) > 0 
                    THEN PATINDEX(N'%[a-z]%',version)-1 
                    ELSE LEN(version) 
                END
            )
        )
    ) as version_sub
FROM #versions

-- Now your code:
;WITH vNumber AS(
    SELECT version,SUBSTRING(version,1,
        CASE 
            WHEN PATINDEX(N'%[a-z]%',version) > 0 
            THEN PATINDEX(N'%[a-z]%',version)-1 
            ELSE LEN(version) 
        END
    ) as version_number
    FROM #versions
), vSuffix AS(
    SELECT version, SUBSTRING(version,
                CASE 
                    WHEN PATINDEX(N'%[a-z]%',version) > 0 
                    THEN PATINDEX(N'%[a-z]%',version)
                    ELSE LEN(version) 
                END, LEN(version)
            ) as version_suffix
    FROM #versions
)
SELECT dat.version
FROM (
    SELECT vn.version, vn.version_number,
        CASE 
            SUBSTRING(vn.version,
                CASE 
                    WHEN PATINDEX(N'%[a-z]%',vn.version) > 0 
                    THEN PATINDEX(N'%[a-z]%',vn.version)
                    ELSE 0
                END, 1
            )
            WHEN N'a' THEN 1
            WHEN N'b' THEN 2
            WHEN N'' THEN 3
            WHEN N'p' THEN 4
        END as version_suffix,
        SUBSTRING(vn.version,
            PATINDEX(N'%[a-z]%',
                vs.version_suffix
            ),
            PATINDEX(N'%[0-9]%',
                SUBSTRING(vn.version,1,
                    CASE 
                        WHEN PATINDEX(N'%[a-z]%',vn.version) > 0 
                        THEN PATINDEX(N'%[a-z]%',vn.version)-1 
                        ELSE LEN(vn.version) 
                    END
                )
            )
        ) as version_sub
    FROM vNumber as vn
    INNER JOIN vSuffix as vs
            ON vn.version = vs.version
) AS dat
ORDER BY dat.version_number, dat.version_suffix, dat.version_sub

DROP TABLE #versions

这是我的意见:

version
----------
100a1
100a4
100b1
100p1
100
101
101p3

这就是结果:

version
----------
100a1
100a4
100b1
100
100p1
101
101p3

反正。我建议将这些值拆分为单独的列。它会让你的生活更轻松。 : - )