2012-06-01 1 views
4

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

Пример:

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, чтобы найти последнее место, но им не уверен, как я могу получить оставшиеся, и вернуть их в ряды ,

+0

Самая большая длина? –

+0

Проверьте код PrintMax, который я нашел здесь. , , http://weblogs.asp.net/bdill/archive/2007/09/29/sql-server-print-max.aspx. Он ищет разрывы строк, но вы можете так же легко искать пробелы. –

+0

Взгляните на функцию Split8k Джеффа Модена [здесь] (http://www.sqlteam.com/forums/topic.asp?TOPIC_ID=153458). Вероятно, это может быть изменено, чтобы делать то, что вы хотите. – Chad

ответ

3

Попробуйте что-нибудь подобное. Может быть, вы можете создать 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. 
+0

Я не хочу, чтобы он был напечатан, я хочу, чтобы он возвращался как отдельные строки в наборе записей. –

+0

И вот ты туда. Я обновил свой код. –

+0

спасибо, что меня начали. –

0

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

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

+0

Мне нравится, как вы думаете :) – Chad

+0

@ Крис я ответил на вопрос в разделе «Безопасность информации» и получил 5 минус, просто чтобы не объяснить ответ подробно, даже я написал некоторые полезные ссылки дополнительно. Ваш ответ не полезен без объяснения, кода, образца и ссылки. я не минус, но будьте внимательны и делайте все возможное, когда вы отвечаете на вопрос. – QMaster

+0

@QMaster Я ценю ваш комментарий и действительно хочу, чтобы у меня было время, чтобы дать полное объяснение моим ответам. К сожалению, ответы на сообщения - это в основном * благодарственное хобби. Я потрудился ответить в этом случае, потому что, хотя я бы добавил другую точку зрения, было бы полезно, и я думаю, что Чад Хендерсон дал ответ, после которого будет показан пример CTE. Также лично я считаю, что отрицательные голоса должны быть сохранены для вводящих в заблуждение или неправильных ответов. Основываясь на количестве голосов, полученных моим ответом, он сидит на заборе не быть ни полезным, ни неправильным. –

2

Просто чтобы убедиться, что это можно сделать, я придумал решение, которое не работает. Он основан на somebody else's function, чтобы разбить строку на основе разделителя.

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

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 
+0

Большая ошибка. Попробуйте установить pLineLen как небольшое значение, например 5, и вы увидите, что он не может получить результат. Я проверил его на переменную длину как pString и нашел, когда pString достаточно долго, это работает для pLineLen => 11 и может быть изменено длиной pString, но любой длины вы не можете установить pLineLen меньше 11. Это такая большая проблема и я считаю бесполезным в большинстве сценариев. – QMaster

+0

После небольшого эксперимента, я думаю, что '@ pLineLen' должен быть установлен как минимум на максимальный размер токена плюс два. Это имеет смысл, потому что в противном случае вы разделили бы слово наполовину. В приведенном выше примере «информация». длина 12, поэтому для '@ pLineLen' должно быть установлено не менее 14, чтобы получить все. – Bret

+1

@Bret, спасибо, что указал. Я исправил некоторые ошибки в коде, так что теперь он должен вести себя (главным образом) правильно, если '@ pLineLen' не меньше, чем максимальная длина маркера. – Chad

1

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

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 To Split String Every 8 Characters

Надеется, что это поможет.

+1

Это не отвечает на вопрос OP, потому что он не сохраняет слова (IE, если есть слово на знаке с 50 символами, оно будет срезать его пополам, а не перемещать целое слово на следующую строку). Но ... это действительно то, что мне нужно. :) – Keiki

+0

@Keiki Спасибо за ваши пожелания, я проверю это как можно скорее. – QMaster

Смежные вопросы