SQL Server 2017 - динамически генерировать строку на основе количества столбцов в другой строке - PullRequest
0 голосов
/ 06 марта 2019

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

CREATE TABLE dbo.TableMapping 
(
[GenericMappingKey] [nvarchar](256) NULL,
[GenericMappingValue] [nvarchar](256) NULL,
[TargetMappingKey] [nvarchar](256) NULL,
[TargetMappingValue] [nvarchar](256) NULL
)

INSERT INTO dbo.TableMapping 
(
     [GenericMappingKey]
    ,[GenericMappingValue]
    ,[TargetMappingKey]
    ,[TargetMappingValue]
)
VALUES
( 
     'Generic' 
    ,'Col1Source|Col1Target;Col2Source|Col2Target;Col3Source|Col3Target;Col4Source|Col4Target;Col5Source|Col5Target;Col6Source|Col6Target'
    ,'Target'
    ,'Fruit|Apple;Car|Red;House|Bungalo;Gender|Female;Material|Brick;Solution|IT'
)

Мне нужно было бы иметь возможность автоматически генерировать мою строку GenericMappingValue динамически на основе количества пар столбцов в столбце TargetMappingValue.

В настоящее время существует 6 пар сопоставления столбцов. Тем не менее, если бы в моем TargetMapping было только две пары столбцов сопоставления, например:

'Fruit|Apple;Car|Red'

тогда я бы хотел, чтобы GenericMappingValue генерировался (обновлялся) автоматически, например, так как, как следствие, в моей строке было бы только 2 пары столбцов ...

'Col1Source|Col1Target;Col2Source|Col2Target'

Я начал строить следующую логику запроса:

DECLARE @Mapping nvarchar(256)
SELECT @Mapping = [TargetMappingValue] from TableMapping
print @Mapping
SELECT count(*) ColumnPairCount
FROM String_split(@Mapping, ';')

Приведенный выше запрос дает мне правильное количество 6 для моих пар столбцов.

Как я смогу продолжить свою логику для получения автоматически сгенерированной строки сопоставления?

Ответы [ 2 ]

2 голосов
/ 06 марта 2019

Я думаю, я понимаю, что вы после.Это должно заставить вас двигаться в правильном направлении.

Поскольку вы пометили 2017, вы можете использовать STRING_AGG ()

  • Вы захотите разделить свойTargetMappingValue с использованием STRING_SPLIT () с ROW_NUMER () в подзапросе.(ПРИМЕЧАНИЕ. Мы не гарантируем порядок с использованием string_split () с ROW_NUMBER здесь, но будем работать в этой ситуации. Пример ниже с использованием OPENJSON, если нам нужно обеспечить точный порядок.)
  • Затем вы можете использовать этот ROW_NUMBER() в качестве индикатора / числа столбца в CONCAT ().
  • Затем соберите все вместе, используя STRING_AGG ()

Посмотрите на эторабочий пример:

DECLARE @TableMapping TABLE
    (
        [GenericMappingKey] [NVARCHAR](256) NULL
      , [GenericMappingValue] [NVARCHAR](256) NULL
      , [TargetMappingKey] [NVARCHAR](256) NULL
      , [TargetMappingValue] [NVARCHAR](256) NULL
    );

INSERT INTO @TableMapping (
                              [GenericMappingKey]
                            , [GenericMappingValue]
                            , [TargetMappingKey]
                            , [TargetMappingValue]
                          )
VALUES ( 'Generic'
       , 'Col1Source|Col1Target;Col2Source|Col2Target;Col3Source|Col3Target;Col4Source|Col4Target;Col5Source|Col5Target;Col6Source|Col6Target'
       , 'Target'
       , 'Fruit|Apple;Car|Red;House|Bungalo;Gender|Female;Material|Brick;Solution|IT' );



SELECT   [col].[GenericMappingKey]
       , STRING_AGG(CONCAT('Col', [col].[ColNumber], 'Source|Col', [col].[ColNumber], 'Target'), ';') AS [GeneratedGenericMappingValue]
       , [col].[TargetMappingKey]
       , [col].[TargetMappingValue]
FROM     (
             SELECT      *
                       , ROW_NUMBER() OVER ( ORDER BY (
                                                          SELECT 1
                                                      )
                                           ) AS [ColNumber]
             FROM        @TableMapping
             CROSS APPLY STRING_SPLIT([TargetMappingValue], ';')
         ) AS [col]
GROUP BY [col].[GenericMappingKey]
       , [col].[TargetMappingKey]
       , [col].[TargetMappingValue];

Вот пример того, как будет выглядеть обновление, если предположить, что вашим первичным ключом является столбец GenericMappingKey:

--This what an update would look like
--Assuming your primary key is the [GenericMappingKey] column
UPDATE     [upd]
SET        [upd].[GenericMappingValue] = [g].[GeneratedGenericMappingValue]
FROM       (
               SELECT   [col].[GenericMappingKey]
                      , STRING_AGG(CONCAT('Col', [col].[ColNumber], 'Source|Col', [col].[ColNumber], 'Target'), ';') AS [GeneratedGenericMappingValue]
                      , [col].[TargetMappingKey]
                      , [col].[TargetMappingValue]
               FROM     (
                            SELECT      *
                                      , ROW_NUMBER() OVER ( ORDER BY (
                                                                         SELECT 1
                                                                     )
                                                          ) AS [ColNumber]
                            FROM        @TableMapping
                            CROSS APPLY [STRING_SPLIT]([TargetMappingValue], ';')
                        ) AS [col]
               GROUP BY [col].[GenericMappingKey]
                      , [col].[TargetMappingKey]
                      , [col].[TargetMappingValue]
           ) AS [g]
INNER JOIN @TableMapping [upd]
    ON [upd].[GenericMappingKey] = [g].[GenericMappingKey];

Shnugo поднимает замечательный вопрос в комментарияхчто мы не гарантируем порядок сортировки с помощью string_split () и использования номера строки.В этой конкретной ситуации это не имеет значения, поскольку выходные сопоставления являются общими.Но что если вам нужно использовать элементы из столбца «TargetMappingValue» в окончательном «GenericMappingValue», то вам нужно убедиться, что порядок сортировки был точным.

Вот пример, показывающий, как использовать OPENJSON() и это «ключ», который гарантирует этот порядок на примере Shnugo:

SELECT   [col].[GenericMappingKey]
       , STRING_AGG(CONCAT('Col', [col].[colNumber], 'Source|Col', [col].[colNumber], 'Target'), ';') AS [GeneratedGenericMappingValue]
       , [col].[TargetMappingKey]
       , [col].[TargetMappingValue]
FROM     (
             SELECT      [tm].*
                       , [oj].[Key] + 1 AS [colNumber] --Use the key as our order/column number, adding 1 as it is zero based.
                       , [oj].[Value] -- and if needed we can bring the split value out.
             FROM        @TableMapping [tm]
             CROSS APPLY OPENJSON('["' + REPLACE([tm].[TargetMappingValue], ';', '","') + '"]') [oj] --Basically turn the column value into JSON string.
         ) AS [col]
GROUP BY [col].[GenericMappingKey]
       , [col].[TargetMappingKey]
       , [col].[TargetMappingValue];
0 голосов
/ 06 марта 2019

, если данные уже есть в таблице и вы хотите разбить их на столбцы, это должно сработать

select 
     v.value
    ,left(v.value, charindex('|',v.value) -1) col1
    ,reverse(left(reverse(v.value), charindex('|',reverse(v.value)) -1)) col2 
from String_split(@mapping,';') v
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...