[英]SQL greater than custom ordering
I've got a database that contains release versions of software and I want to be able to pull back all versions that are greater than the current version, ordered by version number. 我有一个数据库,其中包含软件的发行版本,并且我希望能够按版本号将所有大于当前版本的版本拉回。 However, the releases are sorted in a custom (but standard) way - from alpha version to beta version to main release to patch.
但是,这些发布以自定义(但标准)的方式排序-从Alpha版本到Beta版本再到主要版本再到补丁。 So here's an example of the ordering:
因此,这是排序的示例:
100a1
100a4
100b1
100
100p1
101
101p3
etc.
Is it possible to form an SQL query that pulls back this data given the custom ordering or does > only work for given orderings like integers and dates? 是否可以形成一个SQL查询,以给定自定义顺序来拉回此数据,或者>仅适用于给定顺序(如整数和日期)? I'm working with MSSQL if that makes any difference.
如果这有任何区别,我正在使用MSSQL。
As long as you can actually describe how the ordering is supposed to work, sure. 当然,只要您能真正描述订购的工作原理即可。
The two basic approaches are: 两种基本方法是:
order by left([Version] + '__', 5)
. order by left([Version] + '__', 5)
。 Making a single integer out of the more complex value also works. order by
, in any order you want. order by
的任意顺序按order by
使用所有值。 This is the more idiomatic way of handling this in SQL - basically, why are you using one value 101p1
when you're logically working with 101, p, 1
? 101, p, 1
时为什么要使用一个值101p1
? Parsing is a bit tricky to handle in SQL, because SQL really is designed for normalized data sets - and you're effectively storing multiple values in one column. 在SQL中处理解析有些棘手,因为SQL实际上是为规范化数据集设计的-而且您实际上将多个值存储在一个列中。 If your rules aren't too complicated, though, this should still be doable.
但是,如果您的规则不太复杂,这仍然可行。 It's not going to be awfully pretty, though :D
虽然不会很漂亮,但:D
For fixed length values, this is pretty simple, of course - that's the equivalent of using eg 001p01
as filenames in the file system - the alphabetical ordering is the correct ordering. 对于固定长度的值,这当然非常简单-相当于在文件系统中使用例如
001p01
作为文件名-字母顺序是正确的顺序。 You could then simply use order by
on the whole value, or split it into parts based on substring
s. 然后,您可以简单地对整个值使用
order by
,或根据substring
s将其拆分为多个部分。 For values with separators, it's a bit uglier, but still pretty easy - 1.p.1
can be split relatively easily, and then you can order by each of the parts in sequence. 对于带有分隔符的值,它有点
1.p.1
,但仍然很简单1.p.1
可以相对容易地拆分,然后可以按顺序按每个部分排序。
However, your system seems to be a better fit for humans than machines - there's no real hints to follow. 但是,您的系统似乎比机器更适合人类-没有真正的提示可循。 Basically, it seems that you're looking at a pattern of "numbers, letters, numbers... treat numbers as numbers, and letters as letters".
基本上,您似乎正在看“数字,字母,数字...将数字视为数字,而将字母视为字母”的模式。 This is actually quite tricky to handle in T-SQL.
在T-SQL中处理这实际上非常棘手。 It might be worth it to bring in the help of the CLR, and regular expressions in particular - I'm not sure if you'll be able to handle this in general for an unlimited amount of number/letter groups anyway, though.
引入CLR(尤其是正则表达式)的帮助可能是值得的-但是我不确定您是否总能为数量不限的数字/字母组进行处理。
The simplest way by far seems to be to simply separate the version column into multiple columns, each with just one value - something like MajorVersion, Level, Revision
or something like that, corresponding to 101, Alpha, 3
. 到目前为止,最简单的方法似乎是将version列简单地分成多个列,每个列只有一个值-类似于
MajorVersion, Level, Revision
或类似的值,对应于101, Alpha, 3
。
我认为前3个是数字。
select * from tablename order by convert(int,left(Columnname,3))
Here is my code example. 这是我的代码示例。 Not the shortest one but it holds many demo input/output and can be further simplified if you understand what I want.
它不是最短的,但它拥有许多演示输入/输出,如果您了解我想要的内容,可以进一步简化。
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
This is my input: 这是我的输入:
version
----------
100a1
100a4
100b1
100p1
100
101
101p3
And this is the result: 结果如下:
version
----------
100a1
100a4
100b1
100
100p1
101
101p3
Anyway. 无论如何。 I would suggest to split those values into separate columns.
我建议将这些值分成单独的列。 It will make your live much easier.
这将使您的生活更加轻松。 :-)
:-)
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.