不使用 x 更新语句(如果可能)更新同一列的最佳方法?
Best way to update the same column without using x update statements (if possible)?
我有几个关于以下场景的问题 -
考虑这个 table (db<>fiddle here):
create TABLE StudentClass(
name [nvarchar](30) NULL,
class [nvarchar](100) NULL,
)
insert into StudentClass
values ('Anne', 'Math;Art;Art History'),
('Bill', 'Math;English'),
('Charlie', 'English'),
('Daisy', 'English;Art;Art History'),
('Eddy', 'Math;Art')
我想用数字代码替换每个 class 主题。例如:
Math --> 001
English --> 002
Art --> 003
Art History --> 004
如果我尝试在一个更新语句中执行此操作,它不会让我这样做。我唯一能想到的是使用 replace:
的四个独立语句
update StudentClass set class = replace(class, 'Math', '001')
update StudentClass set class = replace(class, 'English', '002')
update StudentClass set class = replace(class, 'Art', '003')
update StudentClass set class = replace(class, 'Art History', '004')
这几乎 有效,但不完全有效。由于“艺术史”中包含“艺术”,所以本次更新update StudentClass set class = replace(class, 'Art', '003')
先触发并打乱了下一次更新
我的两个问题是:
首先,有没有更好的方法来更新同一列而不是创建 x 更新语句?
其次,是否有更好的方法来确保不会发生 Art/ArtHistory 替换?为此,我唯一能想到的就是切换查询顺序,使其看起来像这样,但我认为有更好的方法我不知道:
update StudentClass set class = replace(class, 'Math', '001')
update StudentClass set class = replace(class, 'English', '002')
update StudentClass set class = replace(class, 'Art History', '004')
update StudentClass set class = replace(class, 'Art', '003')
一如既往地感谢所有帮助!
update StudentClass set class =
CASE
WHEN class LIKE '%Math%' THEN replace(class, 'Math', '001')
WHEN class LIKE '%English%' THEN replace(class, 'English', '002')
WHEN class LIKE '%Art History%' THEN replace(class, 'Art History', '004')
WHEN class LIKE '%Art%' THEN replace(class, 'Art', '003')
ELSE '000'
END;
如果WHEN条件有多个为真,必须先有长的条件再短的条件!
CASE语句跳第一个为真!
问题是这更新了错误的顺序:
update StudentClass set class = replace(class, 'Art', '003')
update StudentClass set class = replace(class, 'Art History', '004')
因为在第二次更新之前没有任何'Art History'只有'003历史'!
在大多数情况下,您可能会有一个 table,其中包含有关 类
号码的信息
在这种情况下,您可以使用 JOIN 来获得所需的结果,而无需在查询中手动写入选项的名称。
话虽如此,使用少量的 类 数字组成列表,如果您在 table 中没有该信息,您可以简单地使用 CASE 语句。这意味着您依赖 CASE 语句中选项的顺序(当...然后...)。在这种特定情况下,此选项更好,因为您不需要解析文本内容。
如果你需要更灵活的解决方案,那么你可以使用类似下面的东西(这会花费更多,因为我们解析文本的内容(拆分它并聚合)但提供更灵活的解决方案可能满足你的需要
;With MyCTE AS (
SELECT s.name, s.class, V.Num
FROM StudentClass s
CROSS APPLY string_split(s.class, ';') ca
LEFT JOIN (VALUES (N'Math', '001'), (N'English', '002'),(N'Art', '003'),(N'Art History', '004')) V (Class, Num) ON V.Class = ca.value
)
SELECT [name], class, STRING_AGG(Num, ';')
FROM MyCTE
GROUP BY [name], class
注意!因为我没有 table,所以我使用这部分 (VALUES (N'Math', '001'), (N'English', '002'),(N'Art', '003'),(N'Art History', '004')) V (Class, Num)
以便动态地获得表格结构中的值
拜托,拜托,请不要在一列中存储多个值。将一对多 table 的人添加到 class。然后你可以找到所有艺术 class 的人,而不必“包含”并担心艺术史。
然后,在每个 table 上使用主键,在 table 之外没有任何意义(例如,不要使用“name”作为 class 键。然后,你可以 link 学生到 classes 并且有 multiple 列 classes (姓名,代码等)你可以加入学生随心所欲。
简而言之,您正在尝试解决一个您一开始就不应该解决的问题...
这是我将如何处理这个问题,这与罗南在我之前发布的解决方案非常相似)。只是我使用的是 CTE 而不是值构造函数,并且有一个 update
语句,我认为它是你问题的一部分。
with cte (class, label) as
(select 'Math', '001' union all
select 'English', '002' union all
select 'Art', '003' union all
select 'Art History', '004'),
cte2 as
(select t1.name, t1.class, string_agg(t3.label,';') as new_class
from StudentClass t1
cross apply string_split(t1.class,';') as t2
left join cte t3 on t3.class=t2.value
group by t1.name, t1.class)
update a
set a.class=b.new_class
from StudentClass a
join cte2 b on a.name=b.name;
步骤分解:
使用 CTE
.
定义小型更新查找
使用string_split
将分隔值拆分为行并使用 cross apply
横向
将它们附加到数据集。
用一个left join
右拉
根据查找更新值。
使用string_agg
聚合
值。
更新你的主 table 加入最后的 CTE
你真的应该考虑在 string_split
之后停在那个 join
,而不是将它与 string_agg
拼接在一起。使用定界字符串存储多个值很少是个好主意。如果您没有更改 table 设计的自由,我理解。
我有几个关于以下场景的问题 -
考虑这个 table (db<>fiddle here):
create TABLE StudentClass(
name [nvarchar](30) NULL,
class [nvarchar](100) NULL,
)
insert into StudentClass
values ('Anne', 'Math;Art;Art History'),
('Bill', 'Math;English'),
('Charlie', 'English'),
('Daisy', 'English;Art;Art History'),
('Eddy', 'Math;Art')
我想用数字代码替换每个 class 主题。例如:
Math --> 001
English --> 002
Art --> 003
Art History --> 004
如果我尝试在一个更新语句中执行此操作,它不会让我这样做。我唯一能想到的是使用 replace:
的四个独立语句update StudentClass set class = replace(class, 'Math', '001')
update StudentClass set class = replace(class, 'English', '002')
update StudentClass set class = replace(class, 'Art', '003')
update StudentClass set class = replace(class, 'Art History', '004')
这几乎 有效,但不完全有效。由于“艺术史”中包含“艺术”,所以本次更新update StudentClass set class = replace(class, 'Art', '003')
先触发并打乱了下一次更新
我的两个问题是:
首先,有没有更好的方法来更新同一列而不是创建 x 更新语句?
其次,是否有更好的方法来确保不会发生 Art/ArtHistory 替换?为此,我唯一能想到的就是切换查询顺序,使其看起来像这样,但我认为有更好的方法我不知道:
update StudentClass set class = replace(class, 'Math', '001')
update StudentClass set class = replace(class, 'English', '002')
update StudentClass set class = replace(class, 'Art History', '004')
update StudentClass set class = replace(class, 'Art', '003')
一如既往地感谢所有帮助!
update StudentClass set class =
CASE
WHEN class LIKE '%Math%' THEN replace(class, 'Math', '001')
WHEN class LIKE '%English%' THEN replace(class, 'English', '002')
WHEN class LIKE '%Art History%' THEN replace(class, 'Art History', '004')
WHEN class LIKE '%Art%' THEN replace(class, 'Art', '003')
ELSE '000'
END;
如果WHEN条件有多个为真,必须先有长的条件再短的条件! CASE语句跳第一个为真!
问题是这更新了错误的顺序:
update StudentClass set class = replace(class, 'Art', '003')
update StudentClass set class = replace(class, 'Art History', '004')
因为在第二次更新之前没有任何'Art History'只有'003历史'!
在大多数情况下,您可能会有一个 table,其中包含有关 类
号码的信息在这种情况下,您可以使用 JOIN 来获得所需的结果,而无需在查询中手动写入选项的名称。
话虽如此,使用少量的 类 数字组成列表,如果您在 table 中没有该信息,您可以简单地使用 CASE 语句。这意味着您依赖 CASE 语句中选项的顺序(当...然后...)。在这种特定情况下,此选项更好,因为您不需要解析文本内容。
如果你需要更灵活的解决方案,那么你可以使用类似下面的东西(这会花费更多,因为我们解析文本的内容(拆分它并聚合)但提供更灵活的解决方案可能满足你的需要
;With MyCTE AS (
SELECT s.name, s.class, V.Num
FROM StudentClass s
CROSS APPLY string_split(s.class, ';') ca
LEFT JOIN (VALUES (N'Math', '001'), (N'English', '002'),(N'Art', '003'),(N'Art History', '004')) V (Class, Num) ON V.Class = ca.value
)
SELECT [name], class, STRING_AGG(Num, ';')
FROM MyCTE
GROUP BY [name], class
注意!因为我没有 table,所以我使用这部分 (VALUES (N'Math', '001'), (N'English', '002'),(N'Art', '003'),(N'Art History', '004')) V (Class, Num)
以便动态地获得表格结构中的值
拜托,拜托,请不要在一列中存储多个值。将一对多 table 的人添加到 class。然后你可以找到所有艺术 class 的人,而不必“包含”并担心艺术史。
然后,在每个 table 上使用主键,在 table 之外没有任何意义(例如,不要使用“name”作为 class 键。然后,你可以 link 学生到 classes 并且有 multiple 列 classes (姓名,代码等)你可以加入学生随心所欲。
简而言之,您正在尝试解决一个您一开始就不应该解决的问题...
这是我将如何处理这个问题,这与罗南在我之前发布的解决方案非常相似)。只是我使用的是 CTE 而不是值构造函数,并且有一个 update
语句,我认为它是你问题的一部分。
with cte (class, label) as
(select 'Math', '001' union all
select 'English', '002' union all
select 'Art', '003' union all
select 'Art History', '004'),
cte2 as
(select t1.name, t1.class, string_agg(t3.label,';') as new_class
from StudentClass t1
cross apply string_split(t1.class,';') as t2
left join cte t3 on t3.class=t2.value
group by t1.name, t1.class)
update a
set a.class=b.new_class
from StudentClass a
join cte2 b on a.name=b.name;
步骤分解:
使用
定义小型更新查找CTE
.使用
string_split
将分隔值拆分为行并使用cross apply
横向 将它们附加到数据集。用一个
left join
右拉 根据查找更新值。使用
string_agg
聚合 值。更新你的主 table 加入最后的
CTE
你真的应该考虑在 string_split
之后停在那个 join
,而不是将它与 string_agg
拼接在一起。使用定界字符串存储多个值很少是个好主意。如果您没有更改 table 设计的自由,我理解。