SQL - 在多列中拆分字符串
SQL - Splitting string in multiple columns
我想拆分查询的结果并将值显示在单独的列中。例如,我得到以下结果
|Name |
|ABC_DEFG_HIJKL|
|A_B_C |
|A_B_C_D |
我想用“_”拆分值并将它们添加到单独的列中。
查询结果应该是这样的
|Name |first |second |third |fourth|
|ABC_DEFG_HIJKL|ABC |DEFG |HIJKL |null |
|A_B_C |A |B |C |null |
|A_B_C_D |A |B |C |D |
到目前为止我可以拆分结果。但是对于每个值,我都有一个新行。所以我只需要将结果合并到一行并为每一行创建一列。
SELECT DP.Name, value
FROM RitopDatenpunkt DP
CROSS APPLY STRING_SPLIT(DP.Name, '_');
|Name |value |
|ABC_DEFG_HIJKL|ABC |
|ABC_DEFG_HIJKL|DEFG |
|ABC_DEFG_HIJKL|HIJKL |
|A_B_C |A |
|A_B_C |B |
|A_B_C |C |
|A_B_C_D |A |
|A_B_C_D |B |
|A_B_C_D |C |
|A_B_C_D |D |
我知道我应该使用 PIVOT。但是我使用什么聚合函数并且是 FOR 语句的参数 right
SELECT DP.Name, value
FROM RitopDatenpunkt DP
CROSS APPLY STRING_SPLIT(DP.Name, '_')
PIVOT
(
GROUPING(Name) as Name
FOR value in ([first],[second],[third],[fourth])
)piv;
假设你的字符串没有重复,你可以使用这个比较麻烦的方法:
SELECT dp.*, s.*
FROM RitopDatenpunkt DP CROSS APPY
(SELECT MAX(CASE WHEN SEQNUM = 1 THEN s.value END) as first,
MAX(CASE WHEN SEQNUM = 2 THEN s.value END) as second,
MAX(CASE WHEN SEQNUM = 3 THEN s.value END) as third,
MAX(CASE WHEN SEQNUM = 4 THEN s.value END) as fourth
FROM (SELECT s.*,
ROW_NUMBER() OVER (ORDER BY CHARINDEX('_' + s.value + '_', '_' + DP.Name + '_')) as seqnum
FROM STRING_SPLIT(DP.Name, '_') s
) s
) s;
这使用 CHARINDEX()
查找原始字符串中的值,然后使用条件聚合按顺序创建列。
不幸的是,STRING_SPLIT()
不保证顺序。如果组件不超过四个,另一种方法是使用递归 CTE 或 mis-use PARSENAME()
函数。
这是使用 JSON 函数的一种方法:
select t.name,
json_value(x.obj, '$[0]') name1,
json_value(x.obj, '$[1]') name2,
json_value(x.obj, '$[2]') name2,
json_value(x.obj, '$[3]') name4
from mytable t
cross apply (values('["' + replace(t.name, '_', '", "') + '"]')) x(obj)
诀窍是操纵字符串,使它看起来像一个 JSON 数组(这就是 cross apply
子查询所做的)。基本上这会将 'A_B_C'
之类的字符串转换为 '["A", "B", "C"]'
。然后我们可以使用 json_value()
轻松访问每个单独的元素。
这并不假设元素是唯一的。实际上唯一的要求是字符串不应包含嵌入的双引号。
name | name1 | name2 | name2 | name4
:------------- | :---- | :---- | :---- | :----
ABC_DEFG_HIJKL | ABC | DEFG | HIJKL | null
A_B_C | A | B | C | null
A_B_C_D | A | B | C | D
您也可以使用 PARSENAME()
作为:
SELECT Val,
PARSENAME(Value, 1) Name1,
PARSENAME(Value, 2) Name2,
PARSENAME(Value, 3) Name3,
PARSENAME(Value, 4) Name4
FROM
(
VALUES
('ABC_DEFG_HIJKL'),
('A_B_C'),
('A_B_C_D')
) T(Val) CROSS APPLY (VALUES(REPLACE(Val, '_', '.'))) TT(Value);
请注意,如果您有超过 3 个 '_'
,这将不起作用。
我想拆分查询的结果并将值显示在单独的列中。例如,我得到以下结果
|Name |
|ABC_DEFG_HIJKL|
|A_B_C |
|A_B_C_D |
我想用“_”拆分值并将它们添加到单独的列中。 查询结果应该是这样的
|Name |first |second |third |fourth|
|ABC_DEFG_HIJKL|ABC |DEFG |HIJKL |null |
|A_B_C |A |B |C |null |
|A_B_C_D |A |B |C |D |
到目前为止我可以拆分结果。但是对于每个值,我都有一个新行。所以我只需要将结果合并到一行并为每一行创建一列。
SELECT DP.Name, value
FROM RitopDatenpunkt DP
CROSS APPLY STRING_SPLIT(DP.Name, '_');
|Name |value |
|ABC_DEFG_HIJKL|ABC |
|ABC_DEFG_HIJKL|DEFG |
|ABC_DEFG_HIJKL|HIJKL |
|A_B_C |A |
|A_B_C |B |
|A_B_C |C |
|A_B_C_D |A |
|A_B_C_D |B |
|A_B_C_D |C |
|A_B_C_D |D |
我知道我应该使用 PIVOT。但是我使用什么聚合函数并且是 FOR 语句的参数 right
SELECT DP.Name, value
FROM RitopDatenpunkt DP
CROSS APPLY STRING_SPLIT(DP.Name, '_')
PIVOT
(
GROUPING(Name) as Name
FOR value in ([first],[second],[third],[fourth])
)piv;
假设你的字符串没有重复,你可以使用这个比较麻烦的方法:
SELECT dp.*, s.*
FROM RitopDatenpunkt DP CROSS APPY
(SELECT MAX(CASE WHEN SEQNUM = 1 THEN s.value END) as first,
MAX(CASE WHEN SEQNUM = 2 THEN s.value END) as second,
MAX(CASE WHEN SEQNUM = 3 THEN s.value END) as third,
MAX(CASE WHEN SEQNUM = 4 THEN s.value END) as fourth
FROM (SELECT s.*,
ROW_NUMBER() OVER (ORDER BY CHARINDEX('_' + s.value + '_', '_' + DP.Name + '_')) as seqnum
FROM STRING_SPLIT(DP.Name, '_') s
) s
) s;
这使用 CHARINDEX()
查找原始字符串中的值,然后使用条件聚合按顺序创建列。
不幸的是,STRING_SPLIT()
不保证顺序。如果组件不超过四个,另一种方法是使用递归 CTE 或 mis-use PARSENAME()
函数。
这是使用 JSON 函数的一种方法:
select t.name,
json_value(x.obj, '$[0]') name1,
json_value(x.obj, '$[1]') name2,
json_value(x.obj, '$[2]') name2,
json_value(x.obj, '$[3]') name4
from mytable t
cross apply (values('["' + replace(t.name, '_', '", "') + '"]')) x(obj)
诀窍是操纵字符串,使它看起来像一个 JSON 数组(这就是 cross apply
子查询所做的)。基本上这会将 'A_B_C'
之类的字符串转换为 '["A", "B", "C"]'
。然后我们可以使用 json_value()
轻松访问每个单独的元素。
这并不假设元素是唯一的。实际上唯一的要求是字符串不应包含嵌入的双引号。
name | name1 | name2 | name2 | name4 :------------- | :---- | :---- | :---- | :---- ABC_DEFG_HIJKL | ABC | DEFG | HIJKL | null A_B_C | A | B | C | null A_B_C_D | A | B | C | D
您也可以使用 PARSENAME()
作为:
SELECT Val,
PARSENAME(Value, 1) Name1,
PARSENAME(Value, 2) Name2,
PARSENAME(Value, 3) Name3,
PARSENAME(Value, 4) Name4
FROM
(
VALUES
('ABC_DEFG_HIJKL'),
('A_B_C'),
('A_B_C_D')
) T(Val) CROSS APPLY (VALUES(REPLACE(Val, '_', '.'))) TT(Value);
请注意,如果您有超过 3 个 '_'
,这将不起作用。