【问题标题】:TSQL - in a string, replace a character with a fixed one every 2 charactersTSQL - 在一个字符串中,每 2 个字符用一个固定的字符替换一个字符
【发布时间】:2021-12-23 04:01:41
【问题描述】:

我不能将字符串的每 2 个字符替换为 '.'

选择 STUFF('abcdefghi', 3, 1, '.') c3,STUFF('abcdefghi', 5, 1, '.') c5,STUFF('abcdefghi', 7, 1, '.') c7,STUFF('abcdefghi', 9, 1, '.') c9

如果我使用 STUFF,我应该随后将字符串 c3、c5、c7 和 c9 重叠。但我找不到方法

你能帮帮我吗?

初始字符串:

abcdefghi

我想要的结果是

ab.de.gh.

字符串最多可包含 50 个字符

【问题讨论】:

  • 听起来您可能会从递归 CTE 中受益 - 但是样本数据和所需结果会很有用,因为您不清楚您的意思。
  • 我想要的结果是'ab.de.gh.'

标签: tsql overlap


【解决方案1】:

你可以这样试试:

最简单的可能是古怪的更新,就像这里:

DECLARE @string VARCHAR(100)='abcdefghijklmnopqrstuvwxyz';

SELECT @string = STUFF(@string,3*A.pos,1,'.')
FROM (SELECT TOP(LEN(@string)/3) ROW_NUMBER() OVER(ORDER BY (SELECT NULL))
      FROM master..spt_values) A(pos);

SELECT @string;

Better/Cleaner/Prettier 是递归 CTE:

我们使用一个声明的表来拥有一些表格样本数据

DECLARE @tbl TABLE(ID INT IDENTITY, SomeString VARCHAR(200));
INSERT INTO @tbl VALUES('')
                      ,('a')
                      ,('ab')
                      ,('abc')
                      ,('abcd')
                      ,('abcde')
                      ,('abcdefghijklmnopqrstuvwxyz');

--查询

WITH recCTE AS
(
    SELECT ID
          ,SomeString 
          ,(LEN(SomeString)+1)/3 AS CountDots
          ,1 AS OccuranceOfDot
          ,SUBSTRING(SomeString,4,LEN(SomeString)) AS RestString
          ,CAST(LEFT(SomeString,2) AS VARCHAR(MAX)) AS Growing 
    FROM @tbl 

    UNION ALL

    SELECT t.ID
          ,r.SomeString
          ,r.CountDots
          ,r.OccuranceOfDot+2
          ,SUBSTRING(RestString,4,LEN(RestString)) 
          ,CONCAT(Growing,'.',LEFT(r.RestString,2)) 
    FROM @tbl t
    INNER JOIN recCTE r ON t.ID=r.ID
    WHERE r.OccuranceOfDot/2<r.CountDots-1
)
SELECT TOP 1 WITH TIES ID,Growing 
FROM recCTE
ORDER BY ROW_NUMBER() OVER(PARTITION BY ID ORDER BY OccuranceOfDot DESC);

--结果

1   
2   a
3   ab
4   ab
5   ab
6   ab.de
7   ab.de.gh.jk.mn.pq.st.vw.yz

简单的想法

  • 我们使用递归 CTE 来遍历字符串
  • 我们将所需部分与一个点一起添加
  • 我们停止,当剩余长度短到继续时
  • ORDER BY ROW_NUMBER() OVER()TOP 1 WITH TIES 有点神奇。这将允许出现所有第一行(每个 ID 的第一行)。

【讨论】:

    【解决方案2】:

    创建一个 numbers/tally/digits 表,如果您还没有,那么您可以使用它来定位每个字符位置:

    with digits as ( /* This would be a real table, here it's just to test */
        select n from (values(1),(2),(3),(4),(5),(6),(7),(8),(9),(10))x(n)
    ), t as (
        select 'abcdefghi' as s
    )
    select String_Agg( case when d.n%3 = 0 then '.' else Substring(t.s, d.n, 1) end, '')
    from t
    cross apply digits d
    where d.n <Len(t.s)
    

    for xml 与现有表一起使用

    with digits as (
        select n from (values(1),(2),(3),(4),(5),(6),(7),(8),(9),(10))x(n)
    ),
    r as (
        select t.id, case when d.n%3=0 then '.' else Substring(t.s, d.n, 1) end ch
        from t
        cross apply digits d
        where d.n <Len(t.s)
    )
    select result=(select '' + ch
        from r r2
        where r2.id=r.id
        for xml path('')
    )
    from r
    group by r.id
    

    【讨论】:

    • 感谢您的回复。但我不能将 string_agg 用于我的 sql server 版本
    • 您使用的是旧版本(2016 或更早版本),在这种情况下,您可以改用旧版 for xml 方法
    • 你能给我一个使用的例子吗?过去我使用 xml 来连接记录,但不覆盖结果
    • @neotrojan 以上已编辑
    • 如果我将 'abcdefghi' 替换为表格,例如。 (从表中选择字段)错误“子查询返回了多个值。这对于字符 =、!=、、> = 或用作表达式的子查询是不允许的。”它仅在以下情况下有效只有一条记录
    猜你喜欢
    • 2021-06-15
    • 2013-12-03
    • 2021-10-27
    • 2012-01-01
    • 1970-01-01
    • 2012-07-16
    • 1970-01-01
    • 2020-03-30
    • 1970-01-01
    相关资源
    最近更新 更多