Разбить строку в SQL Server до максимальной длины, возвращая каждый в виде строки

Есть ли способ разбить строку (из определенного столбца) на n-числовые символы без разбивки слов, с каждым результатом в отдельной строке?

Пример:

2012-04-24 Change request #3 for the contract per terms and conditions and per John Smith in the PSO department  Customer states terms should be Net 60 not Net 30.  Please review signed contract for this information.

Результаты:

2012-04-24 Change request #3 for the contract per terms and conditions and per John Smith in the
PSO department  Customer states terms should be Net 60 not Net 30.
Please review signed contract for this information.

Я знаю, что могу использоватьcharindex чтобы найти последний пробел, но я не уверен, как я могу получить оставшиеся и вернуть их в виде строк.

 Chad01 июн. 2012 г., 17:04
Посмотрите на функцию Джеффа Модена Split8khere, Это, вероятно, можно изменить, чтобы сделать то, что вы хотите.
 Daniel A. White01 июн. 2012 г., 17:05
@TimSchmelter его 100
 SliverNinja - MSFT01 июн. 2012 г., 17:05
Вы всегда можете использовать .NET Regex через SQL CLR UDF.
 Rango01 июн. 2012 г., 17:01
Какова ваша максимальная длина?
 Gordon Linoff01 июн. 2012 г., 17:03
Проверьте код для PrintMax, который я нашел здесь. , ,weblogs.asp.net/bdill/archive/2007/09/29/…, Он ищет разрывы строк, но вы также можете легко искать пробелы.

Ответы на вопрос(4)

Я прочитал несколько статей, и каждая из них имеет ошибку или плохую производительность или не работает с небольшим или большим отрезком, который мы хотим. Вы можете прочитать мои комментарии даже в этой статье ниже любого ответа. Наконец я нашел хороший ответ и решил поделиться им в этом вопросе. Я не проверял производительность в различных сценариях, но я думаю, что это приемлемо и работает нормально для небольших и больших отрезков. Это код:

CREATE function SplitString
(   
    @str varchar(max),
    @length int
)
RETURNS @Results TABLE( Result varchar(50),Sequence INT ) 
AS
BEGIN

DECLARE @Sequence INT 
SET @Sequence = 1

    DECLARE @s varchar(50)
    WHILE len(@str) > 0
    BEGIN
        SET @s = left(@str, @length)
        INSERT @Results VALUES (@s,@Sequence)

        IF(len(@str)<@length)
        BREAK

        SET @str = right(@str, len(@str) - @length)
        SET @Sequence = @Sequence + 1
    END
    RETURN 
END

и источник @Rhyno ответ на этот вопрос:TSQL UDF для разделения строки каждые 8 символов

Надеюсь, это поможет.

 18 авг. 2017 г., 01:04
@ Keiki Спасибо за внимание, я проверю это как можно скорее.
 17 авг. 2017 г., 23:30
Это не отвечает на вопрос OP, потому что он не сохраняет слова (т.е., если в 50-символьном знаке есть слово, он будет разрезать его пополам вместо перемещения целого слова на следующую строку). Но ... это именно то, что мне было нужно. :)

Просто чтобы посмотреть, можно ли это сделать, я пришел к решению, которое не зацикливается. Он основан начужая функция разбить строку на основе разделителя.

Note: Это требует, чтобы вы знали максимальную длину токена заранее. Функция прекратит возвращать строки при обнаружении токена, длина которого превышает указанную длину строки. Возможно, скрываются и другие ошибки, поэтому используйте этот код по собственному усмотрению.

CREATE FUNCTION SplitLines
(
    @pString    VARCHAR(7999),
    @pLineLen   INT,
    @pDelim     CHAR(1)
)
RETURNS TABLE
   WITH SCHEMABINDING
AS  
RETURN
WITH
      E1(N) AS ( --=== Create Ten 1's
                 SELECT 1 UNION ALL SELECT 1 UNION ALL
                 SELECT 1 UNION ALL SELECT 1 UNION ALL
                 SELECT 1 UNION ALL SELECT 1 UNION ALL
                 SELECT 1 UNION ALL SELECT 1 UNION ALL
                 SELECT 1 UNION ALL SELECT 1 --10
               ),
      E2(N) AS (SELECT 1 FROM E1 a, E1 b),   --100
      E4(N) AS (SELECT 1 FROM E2 a, E2 b),   --10,000
cteTally(N) AS (SELECT ROW_NUMBER() OVER (ORDER BY (SELECT N)) FROM E4),
lines AS (
  SELECT TOP 1
         1 as LineNumber,
         ltrim(rtrim(SUBSTRING(@pString, 1, N))) as Line,
         N + 1 as start
  FROM cteTally
  WHERE N <= DATALENGTH(@pString) + 1
    AND N <= @pLineLen + 1
    AND SUBSTRING(@pString + @pDelim, N, 1) = @pDelim
  ORDER BY N DESC
UNION ALL
  SELECT LineNumber, Line, start
  FROM (
    SELECT LineNumber + 1 as LineNumber,
           ltrim(rtrim(SUBSTRING(@pString, start, N))) as Line,
           start + N + 1 as start,
           ROW_NUMBER() OVER (ORDER BY N DESC) as r
    FROM cteTally, lines
    WHERE N <= DATALENGTH(@pString) + 1 - start
      AND N <= @pLineLen
      AND SUBSTRING(@pString + @pDelim, start + N, 1) = @pDelim
  ) A
  WHERE r = 1
)
SELECT LineNumber, Line
FROM lines

Это на самом деле довольно быстро, и вы можете делать крутые вещи, такие как присоединиться к нему. Вот простой пример, который получает первую «строку» из каждой строки в таблице:

