【问题标题】:How to reverse string in column a word by word?如何逐字反转列中的字符串?
【发布时间】:2020-12-29 02:04:35
【问题描述】:

我有一个表,其 varchar(50)'Relation' 包含 1000 多行数据,例如:

 P1_P10_P45_P20
 P1_P14_P5_P22
 P1_P3
 P3_P4_P5_P2_P100_P2_P1

我希望输出有相反的顺序:

 P20_P45_P10_P1
 P22_P5_P14_P1
 P3_P1
 P1_P2_P100_P2_P5_P4_P3

您能帮我在单个查询中实现这一点吗?

【问题讨论】:

  • 您好,这不是代码编写服务。您应该首先尝试自己解决问题,然后询问具体问题(或整个问题,但表明您尝试解决它)。查看how to ask a good question
  • 您的 SQl Server 版本是多少?
  • 修复你的数据模型!根本问题是您将多个值存储在一个字符串中。
  • SQL Server v18.5
  • @Aditi v18.5 是 SQL Server Management Studion(客户端工具)的版本。什么是 SQL Server 版本(SELECT @@VERSION; 返回什么)?谢谢。

标签: sql sql-server tsql sql-server-2008


【解决方案1】:

Aditi 您可以使用 Tally 表查找所有 _,然后使用 STUFF + FOR XML PATH 组合将它们连接回来,如下所示。

我建议您尽早阅读有关 Tally 表的信息here

演示链接是here

--create table yourtable(Relation nvarchar(50));
--insert into yourtable values 
-- ('P1_P14_P5_P22'),
--   ('P1_P3'),
--   ('P3_P4_P5_P2_P100_P2_P1'), ('P1_P3'),
--   ('P3_P4_P5_P2_P100_P2_P1');


;WITH Tally AS (
   SELECT 1 as Num
   UNION ALL
   SELECT Num + 1 FROM Tally  WHERE Num < 51
    )
,
InputSet AS
(
 select *, RN=row_number() over (order by (select 1)) from yourtable
    )
,TempSet AS
(
      SELECT 
        Relation, 
        Num,
        RN,
        partBetweenUnderscore = SUBSTRING(Relation, Num, ISNULL(LEAD(Num) OVER (Partition by RN ORDER BY Num ASC),LEN('_'+Relation)+1)-Num-1)
    FROM
    (
        SELECT *  
        FROM InputSet CROSS JOIN Tally 
        WHERE CHARINDEX('_','_'+Relation,Num)=Num
     )T
 )
SELECT 
   Relation,
    NewRelation = STUFF(
                    (SELECT '_' + T1.partBetweenUnderscore FROM TempSet T1 WHERE T1.RN=T2.RN ORDER BY T1.Num DESC FOR XML PATH ('') 
                        ),1,1,'')
FROM TempSet T2
GROUP BY RN, Relation

【讨论】:

  • 如果“关系”列的行数超过 1 行,则会显示错误“传递给左侧或子字符串函数的参数长度无效”。这是通过插入另一行在链接中完成的修改:link
  • @Aditi 更新了我的答案以支持多行。最后一次编辑还处理重复的关系列数据
【解决方案2】:

您需要使用拆分器拆分存储的字符串,该拆分器返回子字符串和每个子字符串的位置。之后,您可以轻松构建所需的输出。

如果您使用 SQL Server 2017+,您可以尝试基于 JSON 的方法。您需要将每个字符串转换为有效的 JSON 数组(例如,P1_P10_P45_P20 转换为 ["'P1","P10","P45","P20"]),将此数组解析为带有 OPENJSON() 的表,并使用 STRING_AGG() 连接行以生成预期的输出:

表:

CREATE TABLE Data (Relation varchar(50))
INSERT INTO Data (Relation)
VALUES
   ('P1_P10_P45_P20'),
   ('P1_P14_P5_P22'),
   ('P1_P3'),
   ('P3_P4_P5_P2_P100_P2_P1')

声明:

SELECT c.Relation
FROM Data d
OUTER APPLY (
   SELECT STRING_AGG([value], '_') WITHIN GROUP (ORDER BY CONVERT(int, [key]) DESC)
   FROM OPENJSON(CONCAT('["', REPLACE(d.Relation, '_', '","'), '"]'))
) c (Relation)

结果:

Relation
----------------------
P20_P45_P10_P1
P22_P5_P14_P1
P3_P1
P1_P2_P100_P2_P5_P4_P3

【讨论】:

    【解决方案3】:

    首先所有先前的 cmets 都是正确的,尤其是这是一个数据模型问题。这是一个非常笨拙的解决方案。我提供它是因为您只有 1000 条记录。这效率不高,也不会扩大规模。以下适用于 MS SQL Server 2017。

    Drop table if exists Relation   
    create table Relation (Relation varchar(50))
    INSERT INTO Relation (Relation)
    VALUES
        ('P1_P10_P45_P20'),
        ('P1_P14_P5_P22'),
        ('P1_P3'),
        ('P3_P4_P5_P2_P100_P2_P1');
    
    
    
    DROP TABLE IF EXISTS Rev
    create table Rev (Relation varchar(50), Rev varchar(50))
    DROP TABLE IF EXISTS Z
    create table Z (token varchar(50))
    declare @Reverse varchar(50)
    set @Reverse = ''
    
    declare @token varchar(50)
    declare @Relation varchar(50)
    declare cur cursor for select * from Relation
    open cur
    fetch next from cur into @Relation
    while @@FETCH_STATUS = 0
    begin   
        with T(Relation, starts, pos) as (
        select @Relation, 1, charindex('_', @Relation)
        union all
        select @Relation, pos + 1, charindex('_', @Relation, pos + 1)
        from T
        where pos > 0)
        insert into Z select substring(@Relation, starts, case when pos > 0 then pos - starts else len(@Relation) end) token
        from T
        declare cur2 cursor for select token from Z
        open cur2
        fetch next from cur2 into @token
        while @@FETCH_STATUS = 0
            begin
                set @Reverse =  @token + '_' + @Reverse  
                fetch next from cur2 into @token
            end
            close cur2
            deallocate cur2
            set @Reverse = (select left(@Reverse,len(@Reverse)-1))
            insert into Rev select @Relation, @Reverse
            set @Reverse = ''
            delete Z
        fetch next from cur into @Relation
    end;
    close cur
    deallocate cur
    select * from Rev
    
    
    
    SELECT @@version
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2016-03-03
      • 1970-01-01
      • 1970-01-01
      • 2013-01-13
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多