Проблемы с разбиением значений на несколько строк с использованием T-SQL - PullRequest
4 голосов
/ 22 ноября 2011

У меня есть Sql Server 2K8 R2 DB с таблицей, в которой есть столбец, содержащий кратные значения, разделенные (char 13 и char 10).

Я создаю скрипт для импорта данных в правильно нормализованной схеме.

Моя исходная таблица содержит что-то вроде этого:

ID   |    Value
________________
1    |    line 1
          line 2
________________
2    |    line 3
________________
3    |    line 4
          line 5
          line 6
________________

и т. Д.

[править] К вашему сведению, Id - целое число, а значение - nvarchar (3072) [/ edit]

То, что я хочу, это запросить таблицу, чтобы вывести что-то вроде этого:

ID   |    Value
________________
1    |    line 1
________________
1    |    line 2
________________
2    |    line 3
________________
3    |    line 4
________________
3    |    line 5
________________
3    |    line 6
________________

Я прочитал много ответов здесь на SO, а также в Интернете, и я считаю, что использование master..sptvalues должно быть решением. Особенно я пытался воспроизвести решение вопроса Разделить один столбец на несколько строк . Однако безуспешно (подозрение на наличие двух символов приводит к проблемам).

К настоящему времени я написал этот запрос:

SELECT
    T.ID,
    T.Value, 
    RIGHT(LEFT(T.Value,spt.Number-1),
    CHARINDEX(char(13)+char(10),REVERSE(LEFT(char(13)+char(10)+T.Value,spt.Number-1)))) as Extracted
FROM 
    master..spt_values spt,
    ContactsNew T
WHERE
    Type = 'P' AND 
    spt.Number BETWEEN 1 AND LEN(T.Value)+1
    AND
        (SUBSTRING(T.Value,spt.Number,2) = char(13)+char(10) OR SUBSTRING(T.Value,spt.Number,2) = '')

Этот запрос, к сожалению, возвращается:

ID   |    Value    |   Extracted
________________________________   
1    |    line 1   |   <blank>
          line 2   |   
________________________________   
1    |    line 1   |   line 2
          line 2   |   
________________________________   
2    |    line 3   |   <blank>
________________________________   
3    |    line 4   |   <blank>
          line 5   |
          line 6   |
________________________________   
3    |    line 4   |   line 5
          line 5   |   line 6
          line 6   |
________________________________   
3    |    line 4   |   line 6
          line 5   |
          line 6   |
________________________________  

<blank> - пустая строка, а не пустая строка.

Буду признателен за помощь в настройке моего запроса.

[Edit2] Моя исходная таблица содержит менее 200 записей, и производительность не требуется, поэтому я нацеливаюсь на простое решение, а не на эффективное [Edit2]

[Edit3] Исходная база данных доступна только для чтения. Я не могу добавить хранимую процедуру, функцию или тип clr. Я должен сделать это в одном запросе. [Edit3]

[Edit4] Что-то странное ... кажется, что пробелы также рассматриваются как разделители.

Если я запускаю следующий запрос:

SELECT
    T.ID,
    replace(T.Value, '#', ' '), 
    replace(RIGHT(
        LEFT(T.Value,spt.Number-1),
        CHARINDEX( char(13) + char(10),REVERSE(LEFT(char(10) + char(13)+T.Value,spt.Number-0)))
        ), '#', ' ')
FROM 
    master..spt_values spt,
    (   
        select contactID, 
        replace(Value,' ', '#') Value
        from ContactsNew where Value is not null
    ) T
WHERE
    Type = 'P' AND 
    spt.Number BETWEEN 1 AND LEN(T.Value)+1
    AND
        (SUBSTRING(T.Value,spt.Number,2) =  char(13) + char(10) OR SUBSTRING(T.Value,spt.Number,1) = '')

Я получил правильное количество возвратов (однако, все еще с неправильными значениями) при выполнении этого запроса:

SELECT
    T.ID,
    T.Value, 
    RIGHT(
        LEFT(T.Value,spt.Number-1),
        CHARINDEX( char(13) + char(10),REVERSE(LEFT(char(10) + char(13)+T.Value,spt.Number-0)))
        )
