我有一个由单个字母串联而成的字段。我试图在视图中排序这些字符串。这些值太多,因此不能硬编码。有人能够就要使用的功能提供一些指导以实现下面的所需输出吗?我正在使用MSSQL。
当前输出
CustID | Code
123 | BCA
所需的输出
CustID | Code
123 | ABC
我尝试使用UDF
CREATE FUNCTION [dbo].[Alphaorder] (@str VARCHAR(50))
returns VARCHAR(50)
BEGIN
DECLARE @len INT,
@cnt INT =1,
@str1 VARCHAR(50)='',
@output VARCHAR(50)=''
SELECT @len = Len(@str)
WHILE @cnt <= @len
BEGIN
SELECT @str1 += Substring(@str, @cnt, 1) + ','
SET @cnt+=1
END
SELECT @str1 = LEFT(@str1, Len(@str1) - 1)
SELECT @output += Sp_data
FROM (SELECT Split.a.value('.', 'VARCHAR(100)') Sp_data
FROM (SELECT Cast ('<M>' + Replace(@str1, ',', '</M><M>') + '</M>' AS XML) AS Data) AS A
CROSS APPLY Data.nodes ('/M') AS Split(a)) A
ORDER BY Sp_data
RETURN @output
END
在调用一个字段时有效 即。
Select CustID, dbo.alphaorder(Code)
from dbo.source
where custid = 123
但是当我尝试将其应用于top(10)时,我收到错误消息 “传递给LEFT或SUBSTRING函数的无效长度参数。”
请记住,我的消息来源有大约400万条记录,这仍然是最好的解决方案吗?
不幸的是,我无法将数据规范化为带有每个代码记录的单独表。
答案 0 :(得分:3)
这不依赖于id列本身,因此性能几乎一样快 作为@Shnugo的答案:
SELECT
CustID,
(
SELECT
chr
FROM
(SELECT TOP(LEN(Code))
SUBSTRING(Code,ROW_NUMBER() OVER(ORDER BY (SELECT NULL)),1)
FROM sys.messages) A(Chr)
ORDER by chr
FOR XML PATH(''), type).value('.', 'varchar(max)'
) As CODE
FROM
source t
答案 1 :(得分:2)
首先:避免循环...
您可以尝试以下方法:
DECLARE @tbl TABLE(ID INT IDENTITY, YourString VARCHAR(100));
INSERT INTO @tbl VALUES ('ABC')
,('JSKEzXO')
,('QKEvYUJMKRC');
-cte将创建所有以单个字符分隔的字符串的列表。
-您可以使用简单的SELECT * FROM SeparatedCharacters
而不是实际的SELECT
WITH SeparatedCharacters AS
(
SELECT *
FROM @tbl
CROSS APPLY
(SELECT TOP(LEN(YourString)) ROW_NUMBER() OVER(ORDER BY (SELECT NULL)) FROM master..spt_values) A(Nmbr)
CROSS APPLY
(SELECT SUBSTRING(YourString,Nmbr,1))B(Chr)
)
SELECT ID,YourString
,(
SELECT Chr As [*]
FROM SeparatedCharacters sc1
WHERE sc1.ID=t.ID
ORDER BY sc1.Chr
FOR XML PATH(''),TYPE
).value('.','nvarchar(max)') AS Sorted
FROM @tbl t;
结果
ID YourString Sorted
1 ABC ABC
2 JSKEzXO EJKOSXz
3 QKEvYUJMKRC CEJKKMQRUvY
诀窍是第一个CROSS APPLY
。这将即时创建 tally 。您将获得一个从1到n的数字的结果集,其中n是当前字符串的长度。
第二个应用使用此数字使用SUBSTRING()
来获得每个字符 。
原始表中的外部SELECT
调用,这表示每个ID一排,并使用修正的子查询来获取所有相关的字符。它们将使用FOR XML
进行排序和重新连接。您可以添加DISTINCT
以避免重复字符。
就这样:-)
使用版本v2017 there's the new function STRING_AGG()
。这将使重新连接非常容易:
WITH SeparatedCharacters AS
(
SELECT *
FROM @tbl
CROSS APPLY
(SELECT TOP(LEN(YourString)) ROW_NUMBER() OVER(ORDER BY (SELECT NULL)) FROM master..spt_values) A(Nmbr)
CROSS APPLY
(SELECT SUBSTRING(YourString,Nmbr,1))B(Chr)
)
SELECT ID,YourString
,STRING_AGG(sc.Chr,'') WITHIN GROUP(ORDER BY sc.Chr) AS Sorted
FROM SeparatedCharacters sc
GROUP BY ID,YourString;
答案 2 :(得分:1)
考虑到您的表有很多行(〜400万),我建议您在表中创建一个持久的计算字段来存储这些值。由于在视图中运行时计算这些值,将导致性能问题。
如果无法规范化,请将其作为非规范化列添加到现有表中。
我认为您收到的错误可能是由于空代码所致。
If LEN(@str) = 0
BEGIN
SET @output = ''
END
ELSE
BEGIN
... EXISTING CODE BLOCK ...
END
答案 3 :(得分:1)
我可以建议使用引用的SQL函数split string into its characters。 然后,您可以将字符串串联起来,这次是按字母顺序排列的。
您是否正在使用SQL Server 2017?因为在SQL Server 2017中,您可以使用SQL String_Agg string aggregation function来串联按如下顺序拆分的字符
select
t.CustId, string_agg(strval, '') within GROUP (order by strval)
from CharacterTable t
cross apply dbo.SPLIT(t.code) s
where strval is not null
group by CustId
order by CustId
如果您不使用SQL2017,则可以对concatenation in SQL使用SQL XML PATH遵循以下结构
select
CustId,
STUFF(
(
SELECT
'' + strval
from CharacterTable ct
cross apply dbo.SPLIT(t.code) s
where strval is not null
and t.CustId = ct.CustId
order by strval
FOR XML PATH('')
), 1, 0, ''
) As concatenated_string
from CharacterTable t
order by CustId