【问题标题】:T-SQL, cut string into chunks of 35 characters without cutting wordsT-SQL,将字符串切割成35个字符的块而不切割单词
【发布时间】:2020-05-11 04:10:13
【问题描述】:

好的,我一直在到处寻找正确的语法。 假设我有一串长度未知的字符串 即“敏捷的棕色狐狸跳过懒惰的狗。敏捷的棕色狐狸跳过了懒狗。敏捷的棕色狐狸跳过了懒狗。敏捷的棕色狐狸跳过了懒狗。敏捷的棕色狐狸跳过了懒惰的狗。'

我需要将它分成最多 35 个字符的块,每个字符都像:'The quick brown fox jumps over the' 空格是分隔符。

规则是:

1 - 每个块不超过 35 个字符

2 - 不要拆分单词。

2.1 - 如果组合长度大于 35,则返回直到找到长度低于 35 的第一个空格并将其剪掉。

3 - 如果需要,结果集必须返回一个包含 5 个值(由字符串块组成)和一个表示多条记录结果的行号的表。 (见下表)

也就是说,如果一个字符串分成 5 35 个字符块,一条记录返回 任何多余的溢出到 5 组中的更多行

________________________________________________________________________________________________________________________________________________________________________________|
|level  |   Val1                            |Val2                           |   Val3                        |   Val4                            |   Val5                        |
________________________________________________________________________________________________________________________________________________________________________________|
|   1   |The quick brown fox jumps over the | lazy dog. The quick brown fox | jumps over the lazy dog. The  | quick brown fox jumps over the    | lazy dog. The quick brown fox |
|   2   | jumps over the lazy dog. The      | quick brown fox jumps over the| lazy dog.                     |NULL                               |   NULL                        |       
________________________________________________________________________________________________________________________________________________________________________________|

我在这里找到了一些类似工作的代码,但我无法将结果限制为 35 个字符块

它的作用是: 获取字符串中的分隔符数(空格数) 比使用 CTE 将所有内容拆分到表中。 而不是全部连接回来。但是,在“Splitvalues”cte 中,如果我将 mainLevel 分成 5 个,它可以工作,但不是按长度加入,而是按 5 组加入 而且我仍然不知道如何将结果转为 6 列,如所述。

DECLARE     @ColumnLen             INT           = 35
       ,@BNotAllowNullinValue1 BIT           = 1
       ,@Delim                 VARCHAR(5)    = SPACE(1)
       ,@DelimCount            INT
       ,@OriginalStr           NVARCHAR(MAX) = 'The quick brown fox jumps over the lazy dog. The quick brown fox jumps over the lazy dog. The quick brown fox jumps over the lazy dog. The quick brown fox jumps over the lazy dog. The quick brown fox jumps over the lazy dog.'
       ,@ReturnColumnCount     INT           = 5

SET @OriginalStr = @OriginalStr + @Delim
SET @DelimCount = ((LEN(@OriginalStr + '|')-1) - (LEN(REPLACE(@OriginalStr + '|',@Delim,''))-1)) / LEN(LTRIM(RTRIM(@Delim)) + '|')

---- test data
;WITH Splitvalues(SplitValue ,MainLevel ,ProcessLevel,LastPos,Original)
AS (SELECT TOP 1 LTRIM(RTRIM(SUBSTRING(@OriginalStr,1,ABS(CHARINDEX(@Delim,@OriginalStr,1)))))
                ,1 as MainLevel
                ,1 as ProcessLevel
                ,CHARINDEX(@Delim,@OriginalStr,1 + 1) AS LastPos
                ,@OriginalStr
    UNION ALL
    SELECT LTRIM(RTRIM(SUBSTRING(@OriginalStr,LastPos + 1,ABS((CHARINDEX(@Delim,@OriginalStr,LastPos + 1) - LastPos)))))
          ,CASE (ProcessLevel % 5) WHEN  0 THEN MainLevel +1 ELSE MainLevel END  as MainLevel
          ,ProcessLevel + 1 as ProcessLevel
         ,CHARINDEX(@Delim,@OriginalStr,LastPos + 1) AS LastPos
          ,@OriginalStr
    FROM Splitvalues
    WHERE ProcessLevel <= @DelimCount
          AND ISNULL(LTRIM(RTRIM(SUBSTRING(@OriginalStr,LastPos + 1,ABS((CHARINDEX(@Delim,@OriginalStr,LastPos + 1) - LastPos))))),'') <> '')
