String SQL Server 2017-根据另一个字符串中的列数动态生成一个字符串

String SQL Server 2017-根据另一个字符串中的列数动态生成一个字符串,string,tsql,dynamic,count,sql-server-2017,String,Tsql,Dynamic,Count,Sql Server 2017,我有以下表格和数据: CREATE TABLE dbo.TableMapping ( [GenericMappingKey] [nvarchar](256) NULL, [GenericMappingValue] [nvarchar](256) NULL, [TargetMappingKey] [nvarchar](256) NULL, [TargetMappingValue] [nvarchar](256) NULL ) INSERT INTO dbo.TableMapping (

我有以下表格和数据:

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'
)
我需要能够根据TargetMappingValue列中的列对数自动动态生成GenericMappingValue字符串

目前,有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的正确计数


我如何才能继续我的逻辑来实现自动生成的映射字符串?

如果数据已经在表中,并且您希望将其拆分为列,这应该是可行的

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

如果数据已经在表中,并且您希望将其拆分为列,那么这应该可以工作

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

我想我明白你的意思。这会让你朝着正确的方向前进

既然您已经标记了2017,您可以使用

  • 您需要在子查询中使用with ROW_NUMER()拆分TargetMappingValue。(注意:我们不能保证在这里使用带有行号的string_split()进行排序,但在这种情况下可以使用。下面的示例中,如果我们需要确保准确的排序,请使用OPENJSON。)
  • 然后可以使用该行作为CONCAT()中的列指示符/编号
  • 然后使用
请看这个工作示例:

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在评论中提出了一个很好的观点,即我们不能保证使用字符串_split()和使用行号的排序顺序。在这种特殊情况下,它与泛型中的输出映射无关。但是,如果您需要在最终的“GenericMappingValue”中使用“TargetMappingValue”列中的元素,那么您需要确保排序顺序是准确的

下面是一个示例,展示了如何使用它以及它的“密钥”,使用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];

我想我明白你的意思。这会让你朝着正确的方向前进

既然您已经标记了2017,您可以使用

  • 您需要在子查询中使用with ROW_NUMER()拆分TargetMappingValue。(注意:我们不能保证在这里使用带有行号的string_split()进行排序,但在这种情况下可以使用。下面的示例中,如果我们需要确保准确的排序,请使用OPENJSON。)
  • 然后可以使用该行作为CONCAT()中的列指示符/编号
  • 然后使用
请看这个工作示例:

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在评论中提出了一个很好的观点,即我们不能保证使用字符串_split()和使用行号的排序顺序。在这种特殊情况下,它与泛型中的输出映射无关。但是,如果您需要在最终的“GenericMappingValue”中使用“TargetMappingValue”列中的元素,那么您需要确保排序顺序是准确的

下面是一个示例,展示了如何使用它以及它的“密钥”,使用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];

顺便说一句:
STRING\u SPLIT()
不保证返回预期的排序顺序。因此,您的
orderbyselect1
可能在所有测试中都能正常工作,但在生产中却会失败—无论如何。。。您可能希望使用
OPENJSON
解决字符串拆分问题…@Shnugo感谢您的反馈。注意到了,因为我没有想到这一点。考虑到这一点,在这种特殊情况下,顺序并不重要,只要我们得到相同数量的映射对,因为它们想要生成的映射是通用的。@Shnugo再次感谢,我更新了我的答案,并使用OPENJSON提供了一个示例。这是个好把戏,太棒了。你已经完全解决了我的问题!顺便说一句:
STRING\u SPLIT()
不保证返回预期的排序顺序。因此,您的
orderbyselect1
可能在所有测试中都能正常工作,但在生产中却会失败—无论如何。。。您可能希望使用
OPENJSON
解决字符串拆分问题…@Shnugo感谢您的反馈。注意到了,因为我没有想到这一点。考虑到这一点,在这种特殊情况下,顺序并不重要,只要我们得到相同数量的映射对,因为它们想要生成的映射是通用的。@Shnugo再次感谢,我更新了我的答案,并使用OPENJSON提供了一个示例。这是个好把戏,太棒了。你已经完全解决了我的问题!