TSQL — в строке заменяйте символ на фиксированный каждые 2 символа

#tsql #overlap

Вопрос:

Я не могу заменить каждые 2 символа строки на ‘.’

выберите МАТЕРИАЛ(‘abcdefghi’, 3, 1, ‘.’) c3,МАТЕРИАЛ(‘abcdefghi’, 5, 1, ‘.’) c5,МАТЕРИАЛ(‘abcdefghi’, 7, 1, ‘.’) c7,МАТЕРИАЛ(‘abcdefghi’, 9, 1, ‘.’) c9

если я использую МАТЕРИАЛ, я должен впоследствии перекрывать строки c3, c5, c7 и c9. но я не могу найти метод

Вы не могли бы мне помочь?

начальная строка:

abcdefghi

результат, который я хотел бы получить, таков

ab.de.gh.

строка может содержать до 50 символов

Комментарии:

1. похоже, вы могли бы извлечь выгоду из рекурсивного CTE — однако примеры данных и желаемые результаты были бы полезны, поскольку неясно, что вы имеете в виду.

2. результат, который я хотел бы получить, таков ‘ab.de.gh.’

Ответ №1:

Создайте таблицу чисел / подсчетов / цифр, если у вас ее еще нет, то вы можете использовать ее для таргетирования каждой позиции символов:

 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 lt;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 lt;Len(t.s) ) select result=(select ''   ch  from r r2  where r2.id=r.id  for xml path('') ) from r group by r.id  

Комментарии:

1. Спасибо за ответ. но я не могу использовать string_agg для своей версии sql server

2. Вы используете более старую версию (2016 или более раннюю), и в этом случае вместо этого вы можете использовать устаревший for xml метод

3. можете ли вы привести мне пример использования? в прошлом я использовал xml для объединения записей, но не для наложения результатов

4. @neotrojan отредактировал выше

5. если я заменю «abcdefghi» таблицей, например. (выберите поле из таблицы) ошибка «Подзапрос вернул более одного значения. Это не допускается для подзапросов, следующих за символами =,! =, lt;, lt;=,gt;,lt;=,gt;gt; = или используется как выражение. «это работает только в том случае, если есть только одна запись

Ответ №2:

Вы можете попробовать это так:

Проще всего может быть причудливое обновление, как здесь:

 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;  

Лучше/чище/Красивее был рекурсивный 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/2lt;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 . Это позволит отобразить все первые строки (по одному идентификатору).