Как добиться следующего с помощью функций/хранимой процедуры в базе данных SQL Server

У меня есть следующая таблица со следующими данными как

                                            Tab1
FutureMISBoundaryVersion  CurrentMISBoundaryVersion  FutureHAMBoundaryVersion  CurrentHAMBoundaryVersion 
2:21,5:50,4:55,7:80,9:33  2:12,5:40,4:35,7:60,9:87   2:52,5:90,4:75,7:30,9:57  2:42,5:60,4:95,7:70,9:37   

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

FutureMIS-OAKVersion |FutureMIS-HAMVersion |FutureMIS-DURVersion | FutureMIS-BURVersion| FutureMIS-YRTVersion |DeviceMIS-OAKVersion|DeviceMIS-HAMVersion |DeviceMIS-DURVersion| DeviceMIS-BURVersion| DeviceMIS-YRTVersion
              33     |              80     |            21       |            55       |  50                  | 87                 |  60                 |12                  |35                   | 40

i, e: когда он находит столбец «FutureMISBoundaryVersion» на вкладке 1, тогда его значение '2:21,5:50,4:55,7:80,9:33' будет разделен и его значение вставлено таким образом, что соответствующее значение ключа 2 i,e:21 будет вставлено в FutureMIS -DURVersion столбец.

Точно так же значение 50 ключа 5 будет вставлено в столбец FutureMIS-BURVersion и так далее для других ключей.

когда он находит столбец «CurrentMISBoundaryVersion», тогда
'2:12,5:40,4:35,7:60,9:87' будет разделен и его значение вставлено таким образом, что соответствующее значение ключа 2 i,e:12 будет вставлено в CurrentMIS Столбец -DURVersion аналогично ключу 5 со значением 40 будет вставлен в столбец DeviceMIS-YRTVersion и так далее для других столбцов исходной таблицы.

Структура таблицы может расширяться, так как я показал только 4 столбца исходной таблицы, но логика для всех столбцов остается неизменной.

Одинаковы ли длины всех значений в каждом столбце?

Gordon Linoff 27.05.2019 15:13

Нет, бывает по-разному @ Гордон Линофф

krity 27.05.2019 18:12
ReactJs | Supabase | Добавление данных в базу данных
ReactJs | Supabase | Добавление данных в базу данных
Это и есть ваш редактор таблиц в supabase.👇
Понимание Python и переход к SQL
Понимание Python и переход к SQL
Перед нами лабораторная работа по BloodOath:
1
2
118
3
Перейти к ответу Данный вопрос помечен как решенный

Ответы 3

Следующий запрос разберет строку 2:21,5:50,4:55,7:80,9:33, разделенную запятыми, на отдельные компоненты 2:21, 5:30 и т. д. Оттуда вы можете использовать аналогичный метод для извлечения bb из aa:bb.

Поскольку пара ключ-значение имеет формат aa:bb, вы можете использовать datepart(hour, 'aa:bb') и datepart(minute, 'aa:bb') для извлечения aa и bb.

; with 
Tab1 as
(
    select  val  = '2:21,5:50,4:55,7:80,9:33'
)
select  t.*, k1.k, k2.k, k3.k, k4.k, k5.k
from    Tab1 t
    cross apply
    (
        select  i = charindex(',', t.val),
            k = substring(t.val, 1, charindex(',', t.val + ',', 1) - 1)
    ) k1
    cross apply
    (
        select  i = charindex(',', t.val, k1.i + 1),
            k = substring(t.val, k1.i + 1, charindex(',', t.val + ',', k1.i + 1) - k1.i - 1)
    ) k2
    cross apply
    (
        select  i = charindex(',', t.val, k2.i + 1),
            k = substring(t.val, k2.i + 1, charindex(',', t.val + ',', k2.i + 1) - k2.i - 1)
    ) k3
    cross apply
    (
        select  i = charindex(',', t.val, k3.i + 1),
            k = substring(t.val, k3.i + 1, charindex(',', t.val + ',', k3.i + 1) - k3.i - 1)
    ) k4
    cross apply
    (
        select  i = charindex(',', t.val, k4.i + 1),
            k = substring(t.val, k4.i + 1, charindex(',', t.val + ',', k4.i + 1) - k4.i - 1)
    ) k5

@ Squirrel: если какой-либо ключ не найден в паре ключ-значение, выдает неожиданный o/p из вашей логики, например: вместо этого «2: 21, 5: 50, 4: 55, 7: 80, 9: 33» скажем, предположим, что у нас есть '5:50,4:55,7:80,9:33', тогда весь синтаксический анализ испортится

krity 27.05.2019 04:48

как узнать какого ключа не хватает?

Squirrel 27.05.2019 04:50

Вы говорите, что первый ключ всегда 2, второй всегда 5?

Squirrel 27.05.2019 04:51

это не должно быть!! порядок также может измениться, то есть: дело в том, что он может содержать максимум все ключи 2,5,4,7,9, присутствующие в нем '2:21,5:50,4:55,7:80,9:33 ' или может быть '2:21,5:50,4:55', но ключ будет из подмножества (2,5,4,7,9). Если какой-либо ключ не найден, соответствующий столбец должен быть пустой строкой.