---- actual query;
,cte(MainLevel,ProcessLevel,combined,rn)
     AS (SELECT MainLevel,ProcessLevel,Splitvalue ,rn = ROW_NUMBER() OVER(PARTITION BY MainLevel ORDER BY MainLevel,ProcessLevel)FROM Splitvalues)
,cte2(MainLevel,ProcessLevel ,finalstatus ,rn)
     AS (SELECT MainLevel,cte.ProcessLevel ,CONVERT(VARCHAR(MAX),combined)  ,1 FROM cte WHERE rn = 1
         UNION ALL
         SELECT cte2.MainLevel,cte2.ProcessLevel  +1 ,CONVERT(VARCHAR(MAX),cte2.finalstatus + @Delim + cte.combined+ @Delim )
               ,cte2.rn + 1
         FROM cte2
         INNER JOIN cte ON cte.MainLevel = cte2.MainLevel  AND cte.rn = cte2.rn + 1
        )
     SELECT MainLevel,MAX(finalstatus),LEN(MAX(finalstatus)+'|')
     FROM cte2
     GROUP BY MainLevel

感谢大家的帮助。

【问题讨论】:

  • 这里添加最终代码的规则是什么?我应该使用回答我的 onw 问题吗?即使我接受了其中一个答案?

标签: tsql string-concatenation


【解决方案1】:

要找到剪切的位置,我建议获取字符串的前 35 个字符,然后找到最后一个空格。这可以使用 reverse 和 charindex 来完成:

最右边空格的位置是:

36-CHARINDEX(' ', REVERSE(LEFT(@txt, 36)))

现在我将使用递归 CTE。每一层都会切掉字符串的下一位,直到它为空。

我已经包含了下面的查询,我在上面使用放置查找器锚定 CTE,在递归部分,我再次使用位置查找器。

当没有剩余字符时递归结束。

最后,为了将其设置为一行 5 列,我将每个出现的位置编号为 0...n

然后我使用 n%5 模数在 (0,1,2,3,4) 中的事实 并且 n/5 作为整数除法给出了应该输出列的行号。

declare @txt varchar(max)=N'The quick brown fox jumps over the lazy dog. The quick brown fox jumps over the lazy dog. The quick brown fox jumps over the lazy dog. The quick brown fox jumps over the lazy dog. The quick brown fox jumps over the lazy dog.'
;
WITH cte
AS
(SELECT
        TRIM(LEFT(@txt, n)) grp
       ,0 grpn
       ,TRIM(SUBSTRING(@txt, n + 1, LEN(@txt))) remainder
    FROM (SELECT
            36 - CHARINDEX(' ', REVERSE(LEFT(@txt, 36))) n) a
    UNION ALL
    SELECT
        TRIM(LEFT(remainder, (n))) grp
       ,grpn + 1
       ,TRIM(SUBSTRING(remainder, (n) + 1, LEN(remainder))) remainder
    FROM cte
    OUTER APPLY (SELECT
            36 - CHARINDEX(' ', REVERSE(LEFT(remainder, 36))) n) a
    WHERE LEN(remainder) > 0)
SELECT
    max(iif(grpn%5=0,grp,null)) Val1
    ,max(iif(grpn%5=1,grp,null)) Val2
    ,max(iif(grpn%5=2,grp,null)) Val3
    ,max(iif(grpn%5=3,grp,null)) Val4
    ,max(iif(grpn%5=4,grp,null)) Val5