declare @table table (
  id int,
  paragraph varchar(7999)
)
insert into @table values (1, '2012-04-24 Change request #3 for the contract per terms and conditions and per John Smith in the PSO department  Customer states terms should be Net 60 not Net 30.  Please review signed contract for this information.')
insert into @table values (2, 'Is there a way to split a string (from a specific column) to n-number chars without breaking words, with each result in its own row?')

select t.id, l.LineNumber, l.Line, len(Line)
from @table t
cross apply SplitLines(t.paragraph, 42, ' ') l
where l.LineNumber = 1
 20 окт. 2014 г., 11:10
Большая ошибка Попробуйте установить pLineLen в качестве небольшого значения, например, 5, и вы увидите, что он не может получить результат. Я проверил его по переменной длины как pString и обнаружил, что когда pString достаточно длинный, это работает для pLineLen = & gt; 11 и и может быть изменено на длину pString, но при любой длине вы не можете установить pLineLen меньше 11. Это такая большая проблема, и я думаю, что она бесполезна в большинстве сценариев.
 30 мая 2017 г., 17:05
После небольшого количества экспериментов, я думаю,@pLineLen должен быть установлен как минимум на максимальный размер токена плюс два. Это имеет смысл, потому что иначе вы бы разбили слово пополам. В приведенном выше примере «информация». длина 12, так@pLineLen должно быть установлено как минимум 14, чтобы получить все.
 30 мая 2017 г., 19:49
@Bret, спасибо, что указал на это. Я исправил некоторые ошибки в коде, так что теперь он должен вести себя (в основном) правильно, пока@pLineLen по крайней мере такой же большой, как максимальная длина токена.

Я знаю, что это немного поздно, но рекурсивный cte позволит достичь этого.

Также вы можете использовать начальную таблицу, содержащую последовательность чисел для подачи в подстроку в качестве множителя для начального индекса.

 01 июн. 2012 г., 22:34
Мне нравится ход твоих мыслей :)
 20 окт. 2014 г., 11:16
@Chris Я ответил на вопрос на сайте информационной безопасности и получил 5 минус за то, что не объяснил ответ подробно, даже если я написал несколько полезных ссылок дополнительно. Ваш ответ бесполезен без объяснения, кода, образца и ссылки. я не ставлю минус в этом, но, пожалуйста, будьте осторожны и сделайте все возможное, когда вы ответите на вопрос.
 20 окт. 2014 г., 15:10
@ChrisMoutray Я вижу твою хорошую идею и пытаюсь помочь. Я просто хочу намекнуть вам из-за моего опыта. Можете себе представить -5 баллов? :) Наконец, опытный пользователь решил удалить ответ, и даже я написал новый ответ с полным объяснением, без единой точки +1 :) вы можете посмотреть на это, чтобы узнать мою честность:security.stackexchange.com/questions/49315/… Во всяком случае, я ценю вашу попытку и быть здесь.
 20 окт. 2014 г., 13:21
@QMaster Я ценю ваш комментарий и очень хочу, чтобы у меня было время дать подробные объяснения на мои ответы - к сожалению, публикация ответовmostly хобби без благодарностей. Я попытался ответить в этом случае, потому что я, хотя и представляю другую точку зрения, был бы полезен, и я думаю, что Чад Хендерсон дал ответ, после которого показывает пример CTE. Также лично я думаю, что отрицательные голоса должны быть сохранены за вводящие в заблуждение или неправильные ответы. Исходя из количества голосов, которые получил мой ответ, он стоит на грани того, чтобы быть ни полезным, ни неправильным.
Решение Вопроса

Попробуйте что-то вроде этого. Может быть, вы можете создать функцию SQL следующей реализации.

DECLARE @Str VARCHAR(1000)
SET @Str = '2012-04-24 Change request #3 for the contract per terms and conditions and per John Smith in the PSO department  Customer states terms should be Net 60 not Net 30.  Please review signed contract for this information.'

DECLARE @End INT
DECLARE @Split INT

SET @Split = 100

declare @SomeTable table
(
  Content varchar(3000)
)


WHILE (LEN(@Str) > 0)
BEGIN
    IF (LEN(@Str) > @Split)
    BEGIN
        SET @End = LEN(LEFT(@Str, @Split)) - CHARINDEX(' ', REVERSE(LEFT(@Str, @Split)))
        INSERT INTO @SomeTable VALUES (RTRIM(LTRIM(LEFT(LEFT(@Str, @Split), @End))))
        SET @Str = SUBSTRING(@Str, @End + 1, LEN(@Str))
    END
    ELSE
    BEGIN
        INSERT INTO @SomeTable VALUES (RTRIM(LTRIM(@Str)))
        SET @Str = ''
    END
END

SELECT *
FROM @SomeTable

Вывод будет таким:

2012-04-24 Change request #3 for the contract per terms and conditions and per John Smith in the
PSO department  Customer states terms should be Net 60 not Net 30.  Please review signed contract
for this information.
 Daniel A. White01 июн. 2012 г., 18:45
Я не хочу, чтобы он печатался, я хочу, чтобы он возвращался в виде отдельных строк в наборе записей.
 30 апр. 2015 г., 04:30
Спасибо за это ... это один из тех фрагментов кулинарной книги, которые вам нужны в заднем кармане. Я использовал это в качестве основы для чего-то, что мне было нужно, но также многому научился, возиться с этим.
 Daniel A. White01 июн. 2012 г., 18:54
спасибо, что начал меня.
 01 июн. 2012 г., 18:50
И вот, пожалуйста. Я обновил свой код.
 19 окт. 2014 г., 20:17
Это так медленно и бесполезно в небольших размерах. Например, когда я хочу разделить каждые 3 символа.

Ваш ответ на вопрос