krity 27.05.2019 04:54

в любом случае, вы можете использовать аналогичный метод, например charindex(), substring(), чтобы извлечь ключ и значение, которые вам нужны.

Squirrel 27.05.2019 04:57
Ответ принят как подходящий

Очень странные требования, если честно. Обратите внимание, что приведенное ниже решение будет работать только в SQL Server 2016+, поскольку я использую JSON для анализа данных. Однако вы можете написать свой парсер, в этом случае код будет работать почти во всех версиях SQL Server.

Функция разбора:

CREATE FUNCTION dbo.ParseIt(@Type NVARCHAR(255),@Value NVARCHAR(MAX))
RETURNS @Parsed TABLE (Code NVARCHAR(255),Value NVARCHAR(255))
AS
BEGIN
    INSERT INTO @Parsed(Code,Value)
    SELECT @Type + '-' + m.Code + 'Version' AS [Code],p.[1] AS [Value]
    FROM (
        SELECT j.[key] AS [ID],i.[key],i.value
        FROM OPENJSON('["' + REPLACE(@Value,',','","') + '"]') j
        CROSS APPLY OPENJSON('[' + REPLACE(j.[value],':',',') + ']') i
    ) a
    PIVOT(MAX(a.value) FOR a.[key] IN ([0],[1])) p
    INNER JOIN ( VALUES
        (2,'DUR')
        ,(4,'BUR')
        ,(5,'YRT')
        ,(7,'HAM')
        ,(9,'OAK')
    ) m(ID, Code) ON m.ID = p.[0]
    ;
    RETURN;
END

Исходные данные:

DECLARE @Table TABLE (FutureMISBoundaryVersion NVARCHAR(MAX), CurrentMISBoundaryVersion NVARCHAR(MAX),FutureHAMBoundaryVersion NVARCHAR(MAX),CurrentHAMBoundaryVersion NVARCHAR(MAX));
INSERT INTO @Table(FutureMISBoundaryVersion,CurrentMISBoundaryVersion,FutureHAMBoundaryVersion,CurrentHAMBoundaryVersion)VALUES
    ('2:21,5:50,4:55,7:80,9:33','2:12,5:40,4:35,7:60,9:87','2:52,5:90,4:75,7:30,9:57','2:42,5:60,4:95,7:70,9:37')
;

Код:

SELECT COALESCE(p.[FutureMIS-OAKVersion],'') AS [FutureMIS-OAKVersion]
    ,COALESCE(p.[FutureMIS-HAMVersion],'') AS [FutureMIS-HAMVersion]
    ,COALESCE(p.[FutureMIS-DURVersion],'') AS [FutureMIS-DURVersion]
    ,COALESCE(p.[FutureMIS-BURVersion],'') AS [FutureMIS-BURVersion]
    ,COALESCE(p.[FutureMIS-YRTVersion],'') AS [FutureMIS-YRTVersion]
    ,COALESCE(p.[DeviceMIS-OAKVersion],'') AS [DeviceMIS-OAKVersion]
    ,COALESCE(p.[DeviceMIS-HAMVersion],'') AS [DeviceMIS-HAMVersion]
    ,COALESCE(p.[DeviceMIS-DURVersion],'') AS [DeviceMIS-DURVersion]
    ,COALESCE(p.[DeviceMIS-BURVersion],'') AS [DeviceMIS-BURVersion]
    ,COALESCE(p.[DeviceMIS-YRTVersion],'') AS [DeviceMIS-YRTVersion]
FROM (
    SELECT f.Code,f.Value FROM @Table t CROSS APPLY dbo.ParseIt('FutureMIS',t.FutureMISBoundaryVersion) f
    UNION ALL 
    SELECT f.Code,f.Value FROM @Table t CROSS APPLY dbo.ParseIt('DeviceMIS',t.CurrentMISBoundaryVersion) f
) a
PIVOT(MAX(a.Value) FOR a.Code IN ([DeviceMIS-BURVersion],[DeviceMIS-DURVersion],[DeviceMIS-HAMVersion],[DeviceMIS-OAKVersion]
    ,[DeviceMIS-YRTVersion],[FutureMIS-BURVersion],[FutureMIS-DURVersion],[FutureMIS-HAMVersion],[FutureMIS-OAKVersion]
    ,[FutureMIS-YRTVersion])) p
;

@ Виталий Борисов: отличная попытка ... но если какой-либо ключ отсутствует, он вставляет нулевые значения в столбец назначения, но я хочу, чтобы это была пустая строка

krity 27.05.2019 05:43

Я обновил ответ в соответствии с требованием, но настоятельно рекомендую везде использовать правильные типы (int) вместо строк.

Vitaly Borisov 27.05.2019 06:33

@ Виталий Борисов: я просто пытался расширить то же самое для своего требования, как DeviceCurrentMISCRECrossBoundaryBURVersion вместо [DeviceMIS-BURVersion], DeviceFutureMISCRECrossBoundaryBURVersion вместо [FutureMIS-BURVersion] и так далее в качестве выходного столбца. Любая помощь в этом