FROM 
    master..spt_values spt,
    (   
        select contactID, 
        Value
        from ContactsNew where Value is not null
    ) T
WHERE
    Type = 'P' AND 
    spt.Number BETWEEN 1 AND LEN(T.Value)+1
    AND
        (SUBSTRING(T.Value,spt.Number,2) =  char(13) + char(10) OR SUBSTRING(T.Value,spt.Number,1) = '')

разделяется и на пробелы

Ответы [ 2 ]

1 голос
/ 22 ноября 2011

РЕДАКТИРОВАТЬ # 1: я удалил исходный текст ответа. Попробуйте следующий запрос. Я немного изменил твою логику. Если у вас есть какие-либо вопросы по этому поводу, не стесняйтесь спрашивать в комментариях. Если вам нужен еще один разделитель, просто введите другой вложенный запрос, чтобы заменить этот разделитель на CHAR (13) + CHAR (10).

SELECT 
* 
FROM 
(
    SELECT
        T.ID,
        T.Value,
        CASE
            WHEN CHARINDEX(CHAR(13) + CHAR(10), SUBSTRING(T.Value, spt.number, LEN(T.Value) - spt.Number + 1)) > 0 THEN
                LEFT(
                    SUBSTRING(T.Value, spt.number, LEN(T.Value) - spt.Number + 1), 
                    CHARINDEX(CHAR(13) + CHAR(10), SUBSTRING(T.Value, spt.number, LEN(T.Value) - spt.Number + 1)) - 1)
 /* added by Steve B. see comments for the reasons */
        when len(T.Value) = spt.Number then right(t.Value, spt.number -1) 
 /* end of edit */
            ELSE
                SUBSTRING(T.Value, spt.number, LEN(T.Value) - spt.Number + 1)
        END EXTRACTED
    FROM 
        master..spt_values spt,
        ContactsNew T
    WHERE
        Type = 'P' AND 
        spt.Number BETWEEN 1 AND LEN(T.Value)+1
) X
WHERE 
    EXTRACTED <> '' AND
    (
        LEFT(X.VALUE, LEN(EXTRACTED)) = EXTRACTED OR 
        X.Value LIKE '%' + CHAR(13) + CHAR(10) + EXTRACTED + CHAR(13) + CHAR(10) + '%' OR
        X.Value LIKE '%' + CHAR(13) + CHAR(10) + EXTRACTED
    )
0 голосов
/ 22 ноября 2011

Пример запроса, показывающий, как выполнить этот тип операции с некоторыми тестовыми данными, подобными описанным.

Если вы не можете объявить переменные в своем окончательном утверждении, вы можете найти / заменить их для их значений, но это немного упрощает процесс.

Это работает путем замены CR+LF на один символ перед выполнением разделения.
Если '|' используется в ваших данных, выберите другой отдельный символ, которыйне использовать в качестве временного разделителя.

declare @crlf nvarchar(2) = char(10) + char(13)
declare @cDelim nvarchar(1) = N'|'

-- test data
declare @t table
(id int
,value nvarchar(3072))

insert @t
select 1, 'line1' + @crlf + 'line2'
union all select 2, 'line3'
union all select 3, 'line4' + @crlf + 'line5' + @crlf + 'line6'
-- /test data



;WITH charCTE
AS
( 
        --split the string into a dataset
        SELECT  D.id, D.value, SUBSTRING(D.s,n,CHARINDEX(@cDelim, D.s + @cDelim,n) -n) AS ELEMENT
        FROM (SELECT id, value, REPLACE(value,@crlf,@cDelim) as s from @t)    AS D
        JOIN (SELECT TOP 3072 ROW_NUMBER() OVER (ORDER BY a.type, a.number, a.name) AS n
              FROM master.dbo.spt_values a 
              CROSS 
              JOIN master.dbo.spt_values b 
              ) AS numsCte
        ON n <= LEN(s)
        AND SUBSTRING(@cDelim + s,n,1) = @cDelim 
)
SELECT id, ELEMENT
FROM charCTE
order by id, element
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...