SQL Server 2017 - 根据另一个字符串中的列数动态生成一个字符串
SQL Server 2017 - Dynamically generate a string based on the number of columns in another string
我有以下 table & 数据:
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 的正确计数。
我怎样才能继续我的逻辑来实现我自动生成的映射字符串?
如果数据已经在 table 中并且您想将其分成几列,这应该可行
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,因此您可以使用 STRING_AGG()
- 您需要在子查询中使用 STRING_SPLIT() 和 ROW_NUMER() 拆分 TargetMappingValue。 (注意:我们不能保证在此处使用 string_split() 和 ROW_NUMBER 的顺序,但会适用于这种情况。如果我们需要确保准确的顺序,则使用 OPENJSON 下面的示例。)
- 然后您可以使用 ROW_NUMBER() 作为 CONCAT() 中的列 indicator/number。
- 然后使用 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() 和使用行号的排序顺序。在这种特殊情况下,通用输出映射并不重要。但是,如果您需要在最后的 "GenericMappingValue" 中使用 "TargetMappingValue" 列中的元素,那么您需要确保排序顺序准确无误。
这是一个显示如何使用 OPENJSON() 的示例,"key" 将保证使用 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];
我有以下 table & 数据:
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 的正确计数。
我怎样才能继续我的逻辑来实现我自动生成的映射字符串?
如果数据已经在 table 中并且您想将其分成几列,这应该可行
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,因此您可以使用 STRING_AGG()
- 您需要在子查询中使用 STRING_SPLIT() 和 ROW_NUMER() 拆分 TargetMappingValue。 (注意:我们不能保证在此处使用 string_split() 和 ROW_NUMBER 的顺序,但会适用于这种情况。如果我们需要确保准确的顺序,则使用 OPENJSON 下面的示例。)
- 然后您可以使用 ROW_NUMBER() 作为 CONCAT() 中的列 indicator/number。
- 然后使用 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() 和使用行号的排序顺序。在这种特殊情况下,通用输出映射并不重要。但是,如果您需要在最后的 "GenericMappingValue" 中使用 "TargetMappingValue" 列中的元素,那么您需要确保排序顺序准确无误。
这是一个显示如何使用 OPENJSON() 的示例,"key" 将保证使用 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];