简体   繁体   English

订单串联字段

[英]Order Concatenated field

I have a field which is a concatenation of single letters. 我的字段是单个字母的串联。 I am trying to order these strings within a view. 我试图在视图中排序这些字符串。 These values can't be hard coded as there are too many. 这些值太多,因此不能硬编码。 Is someone able to provide some guidance on the function to use to achieve the desired output below? 有人能够就要使用的功能提供一些指导以实现下面的所需输出吗? I am using MSSQL. 我正在使用MSSQL。

Current output 电流输出

CustID | Code
123    | BCA

Desired output 所需的输出

CustID | Code
123    | ABC

I have tried using a UDF 我尝试使用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

This works when calling one field ie. 这在调用一个字段时有效。

Select CustID, dbo.alphaorder(Code)
from dbo.source
where custid = 123

however when i try to apply this to top(10) i receive the error "Invalid length parameter passed to the LEFT or SUBSTRING function." 但是,当我尝试将其应用于top(10)时,出现错误“传递给LEFT或SUBSTRING函数的无效长度参数”。

Keeping in mind my source has ~4million records, is this still the best solution? 请记住,我的消息来源有大约400万条记录,这仍然是最佳解决方案吗?

Unfortunately i am not able to normalize the data into a separate table with records for each Code. 不幸的是,我无法将数据规范化为带有每个代码记录的单独表。

This doesn't rely on a id column to join with itself, performance is almost as fast as the answer by @Shnugo: 这不依赖于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

First of all: Avoid loops... 首先:避免循环...

You can try this: 您可以尝试以下方法:

DECLARE @tbl TABLE(ID INT IDENTITY, YourString VARCHAR(100));
INSERT INTO @tbl VALUES ('ABC')
                       ,('JSKEzXO')
                       ,('QKEvYUJMKRC');

--the cte will create a list of all your strings separated in single characters. -cte将创建所有以单个字符分隔的字符串的列表。
--You can check the output with a simple SELECT * FROM SeparatedCharacters instead of the actual SELECT -您可以使用简单的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;

The result 结果

ID  YourString  Sorted
1   ABC         ABC
2   JSKEzXO     EJKOSXz
3   QKEvYUJMKRC CEJKKMQRUvY

The idea in short 简而言之

The trick is the first CROSS APPLY . 诀窍是第一个CROSS APPLY This will create a tally on-the-fly . 这将动态创建一个提示 You will get a resultset with numbers from 1 to n where n is the length of the current string. 您将获得一个从1到n的数字的结果集,其中n是当前字符串的长度。

The second apply uses this number to get each character one-by-one using SUBSTRING() . 第二个应用使用此号码使用得到每个字符一个接一个 SUBSTRING()

The outer SELECT calls from the orginal table, which means one-row-per-ID and use a correalted sub-query to fetch all related characters. 来自原始表的外部SELECT调用,意味着每个ID一行,并使用相应的子查询来获取所有相关字符。 They will be sorted and re-concatenated using FOR XML . 它们将使用FOR XML进行排序和重新连接。 You might add DISTINCT in order to avoid repeating characters. 您可以添加DISTINCT以避免重复字符。

That's it :-) 而已 :-)

Hint: SQL-Server 2017+ 提示:SQL-Server 2017+

With version v2017 there's the new function STRING_AGG() . 在v2017版中,有了新功能STRING_AGG() This would make the re-concatenation very easy: 这将使重新串联非常容易:

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;

Considering your table having good amount of rows (~4 Million), I would suggest you to create a persisted calculated field in the table, to store these values. 考虑到您的表中有很多行(〜400万),我建议您在表中创建一个持久化的计算字段,以存储这些值。 As calculating these values at run time in a view, will lead to performance problems. 由于在视图中运行时计算这些值,将导致性能问题。

If you are not able to normalize, add this as a denormalized column to the existing table. 如果您无法规范化,请将其作为非规范化列添加到现有表中。

I think the error you are getting could be due to empty codes. 我认为您收到的错误可能是由于空代码所致。

If LEN(@str)  = 0
BEGIN
  SET @output = ''
END
ELSE
BEGIN
... EXISTING CODE BLOCK ...
END

I can suggest to split string into its characters using referred SQL function. 我可以建议使用引用的SQL函数将字符串拆分为字符 Then you can concatenate string back, this time ordered alphabetically. 然后,您可以将字符串串联起来,这次是按字母顺序排列的。

Are you using SQL Server 2017? 您正在使用SQL Server 2017吗? Because with SQL Server 2017, you can use SQL String_Agg string aggregation function to concatenate characters splitted in an ordered way as follows 因为在SQL Server 2017中,您可以使用SQL String_Agg字符串聚合函数将按顺序拆分的字符连接起来,如下所示

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 

If you are not working on SQL2017, then you can follow below structure using SQL XML PATH for concatenation in SQL 如果您不使用SQL2017,则可以使用SQL XML PATH遵循以下结构在SQL中进行串联

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 

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM