Разделить строку на 3 столбца - с изюминкой - PullRequest
0 голосов
/ 10 октября 2018

У меня очень длинная текстовая строка, импортируемая в таблицу.Я хотел бы разделить строку;У меня есть процедура для извлечения данных в таблицу, но она создает все данные в одном поле в таблице.

Пример текста:

05/10/2018 21:14,#FXAAF00123456,,Cup 1 X Plane,0.00000,OK,Cup 1 Y Plane,0.00000,OK,Cup 1 Z Plane,40.64252,OK,Cup 2 X Plane,77.89434,OK,..etc

(Тестовая строка оченьдлиннее этого, в области 1500-1700 символов, но с той же структурой в остальной части строки).

Эти данные представляют собой серию тестовых измерений с названием значения, значениеми индикатор OK / NOK.

Я хочу, чтобы результаты сохранялись в таблице (переменной) с тремя полями, поэтому приведенные выше данные становятся такими:

Field1|Field2|Field3
05/10/2018 21:14|#FXAAF00123456|null|
Cup 1 X Plane|0.00000|OK|
Cup 1 Y Plane|0.00000|OK|
Cup 1 Z Plane|40.64252|OK|
Cup 2 X Plane|77.89434|OK|
...etc

Я использую этофункция для разделения строки на табличную переменную:

CREATE FUNCTION [dbo].[fnSplitString]
    (
        @InputString NVARCHAR(MAX),
        @Delim VARCHAR(255)
    )
    RETURNS TABLE
    AS
        RETURN ( SELECT [Value] FROM 
          ( 
            SELECT 
              [Value] = LTRIM(RTRIM(SUBSTRING(@InputString, [Number],
              CHARINDEX(@Delim, @InputString + @Delim, [Number]) - [Number])))
            FROM (SELECT Number = ROW_NUMBER() OVER (ORDER BY name)
              FROM sys.all_objects) AS x
              WHERE Number <= LEN(@InputString)
              AND SUBSTRING(@Delim + @InputString, [Number], LEN(@Delim)) = @Delim
          ) AS y
        );

Как это можно изменить, чтобы получить требуемый вывод?

Ответы [ 3 ]

0 голосов
/ 10 октября 2018

Вы можете попробовать этот крошечный метод линейного разбиения.

DECLARE @s VARCHAR(MAX)='05/10/2018 21:14,#FXAAF00123456,,Cup 1 X Plane,0.00000,OK,Cup 1 Y Plane,0.00000,OK,Cup 1 Z Plane,40.64252,OK,Cup 2 X Plane,77.89434,OK';


;WITH
 a AS (SELECT n=0, i=-1, j=0 UNION ALL SELECT n+1, j, CAST(CHARINDEX(',', @s, j+1) AS INT) FROM a WHERE j > i)
,b AS (SELECT n, SUBSTRING(@s, i+1, IIF(j>0, j, LEN(@s)+1)-i-1) s FROM a WHERE i >= 0)
,c AS (SELECT n,(n-1) % 3 AS Position,(n-1)/3 AS RowIndex,s FROM b) 
SELECT MAX(CASE WHEN Position=0 THEN s END) AS part1
      ,MAX(CASE WHEN Position=1 THEN s END) AS part2
      ,MAX(CASE WHEN Position=2 THEN s END) AS part3
FROM c
GROUP BY RowIndex
OPTION (MAXRECURSION 0);

Результат

part1               part2           part3
05/10/2018 21:14    #FXAAF00123456  
Cup 1 X Plane       0.00000         OK
Cup 1 Y Plane       0.00000         OK
Cup 1 Z Plane       40.64252        OK
Cup 2 X Plane       77.89434        OK

Подсказка

Вы можете изменить функцию разделения на рекурсивный подход, описанный выше,С одной стороны, вы ограничены длиной строки в sys.all_objects, которая может быть меньше, чем ваш ввод.С другой стороны, ваш подход должен проверять каждую позицию, в то время как рекурсивный подход прыгает с места на место.Должно быть быстрее ...
Это можно легко открыть для многосимвольного разделителя , если необходимо ...

ОБНОВИТЬ другой подход без рекурсии

...что делает его неуклюжим для использования в функции сплиттера (из-за OPTION MAXRECURSION(0), который должен быть помещен в конце запроса и не может находиться внутри функции).Попробуйте:

;WITH
 a(Casted) AS (SELECT CAST('<x>' + REPLACE((SELECT @s AS [*] FOR XML PATH('')),',','</x><x>') + '</x>' AS XML))
,b(s,RowIndex,Position) AS 
(
    SELECT x.value(N'text()[1]','nvarchar(max)')
          ,(ROW_NUMBER() OVER(ORDER BY(SELECT NULL)) -1) /3 
          ,(ROW_NUMBER() OVER(ORDER BY(SELECT NULL)) -1) %3 
    FROM a
    CROSS APPLY Casted.nodes(N'/x') X(x)
)
SELECT RowIndex
      ,MAX(CASE WHEN Position=0 THEN s END) AS part1
      ,MAX(CASE WHEN Position=1 THEN s END) AS part2
      ,MAX(CASE WHEN Position=2 THEN s END) AS part3
FROM b
GROUP BY RowIndex;

Подсказка:

Использование (SELECT @s AS [*] FOR XML PATH('')) позволит сохранить этот подход с запрещенными символами ...

0 голосов
/ 10 октября 2018

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

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

declare @str nvarchar(max) = '05/10/2018 21:14,#FXAAF00123456,,Cup 1 X Plane,0.00000,OK,Cup 1 Y Plane,0.00000,OK,Cup 1 Z Plane,40.64252,OK,Cup 2 X Plane,77.89434,OK'
select
    floor(id / 3)+1 rn,
    case when id % 3 = 1 then val end Field1,
    case when id % 3 = 2 then val end Field2,
    case when id % 3 = 0 then val end Field3
from dbo.Split(@str,',')

select
    rn,
    max(Field1) Field1,
    max(Field2) Field2,
    max(Field3) Field3
from (
select
    floor((id-1) / 3)+1 rn,
    case when id % 3 = 1 then val end Field1,
    case when id % 3 = 2 then val end Field2,
    case when id % 3 = 0 then val end Field3
from dbo.Split(@str,',')
) t
group by rn
0 голосов
/ 10 октября 2018

это потребовало небольшой модификации вашей функции fnSplitString.Добавьте RowNo, чтобы определить исходную последовательность элемента с разделителями

CREATE FUNCTION [dbo].[fnSplitString]
(
    @InputString NVARCHAR(MAX),
    @Delim VARCHAR(255)
)
RETURNS TABLE
AS
      RETURN ( SELECT [Value] FROM 
      ( 
        SELECT RowNo  = ROW_NUMBER() OVER (ORDER BY Number), 
              [Value] = LTRIM(RTRIM(SUBSTRING(@InputString, [Number],
          CHARINDEX(@Delim, @InputString + @Delim, [Number]) - [Number])))
        FROM (SELECT Number = ROW_NUMBER() OVER (ORDER BY name)
          FROM sys.all_objects) AS x
          WHERE Number <= LEN(@InputString)
          AND SUBSTRING(@Delim + @InputString, [Number], LEN(@Delim)) = @Delim
      ) AS y
    );

. При этом вы можете сгруппировать каждые 3 строки в одну.Также RowNo может использоваться для идентификации столбца. Запрос

; with tbl as
(
    select col = '05/10/2018 21:14,#FXAAF00123456,,Cup 1 X Plane,0.00000,OK,Cup 1 Y Plane,0.00000,OK,Cup 1 Z Plane,40.64252,OK,Cup 2 X Plane,77.89434,OK'
)
select  Field1 = MAX(CASE WHEN (RowNo - 1) % 3 = 0 THEN Value END),
        Field2 = MAX(CASE WHEN (RowNo - 1) % 3 = 1 THEN Value END),
        Field3 = MAX(CASE WHEN (RowNo - 1) % 3 = 2 THEN Value END)
from    tbl t
        cross apply dbo.fnSplitString (t.col, ',') 
group by (RowNo - 1) / 3
...