如何使用交叉应用字符串拆分结果更新sql中的表?
Posted
技术标签:
【中文标题】如何使用交叉应用字符串拆分结果更新sql中的表?【英文标题】:How to use cross apply string split result to update a table in sql? 【发布时间】:2021-12-13 07:34:10 【问题描述】:我正在尝试拆分表“movies_titles”的column('categories'),其中包含字符串分隔的数据值。
e.g:
ID title categories
1 Movie A Comedy, Drama, Romance
2 Movie B Animation
3 Movie C Documentary, Life changing
我想拆分逗号分隔的字符串并将每个值放在单独的行中并更新表格
-- this query shows the splitted strings as I want it
SELECT *
FROM dbo.movies_titles
CROSS APPLY
string_split(categories, ',')
O/P:
ID title categories value
1 Movie A Comedy, Drama, Romance Comedy
1 Movie A Comedy, Drama, Romance Drama
1 Movie A Comedy, Drama, Romance Romance
2 Movie B Animation Animation
3 Movie C Documentary, Life changing Documentary
3 Movie C Documentary, Life changing Life changing
我想使用 UPDATE 查询来设置从 value 列获得的结果。我只是不想使用 SELECT 查询来查看结果,而是永久更新对表的更改。如何在 sql server 中实现这一点?
【问题讨论】:
你想更新什么表?现在对于电影 A,您有三个值 我想像上面的输出一样更新表中的行。例如,如果我应用 SELECT * FROM dbo.movies_titles 来查看数据,它不会显示任何更改或更新。我希望能够使用 'cross apply string_split' 作为 UPDATE 查询的一部分,而不仅仅是 SELECT 它。 您不能更新源表并使其像输出一样,因为输出中的行数多于源表中的行数,并且 UPDATE 不会更改行数。但是您可以使用第二个表并将输出插入到其中。 要获得您想要的结果将需要 几个 步骤。您首先需要创建中间结果,即使用临时表,然后向表中插入新行,然后使用各个类别值更新所有行。 @Stu 可以与MERGE
从/到同一张桌子一步完成,看我的回答
【参考方案1】:
您可以执行与创建新行的意图类似的操作,因为更新语句不会创建拆分产生的额外行。
如果 ID 列是唯一的,例如主键,则可能会出现问题,并且需要保持与该列相关联的标题。
我在 DB Fiddle 上创建了两个场景,展示了如何按照问题的指示仅使用一个表来执行此操作,但 better alternative 会将这些信息保存在另一个表上。
DB Fiddle 上的此代码:link
--Assuming your table is something like this
create table movies_id_as_pk (
ID int identity(1,1) primary key,
title varchar(200),
categories varchar(200),
category varchar(200)
)
--Or this
create table movies_other_pk (
another_id int identity(1,1) primary key,
ID int,
title varchar(200),
categories varchar(200),
category varchar(200)
)
--The example data
set identity_insert movies_id_as_pk on
insert into movies_id_as_pk (ID, title, categories) values
(1, 'Movie A', 'Comedy, Drama, Romance'),
(2, 'Movie B', 'Animation'),
(3, 'Movie C', 'Documentary, Life changing')
set identity_insert movies_id_as_pk off
insert into movies_other_pk (ID, title, categories)
select ID, title, categories from movies_id_as_pk
--You can't update directly any of the tables, because as the result of the split
--have more rows than the table, it would just leave the first value found:
update m set category = rtrim(ltrim(s.value))
from movies_id_as_pk m
cross apply string_split(m.categories, ',') as s
update m set category = rtrim(ltrim(s.value))
from movies_other_pk m
cross apply string_split(m.categories, ',') as s
select * from movies_id_as_pk
select * from movies_other_pk
--What you can do is create the aditional rows, inserting them:
--First, let's undo what the last instructions have changed
update movies_id_as_pk set category=NULL
update movies_other_pk set category=NULL
--Then use inserts to create the rows with the categories split
insert into movies_id_as_pk (title, category)
select m.title, rtrim(ltrim(s.value))
from movies_id_as_pk m
cross apply string_split(m.categories, ',') as s
insert into movies_other_pk (ID, title, category)
select m.ID, m.title, rtrim(ltrim(s.value))
from movies_other_pk m
cross apply string_split(m.categories, ',') as s
select * from movies_id_as_pk
select * from movies_other_pk
【讨论】:
【参考方案2】:实际上是可以同时插入或更新的。也就是说:我们可以用单个category
更新每一行,然后创建新的额外的行。
我们可以为此使用MERGE
。我们可以使用同一个表作为源和目标。我们只需要拆分源,然后为每个原始行添加一个分区的行号。然后我们过滤ON
子句以仅匹配第一行。
WITH Source AS (
SELECT
m.ID,
m.title,
category = TRIM(cat.value),
rn = ROW_NUMBER() OVER (PARTITION BY ID ORDER BY (SELECT NULL))
FROM movies m
CROSS APPLY STRING_SPLIT(m.categories, ',') cat
)
MERGE movies t
USING Source s
ON s.ID = t.ID AND s.rn = 1
WHEN MATCHED THEN
UPDATE
SET categories = s.category
WHEN NOT MATCHED THEN
INSERT (ID, title, categories)
VALUES (s.ID, s.title, s.category)
;
db<>fiddle
不过,我不一定建议将此作为通用解决方案,因为看起来您实际上还有其他规范化问题需要首先解决。对于所有这些信息,您真的应该有单独的表格:
Movie
Category
MovieCategory
【讨论】:
以上是关于如何使用交叉应用字符串拆分结果更新sql中的表?的主要内容,如果未能解决你的问题,请参考以下文章