订单串联字段

时间:2018-10-22 02:23:31

标签: sql sql-server

我有一个由单个字母串联而成的字段。我试图在视图中排序这些字符串。这些值太多,因此不能硬编码。有人能够就要使用的功能提供一些指导以实现下面的所需输出吗?我正在使用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万条记录,这仍然是最好的解决方案吗?

不幸的是,我无法将数据规范化为带有每个代码记录的单独表。

4 个答案:

答案 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以避免重复字符。

就这样:-)

提示:SQL-Server 2017 +

使用版本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