如何根据同一个表中的先前数据更新SQL表

时间:2017-02-16 16:27:23

标签: mysql date join

我有一张表格,用于衡量我的数据库中的学生成绩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表?我应该使用加入吗? 感谢。

2 个答案:

答案 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子句中正在使用的表。

enter image description here

另一种更有效的方法是将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而不是零。 我发现该解决方案是其他几种可行解决方案中最有效的。