#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
. Это позволит отобразить все первые строки (по одному идентификатору).