我有一个表格,可以将班级与该班级的学生联系起来:
create table class_student
(class_id int,
student_id int,
constraint class_student_u unique nonclustered (class_id, student_id))
如果我想将所有课程从一个学生转移到另一个学生(从他/她注册的所有课程中删除一个学生,并将另一个学生添加到老学生注册的每个课程中),我使用以下查询:
update class_student
set student_id = @newStudent
where student_id = @oldStudent
and class_id not in (select class_id
from class_student
where student_id = @newStudent)
delete from class_student
where student_id = @oldStudent
如何将课程从多个学生转移到新生?我不能只放where student_id in (@oldStudent1, @oldStudent2)
,因为如果两个老同学都在同一个班级,那么在运行上述查询后,将会违反唯一约束。另外,如果可能的话,我想在尽可能少的查询中进行更新(我可以运行上述查询两次,但我想更少地执行此操作)。
我正在使用SQL Server 2008 R2。
编辑:澄清一下,这是一个例子:
class_id student_id
===================
1 1
1 2
2 3
3 1
3 3
4 2
4 3
这意味着学生1在1级和3级,2名学生在1级和4级,3名学生在2级,3级和4级。如果我想将所有课程从学生1转移到学生3,我会运行以下查询:
update class_student
set student_id = 3
where student_id = 1
and class_id not in (select class_id
from class_student
where student_id = 3)
delete from class_student
where student_id = 1
我们的数据如下所示:
class_id student_id
===================
1 3
1 2
2 3
3 3
4 2
4 3
相反,如果我运行了此查询:
update class_student
set student_id = 3
where student_id in (1, 2)
and class_id not in (select class_id
from class_student
where student_id = 3)
delete from class_student
where student_id in (1, 2)
忽略表上的唯一约束,数据如下所示:
class_id student_id
===================
1 3
1 3
2 3
3 3
4 3
双重(1,3)记录是我试图避免的,因为它会在表格中导致唯一的约束违规。
答案 0 :(得分:1)
我认为您需要至少2个DML语句才能实现目标。如果你真的需要一次性发生,那么你可以将语句包装在一个存储过程中。
insert into class_student (class_id, student_id)
select distinct class_id, @newStudent
from class_student
where student_id in (@oldStudent1, @oldStudent2)
and class_id not in (select class_id
from class_student
where student_id = @newStudent);
delete from class_student
where student_id in (@oldStudent1, @oldStudent2);
编辑:修复了插入以包含“not in”子句。
答案 1 :(得分:1)
在设置原始表格时,您应该始终包含一个唯一的行ID,用于引用任何特定行(请参阅以下名为row_id的' identity'列):
DROP TABLE class_student
create table class_student
(
row_id int identity(1,1),
class_id int,
student_id int,
constraint class_student_u unique nonclustered (class_id, student_id)
)
insert class_student (class_id,student_id) values (1,1)
insert class_student (class_id,student_id) values (1,2)
insert class_student (class_id,student_id) values (2,3)
insert class_student (class_id,student_id) values (3,1)
insert class_student (class_id,student_id) values (3,3)
insert class_student (class_id,student_id) values (4,2)
insert class_student (class_id,student_id) values (4,3)
在学生1和2正在离开的情况下,你正在通过他们带到学生3的任何课程(除非学生3已经参加这些课程),代码可以 看起来像这样:
WITH CTE
AS
(
SELECT row_Id,class_id,student_id,RN = ROW_NUMBER()OVER(PARTITION BY
class_id ORDER BY class_id) FROM class_student WHERE student_id in (1,2,3)
)
DELETE FROM class_student where class_id in (select class_id from
class_student group by class_id having count(class_id) > 1) and student_id
<> 3 and row_id not in (select row_id from cte where student_id <> 3 and
rn >= 2)
Update class_student set student_id = 3
我使用的是常用表格表达式&#39;与&#39; RANK&#39;根据带有相同class_id的行数为每个class_id编号。要看到这一点,您可以在以后运行下面的代码 创建class_student表并插入数据(参见顶部),但在运行上面的CTE代码之前:
WITH CTE
AS
(
SELECT row_Id,class_id,student_id,RN = ROW_NUMBER()OVER(PARTITION BY
class_id ORDER BY class_id) FROM class_student WHERE student_id in (1,2,3)
)
SELECT * FROM CTE
因为class_id 1,3和4是重复的,所以它们在RN(行号)列中的值为2.
我在CTE中使用此结果删除了class_student表中不需要的行,这就是可以看到始终拥有唯一row_id的重要性。
Delete查询删除class_student表中属于Class ID重复项的行。如果是学生3和其中一个或两个学生参加的课程 获取学生ID不是3的行(因为学生3没有离开)。
为了成功完成此操作(不将我们想要保留的行分配给学生3),它需要(通过比较row_id&#39; s)RN = 2的行(即class_id重复) 并保留student_id不是3,以便我们保留学生1和2正在进行的课程的其中一行,但学生3没有(即student_id不是3)。
最后,将表格中的所有剩余行更新为student_id为3,以便学生3获得所有课程。
要查看可以运行的结果:
select * from class_student