FROM cte
group by grpn/5

【讨论】:

  • 我接受这个作为答案。基于 Søren Kongstad 示例,我能够构建一个可行的功能。谢谢。
【解决方案2】:

如果您的 MS Sql Server 版本是 2017 或更高版本,那么您可以为此使用 STRING_SPLIT 和 STRING_AGG。

示例:

declare @OriginalStr nvarchar(max);
set @OriginalStr = N'The quick brown fox jumps over the lazy dog. The quick brown fox jumps over the lazy dog. The quick brown fox jumps over the lazy dog. The quick brown fox jumps over the lazy dog. The quick brown fox jumps over the lazy dog';

declare @ColumnLen int = 35;

declare @parts table 
(
  part_id int identity(1,1) primary key,
  part_content nvarchar(max)
);

declare @lines table 
(
  line_id int primary key,
  line_content nvarchar(max)
);

-- splitting the string on the spaces
insert into @parts (part_content)
select value
from string_split(@OriginalStr, ' ') spl

-- glueing the parts back together
insert into @lines (line_id, line_content)
select 
 lineNr,
 string_agg(part_content, ' ') as line
from
(
  select part_content
  , floor(1.0*(sum(len(part_content)+1) 
               over (order by part_id))/(@ColumnLen-1))+1 as lineNr
  from @parts 
) q
group by lineNr;

-- pivoting the lines
select
ceiling((line_id-0.1)/5) as [Level],
max(case when line_id%5 = 1 then line_content end) as Val1,
max(case when line_id%5 = 2 then line_content end) as Val2,
max(case when line_id%5 = 3 then line_content end) as Val3,
max(case when line_id%5 = 4 then line_content end) as Val4,
max(case when line_id%5 = 0 then line_content end) as Val5
from @lines l
group by ceiling((line_id-0.1)/5)
order by [Level];

GO
水平 | VAL1 | val2 | VAL3 | val4 | VAL5 :---- | :-------------------------------- | :-------------------------------- | :--------------------------------- | :--------------------------------- | :---------------------------- 1 |敏捷的棕狐跳过|懒惰的狗。敏捷的棕狐|跳过懒狗。快速|棕狐跳过懒狗。 |敏捷的棕色狐狸跳过 2 |懒惰的狗。敏捷的棕狐|跳过懒狗| 天天要闻 | |

db小提琴here

在旧版本的 Sql Server 上,这应该可以为行填充表变量。

with rcte as
(
   select 
   1 as lineNr,
   1 as strPos,
   @ColumnLen + 1 - cast(
     charindex(N' ',
       reverse(
         substring(@OriginalStr, 1, @ColumnLen)
       ) 
     ) as int) as lineLen
   
   union all
   
   select
   lineNr + 1,
   strPos + lineLen,
   @ColumnLen + 1 - cast(
     charindex(N' ',
       reverse(
         substring(@OriginalStr, strPos+lineLen, @ColumnLen)
       )
     ) as int)
   from rcte
   where strPos+lineLen < len(@OriginalStr)
)
insert into @lines (line_id, line_content)
select 
 lineNr,
 line = rtrim(substring(@OriginalStr, pos, lineLen))
from rcte;

【讨论】:

  • 感谢 LukStorms,但公司政策阻止我这样做,因为并非所有客户都了解最新和最优秀的产品。
  • @vlad Thx,我明白了。好吧,无论如何,我在答案中添加了我的递归 CTE 版本。它采用了一种稍微不同的方法,首先计算线条的位置和长度。
  • 来自String_Split:“输出行可能是任何顺序。顺序保证与输入字符串中子字符串的顺序相匹配。”跨度>
猜你喜欢
  • 1970-01-01
  • 2017-06-18
  • 2014-09-10
  • 2013-11-04
  • 2011-11-16
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多