我有一张表格,用于衡量我的数据库中的学生成绩student
,如下所示:
ID TestDate PerformanceStatus (PS)
1 15/03/2016 0
1 01/04/2016 2
1 05/05/2016 1
1 07/06/2016 1
2 15/03/2016 0
2 01/04/2016 2
2 05/05/2016 1
2 07/06/2016 3
2 23/08/2016 1
我想更新我的表以获得一个新列PreviousPerformanceStatus。
此PreviousPerformanceStatus是根据监控的performanceStatus计算的,如下所示:
注意:如果在TestDate之前没有记录performanceStatus,我想制作PreviousPerformanceStatus = PerformanceStatus
ID TestDate PS PreviousPerformanceStatus
1 15/03/2016 0 0
1 01/04/2016 2 0
1 05/05/2016 1 2
1 07/06/2016 1 1
2 15/03/2016 0 0
2 01/04/2016 2 0
2 05/05/2016 1 2
2 07/06/2016 3 1
2 23/08/2016 1 3
如何更新我的SQL表?我应该使用加入吗? 感谢。
答案 0 :(得分:2)
假设testdate
列是DATE
数据类型(而不是VARCHAR)
假设(id,testdate)
student
元组是唯一的
我们可以在SELECT列表中使用相关子查询。举个例子:
SELECT t.id
, t.testdate
, t.performancestatus
, ( SELECT p.performancestatus
FROM student p
WHERE p.id = t.id
AND p.testdate < t.testdate
ORDER BY p.testdate DESC
LIMIT 1
) AS previousperformancestatus
FROM student t
ORDER BY t.id, t.testdate
一旦我们确认SELECT语句为我们提供了我们正在寻找的结果,我们就可以将其转换为UPDATE语句。无论是作为内联视图,还是直接使用相关子查询。
UPDATE student t
SET t.previousperformancestatus
= ( SELECT p.performancestatus
FROM student p
WHERE p.id = t.id
AND p.testdate < t.testdate
ORDER BY p.testdate DESC
LIMIT 1
)
如果testdate
不是 DATE
数据类型,或不以规范格式存储,那么&#34;小于&# 34;比较不能保证将行限制为早期&#34; testdate。 &#34;按顺序排列&#34;不能保证返回最近的&#34;更早的&#34;首先测试。
对于&#34;第一&#34; testdate,当没有早期测试日期时,子查询将返回NULL。我们可以使用表达式将NULL值转换为0.我们可以将子查询包装在函数IFNULL( <subquery> ,0)
答案 1 :(得分:0)
spencer7593提出的SELECT可以工作,但是效率很低(在我的情况下,要花一分钟才能更新三列)。 UPDATE将失败,并显示一条错误消息,提示您无法更新SET目标的FROM子句中正在使用的表。
另一种更有效的方法是将CTE与LAG()结合使用,并从中获取更新的值。但是,如果没有其他简单或复合唯一键,则需要一个唯一键用作CTE和表之间的联接键。
-- Create the table as per question
drop table if exists student;
create table if not exists student (
pk int auto_increment,
id int not null,
TestDate date not null,
PerformanceStatus int not null,
PreviousPerformanceStatus int null default null,
primary key (pk)
) engine=innodb;
insert into student(id, TestDate, PerformanceStatus, PreviousPerformanceStatus)
values (1, '2016-03-15', 0, null),
(1, '2016-04-01', 2, null),
(1, '2016-05-05', 1, null),
(1, '2016-06-07', 1, null),
(2, '2016-03-15', 0, null),
(2, '2016-04-01', 2, null),
(2, '2016-05-05', 1, null),
(2, '2016-06-07', 3, null),
(2, '2016-08-23', 1, null);
-- Update PreviousPerformanceStatus using lag()
with p as
(
select pk, id, testdate, performancestatus,
LAG(performancestatus, 1, 0) OVER (
PARTITION BY id
ORDER BY id, testdate asc
) as PreviousPerformanceStatus
from student
)
update student t
inner join p
on p.pk = t.pk
set
t.PreviousPerformanceStatus = p.PreviousPerformanceStatus;
您可以将LAG()的第三个参数替换为null而不是零。 我发现该解决方案是其他几种可行解决方案中最有效的。