如何使用交叉应用字符串拆分结果更新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.movi​​es_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中的表?的主要内容,如果未能解决你的问题,请参考以下文章

由于存在不相关字段而导致的交叉表拆分结果

不平衡面板数据:如何使用时间序列拆分交叉验证?

如何拆分数据进行训练和测试?交叉验证可能吗? M估计还是OLS?

如何优化SQL语句

SQL如何根据一个字段的某个关键词的前面部分分组查询

SQL的join使用