将相关的逗号分隔字符串转换为具有多列和一对多的单个行

Turning related Comma Separated strings into individual rows with multiple columns with one to many

提问人:mthom 提问时间:6/2/2023 最后编辑:philipxymthom 更新时间:6/2/2023 访问量:40

问:

我正在看帖子将相关的逗号分隔字符串转换为具有多列的单独行。当数据数据是一对一的相关数据时,这对我来说非常有用。但是我需要更多的数据,例如 Data to Many RelatedDate。我使用 [] 将每个 RelatedData 分组到数据的每个部分。

| SomeID         | OtherID     | Data      | RelatedData |
+----------------+-------------+-----------+-------------+
| abcdef-.....   | cdef123-... | 18,20,22  | [xxx,xxxx],[xxxx],[yyy]|
| abcdef-.....   | 4554a24-... | 17,19     | [a],[bb]     |
| 987654-.....   | 12324a2-... | 13,19,20  | [r],[s],[t]  |

我想返回如下所示的行:

| SomeID         | OtherID     | Data | RelatedData |
+----------------+-------------+------+-------------+
| abcdef-.....   | cdef123-... | 18   | xxx         |
| abcdef-.....   | cdef123-... | 20   | xxxx        |
| abcdef-.....   | cdef123-... | 20   |             |
| abcdef-.....   | cdef123-... | 22   | yyy         |
| abcdef-.....   | 4554a24-... | 17   | a           |
| abcdef-.....   | 4554a24-... | 19   | bb          |
| ...            | ...         | ...  | ...         |

我试图修改帖子中的一个答案以满足我的需求,但我没有运气,因为它将我的数据推送到新行。

;WITH cte AS (
  SELECT SomeId, OtherId, TRIM(cs1.Value) AS DataValue, TRIM(REPLACE(REPLACE(cs2.Value, '[', ''), ']', '')) AS RelatedDataValue,
    DataItemNumber = ROW_NUMBER() OVER (PARTITION BY t.SomeId, t.OtherId, cs1.Value ORDER BY (SELECT NULL)),
    RelatedDataItemNumber = ROW_NUMBER() OVER (PARTITION BY t.SomeId, t.OtherId, cs2.Value ORDER BY (SELECT NULL))
  FROM t
  CROSS APPLY STRING_SPLIT(Data, ',') cs1
  CROSS APPLY STRING_SPLIT(REPLACE(REPLACE(RelatedData, '[', ''), ']', ''), ',') cs2
)
SELECT cte.SomeId, cte.OtherId, cte.DataValue, cte.RelatedDataValue
FROM cte
WHERE cte.DataItemNumber = cte.RelatedDataItemNumber
ORDER BY cte.SomeId, cte.OtherId, cte.DataItemNumber;
T-SQL SQL-SERVER-2017 数据库规范化 第一范式形式

评论

