检查更新语句中的唯一性

时间:2015-06-02 21:26:36

标签: sql sql-server

我有一个表格,可以将班级与该班级的学生联系起来:

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)记录是我试图避免的,因为它会在表格中导致唯一的约束违规。

2 个答案:

答案 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