记一个有意思的sql,mysql在update中使用select查询出的数据来update,我一开始是想先用sql把数据查询出来成一个列表,然后再循环遍历update更新的,但是效率太低了要执行很多遍sql,为什么sql不能写成一条呢,在update的时候直接select出来,学习了一下,发现可以。
update中使用select查询出的数据-通用模式: update a inner join (select * from b) c on a.id = c.id set a.xx = c.xxx
注:在mysql中要想update中使用select查询出来的数据来更新的话只能用inner join
先描述一个场景,我随便描述的,场景不一定合理,我就想讲一下这种情况的解决方案,给以后看到的小伙伴一点类似问题的解决思路。 (1)有一个读书使用表book_used_record,(2)记录书本的每次使用记录,(3)同一本书的使用记录的书编号book_no是一样的,(4)首次使用的时候before_book_no为null,(5)后面的使用记录都会记录上一次的before_book_no,(6)use_month记录这本书使用了多少个月
问题:use_month记录的使用时长需要按同一本书最早的创建时间来记录,即假如同一本书有三条记录,这三条记录的use_month字段都是按这三条记录中创建时间最早的那一条创建时间来计算出来。 有一个定时任务,每隔一段时间就需要更新use_month这个字段,sql要如何写? book_used_record表的数据:
查询出同一本书的最早创建时间不难,这里主要由两个问题 1.如何查询出两个时间的相差月数 2.如何一条update的sql来更新(一条sql肯定得在update中用select查询出数据)
一、首先查询出两个时间的相差月数: TIMESTAMPDIFF(MONTH,create_time,NOW())
注:
一开始考虑用period_diff函数来处理的,例:select period_diff(201903,201901);查出来的结果就是2,但是这里忽略了天数,实际日期可能是201903.01,201901.30,period_diff也是2,相当于向上取整了,但是我要向下取整,不满两个月的还是按1个月来算。所以不合适。
后面我想还可以通过datediff求出两个时间相差的天数然后取商就是向下取整,但是算法用在日期上是不对的,每个月不一定都是30天的,所以也不合适。
period_diff需要date_format(curdate(),’%Y%m’)来格式化时间,时间格式就是201901这样子的,datediff函数的注意,需要用date_format(curdate(),’%Y%m%d’)来格式化
DATE_FORMAT(now(), ‘%Y-%m-%d %H:%i:%S’)
最后timestampdiff是最合适的 –计算相差月数 select TIMESTAMPDIFF(MONTH, ‘2018-03-20 23:59:00’, ‘2015-03-22 00:00:00’); –计算相差天数 select TIMESTAMPDIFF(DAY, ‘2018-03-20 23:59:00’, ‘2015-03-22 00:00:00’); –计算相差小时数 select TIMESTAMPDIFF(HOUR, ‘2018-03-20 09:00:00’, ‘2018-03-22 10:00:00’); –计算相差秒数 select TIMESTAMPDIFF(MINUTE, ‘2018-03-20 09:00:00’, ‘2018-03-22 10:00:00’);
二、在update中用select查询出的数据更新 UPDATE book_used_record bookINNER JOIN ( SELECT book_no, min ( create_time ) as orignTime FROM book_used_record GROUP BY book_no) orign_book ON book. book_no = orign_book. book_no SET use_month = ( TIMESTAMPDIFF( MONTH , orign_book. orignTime, NOW ( ) ) ) ;
先查询出书的编号,以及该编号的最早的创建时间,select book_no,min(create_time) FROM book_used_record 连表更新,set字段use_month 的时候最早创建时间用上面查询出的创建时间和当前时间来计算相差月数。update模式:update a inner join (select * from b) c on a.id = b.id set a.xx = c.xxx 更新之后可以看到use_month都正常统计了 相关sql:
CREATE TABLE ` book_used_record` ( ` id` int ( 11 ) NOT NULL COMMENT '主键' , ` book_no` varchar ( 64 ) DEFAULT NULL COMMENT '书的编号' , ` book_name` varchar ( 255 ) DEFAULT NULL COMMENT '书名' , ` use_month` int ( 11 ) DEFAULT NULL COMMENT '使用时长(按月)' , ` user_name` varchar ( 50 ) DEFAULT NULL COMMENT '使用人' , ` user_code` varchar ( 64 ) DEFAULT NULL COMMENT '使用人编号' , ` create_time` datetime DEFAULT NULL COMMENT '创建时间' , ` before_user_code` varchar ( 64 ) DEFAULT NULL COMMENT '上一个使用人的编号' , PRIMARY KEY ( ` id` ) ) ENGINE = InnoDB DEFAULT CHARSET = utf8; INSERT INTO ` test` . ` book_used_record` ( ` id` , ` book_no` , ` book_name` , ` use_month` , ` user_name` , ` user_code` , ` create_time` , ` before_user_code` ) VALUES ( 1 , 'b1' , '孙子兵法' , NULL , '小明' , 'g142' , '2019-07-01 23:08:35' , NULL ) ; INSERT INTO ` test` . ` book_used_record` ( ` id` , ` book_no` , ` book_name` , ` use_month` , ` user_name` , ` user_code` , ` create_time` , ` before_user_code` ) VALUES ( 2 , 'b1' , '孙子兵法' , NULL , '小红' , 'g143' , '2019-09-01 23:09:28' , 'g142' ) ; INSERT INTO ` test` . ` book_used_record` ( ` id` , ` book_no` , ` book_name` , ` use_month` , ` user_name` , ` user_code` , ` create_time` , ` before_user_code` ) VALUES ( 3 , 'b1' , '孙子兵法' , NULL , '小林' , 'g144' , '2019-10-14 23:10:22' , 'g143' ) ; INSERT INTO ` test` . ` book_used_record` ( ` id` , ` book_no` , ` book_name` , ` use_month` , ` user_name` , ` user_code` , ` create_time` , ` before_user_code` ) VALUES ( 4 , 's2' , 'sql优化' , NULL , '小威' , 'g145' , '2019-08-01 23:11:14' , NULL ) ; INSERT INTO ` test` . ` book_used_record` ( ` id` , ` book_no` , ` book_name` , ` use_month` , ` user_name` , ` user_code` , ` create_time` , ` before_user_code` ) VALUES ( 5 , 's2' , 'sql优化' , NULL , '小华' , 'g146' , '2019-11-14 23:11:44' , 'g145' ) ; INSERT INTO ` test` . ` book_used_record` ( ` id` , ` book_no` , ` book_name` , ` use_month` , ` user_name` , ` user_code` , ` create_time` , ` before_user_code` ) VALUES ( 6 , 'k1' , 'k8s的权威指南' , NULL , '小东' , 'g147' , NULL , NULL ) ; update book_used_record set use_month = null ; select * , TIMESTAMPDIFF( MONTH , create_time, now ( ) ) from book_used_record