我需要快速帮助在SQL端对数据进行排序。我正在使用Sqlserver 2012
(如果使用新功能给出答案,那就很好。)
我已将某些链接搜索为Sorting in alphanumeric,Alphanumeric string Sorting in Sqlserver - Code project。但是没有给出理想的结果。
我还有什么尝试:
CREATE TABLE dbo.Section
(
Section varchar(50) NULL
)
INSERT INTO dbo.Section (Section.Section) VALUES ('Campsit no.43')
INSERT INTO dbo.Section (Section.Section) VALUES ('Campsit no.41')
INSERT INTO dbo.Section (Section.Section) VALUES ('Campsite No. 11')
INSERT INTO dbo.Section (Section.Section) VALUES ('Campsite No. 1')
INSERT INTO dbo.Section (Section.Section) VALUES ('Campsite No. 12')
INSERT INTO dbo.Section (Section.Section) VALUES ('Campsite No. 2')
INSERT INTO dbo.Section (Section.Section) VALUES ('Campsite No. 3')
INSERT INTO dbo.Section (Section.Section) VALUES ('Campsite No. 4')
INSERT INTO dbo.Section (Section.Section) VALUES ('Campsite No. 40')
INSERT INTO dbo.Section (Section.Section) VALUES ('Campsite No. 41')
INSERT INTO dbo.Section (Section.Section) VALUES ('Campsite no.20')
INSERT INTO dbo.Section (Section.Section) VALUES ('Campsite no.41')
INSERT INTO dbo.Section (Section.Section) VALUES ('Cabin')
INSERT INTO dbo.Section (Section.Section) VALUES ('Group Tent Campsite')
INSERT INTO dbo.Section (Section.Section) VALUES ('Tent Campsite')
INSERT INTO dbo.Section (Section.Section) VALUES ('test1')
INSERT INTO dbo.Section (Section.Section) VALUES ('test2')
INSERT INTO dbo.Section (Section.Section) VALUES ('test11')
SELECT Section
FROM dbo.Section
--Show normal Sort
SELECT Section
FROM dbo.Section
ORDER BY Section
--Show AlphaNumberic Sort
SELECT Section
FROM dbo.Section
ORDER BY LEFT(Section,PATINDEX('%[0-9]%',Section)), -- alphabetical sort
CONVERT(varchar(50),SUBSTRING(Section,PATINDEX('%[0-9]%',Section),LEN(Section))) -- numerical sort
--cleanup our work
--DROP Table dbo.Section
现在我想要的是:如果相同的字符串在字母表中找到第一个排序,然后是数字(如果可能的话也考虑空格,或者你可以给出没有空间的结果,如露营地第41号和露营地第41号会给出相同的顺序)
Actual Result Expected Result
Campsit no.41 Campsit no.41
Campsit no.43 Campsit no.43
Campsite No. 1 Campsite No. 1
Campsite No. 11 Campsite No. 2
Campsite No. 12 Campsite No. 3
Campsite No. 2 Campsite No. 4
Campsite No. 21 Campsite No. 11
Campsite No. 3 Campsite No. 12
Campsite No. 4 Campsite No. 21
Campsite No. 40 Campsite No. 40
Campsite No. 41 Campsite No. 41
Campsite no.20 Campsite no.20 --this will good to come here, if possible or if not, then remove space and set approriate
Campsite no.41 Campsite no.41 --this will good to come here, if possible or if not, then remove space and set approriate
Group Tent Campsite Group Tent Campsite
Tent Campsite Tent Campsite
test1 test1
test11 test2
test2 test11
答案 0 :(得分:2)
这里有一个提示:每当您遇到排序问题时,按项目的顺序添加到您的select子句。这将使您能够查看您要排序的内容实际上是您要排序的内容:
SELECT Section,
CASE WHEN PATINDEX('%[0-9]%',Section) > 1 THEN
LEFT(Section,PATINDEX('%[0-9]%',Section)-1)
ELSE
Section
END As alphabetical_sort, -- alphabetical sort
CASE WHEN PATINDEX('%[0-9]%',Section) > 1 THEN
CAST(SUBSTRING(Section,PATINDEX('%[0-9]%',Section),LEN(Section)) as float)
ELSE
NULL
END As Numeric_Sort
FROM dbo.Section
ORDER BY alphabetical_sort, Numeric_Sort
在我正确排序之后,我所要做的就是将case语句移到order by子句中:
SELECT Section
FROM dbo.Section
ORDER BY
CASE WHEN PATINDEX('%[0-9]%',Section) > 1 THEN
LEFT(Section,PATINDEX('%[0-9]%',Section)-1)
ELSE
Section
END , -- Alphabetical sort
CASE WHEN PATINDEX('%[0-9]%',Section) > 1 THEN
CAST(SUBSTRING(Section,PATINDEX('%[0-9]%',Section),LEN(Section)) as float)
ELSE
NULL
END -- Numeric sort
基本上,你有4个主要问题:
答案 1 :(得分:1)
下面给出了你所追求的结果,但我怀疑它是万无一失的。我认为你很难找到一个无懈可击但仍然表现良好的解决方案。
第一部分是获取第一个数字(它出现在空格之后,以便Campsite no.41
被区别对待Campsite no. 40
),我已将其放入APPLY以使其更容易重新 - 使用结果。
下一个阶段是在第一个数字后面找到第一个非数字字符,即数字结束的地方,这样我们就可以使用子字符串提取完整数字,然后最后使用TRY_CONVERT(INT
将这个提取到可分类的。
SELECT s.Section,
TextPart = SUBSTRING(s.Section, 1, ISNULL(fn.FirstNumber, LEN(s.Section))),
Number = CASE WHEN FirstNumber IS NULL THEN NULL
ELSE TRY_CONVERT(INT, SUBSTRING(s.section, fn.FirstNumber + 1, ISNULL(ln.LastNumber, LEN(s.Section))))
END
FROM dbo.Section AS s
-- GET FIRST NUMBER (WHERE PRECEDING CHARACTER IS A SPACE
CROSS APPLY (SELECT NULLIF(PATINDEX('% [0-9]%', s.section), 0)) AS fn (FirstNumber)
-- GET FIRST NON NUMERIC CHARACTER AFTER FIRST NUMBER
CROSS APPLY (SELECT NULLIF(PATINDEX('%[^0-9]%', SUBSTRING(s.section, fn.FirstNumber + 1, LEN(s.Section))), 0)) AS ln (LastNumber)
ORDER BY TextPart, Number;
n.b。您需要将select中的表达式移动到顺序而不是列别名,但我已将其保留为此格式,以便更清楚地显示正在进行的操作
我试图评论解决方案,但有一点点正在进行,因此对每一位的完整解释将非常困难。对不起,如果有什么不清楚的话
修改强>
抱歉,错过了从想要(test1,test11,test2)切换到(test1,test2,test11)的更新。这只是改变你的逻辑来寻找第一个字母,但是现在它是前一个字符不是fullstup(PATINDEX('%[^.][0-9]%', s.section)
)的地方,而不是之前的字符就像以前一样的空格(这确保了{{1} }在Campsite no.20
)之后排序
Campsite no. 40
答案 2 :(得分:1)
在这里尝试一下。注意:Cabin在您的数据中,但不在您的预期结果中。此外,如果您想要使用空格更改某些内容,请告诉我们。
SELECT Section,
FormatSection
FROM dbo.Section
CROSS APPLY (SELECT CASE
WHEN PATINDEX('%[0-9]%',Section) != 0
THEN SUBSTRING(Section,0,PATINDEX('%[0-9]%',Section)) + FORMAT(CAST(SUBSTRING(Section,PATINDEX('%[0-9]%',Section),5) AS INT),'0#')
ELSE Section
END
) AS CA(FormatSection)
ORDER BY FormatSection
结果:
Section FormatSection
-------------------------------------------------- ---------------------
Cabin Cabin
Campsit no.41 Campsit no.41
Campsit no.43 Campsit no.43
Campsite No. 1 Campsite No. 01
Campsite No. 2 Campsite No. 02
Campsite No. 3 Campsite No. 03
Campsite No. 4 Campsite No. 04
Campsite No. 11 Campsite No. 11
Campsite No. 12 Campsite No. 12
Campsite No. 40 Campsite No. 40
Campsite No. 41 Campsite No. 41
Campsite no.20 Campsite no.20
Campsite no.41 Campsite no.41
Group Tent Campsite Group Tent Campsite
Tent Campsite Tent Campsite
test1 test01
test2 test02
test11 test11
答案 3 :(得分:1)
我找到了以下替代方案。
创建此函数,然后在查询“ ORDER BY fnGetNumericFromString([columnNm])”中使用此函数。
CREATE FUNCTION fnGetNumericFromString (@InString VARCHAR(20), @OutStrType VARCHAR(3))
RETURNS VarChar(20)
AS
BEGIN
-- declare variables
DECLARE @pos INT
DECLARE @strLength INT
DECLARE @NumericString VarChar(20)
DECLARE @CharString VarChar(20)
DECLARE @ReturnValue VarChar(20)
-- set values
SET @NumericString = ''
SET @CharString = ''
SET @pos= 1
SET @strLength = LEN(@InString)
SET @InString = UPPER(@InString)
SET @OutStrType = UPPER(@OutStrType)
--start looping
WHILE @pos <= @strLength
BEGIN
-- number codes are 48 to 57
IF ASCII(SUBSTRING(@InString, @pos, 1))BETWEEN 48 AND 57
SET @NumericString = @NumericString + SUBSTRING(@InString, @pos, 1)
else
SET @CharString = @CharString + SUBSTRING(@InString, @pos, 1)
--increment to next character
SET @pos = @pos + 1
END
IF @OutStrType = 'STR'
SET @ReturnValue = @CharString
ELSE
SET @ReturnValue = @NumericString
RETURN @ReturnValue
END
select section from Section
order by dbo.fnGetNumericFromString(section, 'str'), CAST(dbo.fnGetNumericFromString(section, 'int') AS INT)
答案 4 :(得分:-1)
SELECT *,
ROW_NUMBER()OVER(ORDER BY CASE WHEN ISNUMERIC (ID)=1 THEN CONVERT(NUMERIC(20,2),SUBSTRING(Id, PATINDEX('%[0-9]%', Id), LEN(Id)))END DESC)Rn ---- numerical
FROM
(
SELECT '1'Id UNION ALL
SELECT '25.20' Id UNION ALL
SELECT 'A115' Id UNION ALL
SELECT '2541' Id UNION ALL
SELECT '571.50' Id UNION ALL
SELECT '67' Id UNION ALL
SELECT 'B48' Id UNION ALL
SELECT '500' Id UNION ALL
SELECT '147.54' Id UNION ALL
SELECT 'A-100' Id
)A
ORDER BY
CASE WHEN ISNUMERIC (ID)=0 /* alphabetical sort */
THEN CASE WHEN PATINDEX('%[0-9]%', Id)=0
THEN LEFT(Id,PATINDEX('%[0-9]%',Id))
ELSE LEFT(Id,PATINDEX('%[0-9]%',Id)-1)
END
END DESC