krity 28.05.2019 08:20

Просто замените на AS [anyNameYouNeed] в операторе выбора

Vitaly Borisov 28.05.2019 08:31

@ Виталий Борисов: что, если у нас есть формат даты, например: '2:21/06/2017, 3:11/02/2014' Попытался нажать, но получил: Спасибо за отзыв! Голоса, отданные теми, у кого репутация менее 15, записываются, но не изменяют публично отображаемый результат публикации.

krity 29.05.2019 07:40

У вас недостаточно баллов для голосования, но вы можете принять ответ

Vitaly Borisov 29.05.2019 10:22

@ Виталий Борисов: Готово!! А что, если у меня есть формат даты i, e: «2:21/06/2017,3:11/02/2014» или «2:21.06.2017,3:11.02.2014» вместо «2:21,3». :50'

krity 29.05.2019 11:01

В функции замены Cross применяются к этому: CROSS APPLY OPENJSON('["' + REPLACE(j.[value],':','","') + '"]') i

Vitaly Borisov 29.05.2019 21:46

Можем ли мы одновременно обрабатывать как строку, так и вышеупомянутый формат даты в одной функции?

krity 30.05.2019 08:27

Да, просто замените в соответствии с моим предыдущим комментарием

Vitaly Borisov 30.05.2019 10:35

OPENJSON, используемая в приведенной выше функции, несовместима с SQL 2014, но работает в 2016 году, есть ли альтернатива?

krity 10.07.2019 17:05

Напишите функцию парсера, используя строковые функции вместо OpenJson

Vitaly Borisov 10.07.2019 21:44

Это боль в SQL Server. Вы можете сделать это с помощью рекурсивных CTE:

 with cte as (
      select convert(varchar(max), left(FutureMISBoundaryVersion, charindex(',', FutureMISBoundaryVersion) - 1)) as FutureMISBoundaryVersion,
             convert(varchar(max), left(CurrentMISBoundaryVersion, charindex(',', CurrentMISBoundaryVersion) - 1)) as CurrentMISBoundaryVersion,
             convert(varchar(max), left(FutureHAMBoundaryVersion, charindex(',', FutureHAMBoundaryVersion) - 1)) as FutureHAMBoundaryVersion,
             convert(varchar(max), left(CurrentHAMBoundaryVersion, charindex(',', FutureMISBoundaryVersion) - 1)) as CurrentHAMBoundaryVersion,
             stuff(FutureMISBoundaryVersion, 1, charindex(',', FutureMISBoundaryVersion), '') + ',' as FutureMISBoundaryVersion_rest,
             stuff(CurrentMISBoundaryVersion, 1, charindex(',', CurrentMISBoundaryVersion), '') + ',' as CurrentMISBoundaryVersion_rest,
             stuff(FutureHAMBoundaryVersion, 1, charindex(',', FutureHAMBoundaryVersion), '') + ',' as FutureHAMBoundaryVersion_rest,
             stuff(CurrentHAMBoundaryVersion, 1, charindex(',', CurrentHAMBoundaryVersion), '') + ',' as CurrentHAMBoundaryVersion_rest,
             1 as lev
      from t
      union all
      select convert(varchar(max), left(FutureMISBoundaryVersion_rest, charindex(',', FutureMISBoundaryVersion_rest) - 1)) as FutureMISBoundaryVersion,
             convert(varchar(max), left(CurrentMISBoundaryVersion_rest, charindex(',', CurrentMISBoundaryVersion_rest) - 1)) as CurrentMISBoundaryVersion,
             convert(varchar(max), left(FutureHAMBoundaryVersion_rest, charindex(',', FutureHAMBoundaryVersion_rest) - 1)) as FutureHAMBoundaryVersion,
             convert(varchar(max), left(CurrentHAMBoundaryVersion_rest, charindex(',', CurrentHAMBoundaryVersion_rest) - 1)) as CurrentHAMBoundaryVersion,
             stuff(FutureMISBoundaryVersion_rest, 1, charindex(',', FutureMISBoundaryVersion_rest), '') as FutureMISBoundaryVersion_rest,
             stuff(CurrentMISBoundaryVersion_rest, 1, charindex(',', CurrentMISBoundaryVersion_rest), '') as CurrentMISBoundaryVersion_rest,
             stuff(FutureHAMBoundaryVersion_rest, 1, charindex(',', FutureHAMBoundaryVersion_rest), '') as FutureHAMBoundaryVersion_rest,
             stuff(CurrentHAMBoundaryVersion_rest, 1, charindex(',', CurrentHAMBoundaryVersion_rest), '') as CurrentHAMBoundaryVersion_rest,
             lev + 1
      from cte
      where FutureMISBoundaryVersion_rest like '%,%'
     )
select FutureMISBoundaryVersion, CurrentMISBoundaryVersion, FutureHAMBoundaryVersion, CurrentHAMBoundaryVersion, lev
from cte;

Здесь — это рабочий пример db<>.

Другие вопросы по теме