1赞 Tim Biegeleisen 6/2/2023
我的建议:在将 CSV 数据引入 SQL Server 之前对其进行规范化。
0赞 Adrian Maxwell 6/2/2023
18,20,22 & [xxx,xxxx],[xxxx],[yyy] 我想应该是 18/xxx,xxxx(导致 2 行?) 20/xxxx 和 22/yyy - 请仔细检查您的示例表
0赞 philipxy 6/2/2023
请在您的帖子中输入所有需要询问的内容。引用信用并与您的帖子相关。不要指望我们阅读整个参考文献,猜猜什么是相关的,为什么。
0赞 philipxy 6/2/2023
调试问题需要一个最小的可重现示例--剪切、粘贴和可运行的代码,包括初始化;期望和实际输出(包括逐字错误消息);标签和版本;明确的规范和解释。对于 SQL,包括 DDL 和表格初始化代码。对于包含您可以提供的最少代码的调试,即您显示的代码是好的,由您显示的代码扩展为“不正常”。 如何咨询 帮助中心 当你得到一个你意想不到的结果时,暂停你的总体目标,切到第一个子表达式,结果出乎意料,说出你所期望的和为什么, 通过文档证明是合理的。(调试基础。
0赞 philipxy 6/2/2023
请不要删除和重新发布问题。你正在浪费读者的努力并颠覆网站协议。特别是不要转发已关闭的帖子,您被告知要编辑,直到它通过审核。PS 使用多个帐户来执行 1 不允许的事情违反了站点协议。

答:

0赞 Adrian Maxwell 6/2/2023 #1

这可能只会让您更进一步,因为您似乎拥有与数据的一对多关系的“相关数据”。但是,这可能会有所帮助。string_split 的一大问题是它不提供每个拆分的索引引用,因此要解决这个问题,您可以使用 row_number 来模拟它。或者,找到一个“拆分字符串”用户定义的函数,该函数确实输出索引(这些确实存在)。因此,在不涉及太多细节的情况下,使用split_string,您可以以这种方式将零件相互对齐:

注意:我在 RelatedData 中替换了 and,因此只需要将其作为分隔符。,[[]

--DDL
CREATE TABLE T (
    SomeID VARCHAR(255),
    OtherID VARCHAR(255),
    Data VARCHAR(255),
    RelatedData VARCHAR(255)
);

--DML !! NB SomeID is now unique !!
INSERT INTO T (SomeID, OtherID, Data, RelatedData) 
VALUES 
('abcdef-1....', 'cdef123-...', '18,20,22', '[xxx,xxxx],[xxxx],[yyy]'),
('abcdef-2....', '4554a24-...', '17,19', '[a],[bb]'),
('987654-3....', '12324a2-...', '13,19,20', '[r],[s],[t]');
3 rows affected
update T
set RelatedData = replace(replace(RelatedData,',[',''),'[','')
3 rows affected
select * from t
SomeID 其他 ID 数据 相关数据
abcdef-1.... cdef123-... 18,20,22 xxx,xxxx]xxxx]yyy]
abcdef-2.... 4554a24-... 17,19 a]bb]
987654-3.... 12324一2-... 13,19,20 r]s]t]
select t.someID, cs1.*, row_number() over(partition by t.SomeID order by (select 1)) rn
FROM t
  CROSS APPLY STRING_SPLIT(Data, ',') cs1

someID 价值 注册护士
987654-3.... 13 1
987654-3.... 19 2
987654-3.... 20 3
abcdef-1.... 18 1
abcdef-1.... 20 2
abcdef-1.... 22 3
abcdef-2.... 17 1
abcdef-2.... 19 2
select t.someID, cs2.*, row_number() over(partition by t.SomeID order by (select 1)) rn
FROM t
  CROSS APPLY STRING_SPLIT(RelatedData, ']') cs2

someID 价值 注册护士
987654-3.... r 1
987654-3.... s 2
987654-3.... t 3
987654-3.... 4
abcdef-1.... xxx,xxxx 1
abcdef-1.... xxxx 2
abcdef-1.... yyy 3
abcdef-1.... 4
abcdef-2.... 一个 1
abcdef-2.... BB型 2
abcdef-2.... 3
with pdata as (
select t.someID, cs1.*, row_number() over(partition by t.SomeID order by (select 1)) rn
FROM t
  CROSS APPLY STRING_SPLIT(Data, ',') cs1
)
, prelated as (
select t.someID, cs2.*, row_number() over(partition by t.SomeID order by (select 1)) rn
FROM t
  CROSS APPLY STRING_SPLIT(RelatedData, ']') cs2
  )
select
*
from pdata
left join prelated on pdata.SomeID = prelated.SomeID
             and pdata.rn = prelated.rn
order by 1,2
someID 价值 注册护士 someID 价值 注册护士
987654-3.... 13 1 987654-3.... r 1
987654-3.... 19 2 987654-3.... s 2
987654-3.... 20 3 987654-3.... t 3
abcdef-1.... 18 1 abcdef-1.... xxx,xxxx 1
abcdef-1.... 20 2 abcdef-1.... xxxx 2
abcdef-1.... 22 3 abcdef-1.... yyy 3
abcdef-2.... 17 1 abcdef-2.... 一个 1
abcdef-2.... 19 2 abcdef-2.... BB型 2

小提琴 1

请注意,我认为 18 可能需要使用最终string_split进一步划分 - 但这因问题中显示的内容而异。

with pdata as (
select t.someID, cs1.*, row_number() over(partition by t.SomeID order by (select 1)) rn
FROM t
  CROSS APPLY STRING_SPLIT(Data, ',') cs1
)
, prelated as (
select t.someID, cs2.*, row_number() over(partition by t.SomeID order by (select 1)) rn
FROM t
  CROSS APPLY STRING_SPLIT(RelatedData, ']') cs2
  )
select
    pdata.SomeID
  , pdata.value as data
  , cs3.value   as related
from pdata
inner join prelated on pdata.SomeID = prelated.SomeID
                   and pdata.rn = prelated.rn
CROSS APPLY STRING_SPLIT(prelated.value, ',') cs3
order by 1,2

小提琴 2