程序员:你如何写可重复执行的SQL语句?

上图的意思: 百战百胜,屡试不爽。
故事

程序员小张: 刚毕业,参加工作1年左右,日常工作是CRUD

架构师老李: 多个大型项目经验,精通各种开发架构屠龙宝术;
小张注意到,在实际的项目开发场景中,很多开发人员只关注编写SQL脚本来满足功能需求,而忽略了脚本的可重复执行性。
这就意味着,如果脚本中的某个部分执行失败,运维人员就必须从头提供一个新的脚本,这对运维团队和开发人员来说是一个挑战。
因此,小张决定研究如何编写基于MySQL的可以重复执行的SQL脚本,以提高开发效率和简化运维流程。
他向公司的架构师老李咨询了这个问题。老李是一位经验丰富的架构师,
他在多个大型项目中积累了许多宝贵的经验,精通各种开发架构屠龙宝术。
老李听了小张的问题后,笑了笑并开始给予指导。他向小张解释了如何编写一个具有可重复执行性的SQL脚本,并分享了以下几个关键点:
a.使用事务:事务是一组SQL语句的逻辑单元,可以保证这组语句要么全部执行成功,要么全部回滚。
通过使用事务,可以确保脚本的所有修改操作要么完整地执行,要么不执行。
b.使用条件检查:在每个需要修改数据的语句之前,添加条件检查以确保只有当数据不存在或满足特定条件时才进行修改。
这样可以避免重复插入相同的数据,或者执行不必要的更新操作。
c.错误处理:在编写脚本时,考虑到可能出现的错误情况,并提供适当的错误处理机制。例如,使用IF...ELSE语句来处理特定条件下的执行逻辑。
d.使用存储过程:如果脚本非常复杂,包含多个步骤和业务逻辑,可以考虑将它们封装为存储过程。这样可以更好地组织和管理代码,并提高脚本的可读性和维护性。
小张听得津津有味,他开始将老李的建议付诸实践。他仔细研究每个SQL语句,根据老李的指导进行修改和优化。
他使用了事务来包裹整个脚本,添加了条件检查来避免重复插入数据,并实现了错误处理机制以应对异常情况。
背景
所以开发提供给到运维的SQL脚本有一定基本要求:
1.能重复执行;
2.不出错,(不报错,逻辑正确);
如果脚本不可重复执行,则运维无法自动化,会反过来要求后端开发人员给出适配当前环境的新的SQL脚本,增加了运维和沟通成本。
那么怎么写可重复执行的SQL脚本呢?
分成4个场景,来介绍举例。

1 创建表
create table if not exists nginx_config (
id varchar(36) not null default '' comment 'UUID',
namespace varchar(255) not null default '' comment '环境命名空间',
config_content text comment "nginx http块配置",
content_md5 varchar(64) not null default '' comment '配置内容的MD5值',
manipulator varchar(64) not null default '' comment '操作者',
description varchar(512) not null default '' comment '描述',
gmt_created bigint unsigned not null default 0 comment '创建时间',
primary key(id)
)ENGINE=InnoDB comment 'nginx配置表' ;
删除表在生产环境是禁止的。
备份方式修改表名
修改表名: 先创建新表,再copy历史数据进去,不允许删除表;
DELIMITER //
drop procedure if exists modify_table_name;
CREATE PROCEDURE modify_table_name(
IN table_name VARCHAR(255),
IN new_name VARCHAR(255)
)
BEGIN
DECLARE database_name VARCHAR(255);
DECLARE table_exists INT DEFAULT 0;
DECLARE new_table_exists INT DEFAULT 0;
SELECT DATABASE() INTO database_name;
set @db_table_name=concat(database_name,'/',table_name);
select count(t1.TABLE_ID) INTO table_exists from information_schema.INNODB_TABLES t1 where t1.NAME=@db_table_name ;
set @db_table_name_new=concat(database_name,'/',new_name);
select count(t1.TABLE_ID) INTO new_table_exists from information_schema.INNODB_TABLES t1 where t1.NAME=@db_table_name_new ;
IF table_exists = 1 AND new_table_exists = 0 THEN
SET @query = CONCAT('create table ',new_name,' like ',table_name);
PREPARE stmt FROM @query;
EXECUTE stmt;
DEALLOCATE PREPARE stmt;
SET @query = CONCAT('insert into ', new_name, ' select * from ',table_name);
PREPARE stmt FROM @query;
EXECUTE stmt;
DEALLOCATE PREPARE stmt;
SELECT 'table name modify successfully.' AS result ,@db_table_name,@db_table_name_new,table_exists,new_table_exists;
ELSE
SELECT 'table name not exists or new_name already exists.' AS result,@db_table_name,@db_table_name_new,table_exists,new_table_exists;
END IF;
END //
DELIMITER ;
测试脚本:
create table user(id bigint auto_increment primary key ,name varchar(30),age tinyint)comment 'user表';
insert into user(id, name, age) VALUES (1,'a',1),(2,'b',2),(3,'c',3);
call modify_table_name('user','user1');
select * from user1;
call modify_table_name('user','user2');
select * from user2;
测试结果符合预期。
新增修改删除字段
drop procedure if exists modify_table_field;
CREATE PROCEDURE modify_table_field(IN tableName VARCHAR(50), IN fieldName VARCHAR(50), IN fieldAction VARCHAR(10), IN fieldType VARCHAR(255))
BEGIN
IF fieldAction = 'add' THEN
IF NOT EXISTS (SELECT * FROM information_schema.columns WHERE table_name = tableName AND column_name = fieldName) THEN
SET @query = CONCAT('ALTER TABLE ', tableName, ' ADD COLUMN ', fieldName, ' ', fieldType);
PREPARE stmt FROM @query;
EXECUTE stmt;
DEALLOCATE PREPARE stmt;
SELECT 'Field added successfully.' AS result;
ELSE
SELECT 'Field already exists.' AS result;
END IF;
ELSEIF fieldAction = 'modify' THEN
IF EXISTS (SELECT * FROM information_schema.columns WHERE table_name = tableName AND column_name = fieldName) THEN
SET @query = CONCAT('ALTER TABLE ', tableName, ' CHANGE COLUMN ', fieldName, ' ', fieldName, ' ', fieldType);
select @query;
PREPARE stmt FROM @query;
EXECUTE stmt;
DEALLOCATE PREPARE stmt;
SELECT 'Field modified successfully.' AS result;
ELSE
SELECT 'Field does not exist or has the same name.' AS result;
END IF;
ELSEIF fieldAction = 'delete' THEN
IF EXISTS (SELECT * FROM information_schema.columns WHERE table_name = tableName AND column_name = fieldName) THEN
SET @query = CONCAT('ALTER TABLE ', tableName, ' DROP COLUMN ', fieldName);
PREPARE stmt FROM @query;
EXECUTE stmt;
DEALLOCATE PREPARE stmt;
SELECT 'Field deleted successfully.' AS result;
ELSE
SELECT 'Field does not exist.' AS result;
END IF;
ELSE
SELECT 'Invalid field action.' AS result;
END IF;
END;
测试脚本
create table if not exists sys_agent
(
agent_id bigint not null comment '客服唯一id' primary key,
agent_name varchar(64) null comment '客服名称',
agent_type varchar(30) null comment '客服类型(场地客服、直聘客服)',
district varchar(30) null comment '地区',
service_language varchar(30) null comment '服务语种',
agent_description varchar(500) null comment '客户描述',
status tinyint(1) null comment '状态(0=无效,1=有效),默认为1',
del_flag tinyint(1) null comment '是否删除(0=false,1=true)',
user_id bigint null comment '用户id(关联的用户信息)',
time_zone varchar(50) null comment '时区',
create_by varchar(50) null comment '创建者',
create_time datetime default CURRENT_TIMESTAMP null comment '创建时间',
update_by varchar(50) null comment '修改者',
update_time datetime default CURRENT_TIMESTAMP null on update CURRENT_TIMESTAMP comment '修改时间'
) comment '客服管理';
CALL modify_table_field('sys_agent', 'sex', 'add', 'tinyint not null comment ''性别''');
CALL modify_table_field('sys_agent', 'sex2', 'add', 'tinyint not null comment ''性别''');
CALL modify_table_field('sys_agent', 'sex', 'modify', 'int not null comment ''性别''');
CALL modify_table_field('sys_agent', 'sex', 'delete', '');
CALL modify_table_field('sys_agent', 'sex2', 'delete', '');
测试结果符合预期。
新增修改删除索引
一般放在建表语句中,80%的情况;
如果是项目后期增加索引,进行调优,可以参考字段,写一个存储过程支持索引的新增可以重复执行;
DELIMITER //
drop procedure if exists modify_table_index;
CREATE PROCEDURE modify_table_index(
IN table_name VARCHAR(255),
IN index_name VARCHAR(255),
IN index_action ENUM('add', 'modify', 'delete'),
IN index_columns VARCHAR(255)
)
BEGIN
DECLARE database_name VARCHAR(255);
DECLARE index_exists INT DEFAULT 0;
DECLARE index_exists_action INT DEFAULT 0;
-- 获取当前数据库名
SELECT DATABASE() INTO database_name;
set @db_table_name=concat(database_name,'/',table_name);
-- 检查索引是否存在
select count(t2.INDEX_ID) INTO index_exists from information_schema.INNODB_TABLES t1 left join information_schema.INNODB_INDEXES t2 on t1.TABLE_ID=T2.TABLE_ID
where t1.NAME=@db_table_name and t2.NAME=index_name;
set index_exists_action=index_exists;
IF index_action = 'add' THEN
-- 添加索引
IF index_exists < 1 THEN
SET @query = CONCAT('ALTER TABLE `', database_name, '`.`', table_name, '` ADD INDEX `', index_name, '` (', index_columns, ')');
PREPARE stmt FROM @query;
EXECUTE stmt;
DEALLOCATE PREPARE stmt;
select count(t2.INDEX_ID) INTO index_exists_action from information_schema.INNODB_TABLES t1 left join information_schema.INNODB_INDEXES t2 on t1.TABLE_ID=T2.TABLE_ID where t1.NAME=@db_table_name and t2.NAME=index_name;
SELECT 'Index added successfully.' AS result ,database_name,index_exists,@db_table_name,index_exists_action;
ELSE
SELECT 'Index already exists.' AS result,database_name,index_exists,@db_table_name,index_exists_action;
END IF;
ELSEIF index_action = 'modify' THEN
-- 修改索引(先删除后添加)
IF index_exists > 0 THEN
SET @query = CONCAT('ALTER TABLE `', database_name, '`.`', table_name, '` DROP INDEX `', index_name, '`');
PREPARE stmt FROM @query;
EXECUTE stmt;
DEALLOCATE PREPARE stmt;
SET @query = CONCAT('ALTER TABLE `', database_name, '`.`', table_name, '` ADD INDEX `', index_name, '` (', index_columns, ')');
PREPARE stmt FROM @query;
EXECUTE stmt;
DEALLOCATE PREPARE stmt;
select count(t2.INDEX_ID) INTO index_exists_action from information_schema.INNODB_TABLES t1 left join information_schema.INNODB_INDEXES t2 on t1.TABLE_ID=T2.TABLE_ID where t1.NAME=@db_table_name and t2.NAME=index_name;
SELECT 'Index modified successfully.' AS result,database_name,index_exists,@db_table_name,index_exists_action;
ELSE
SELECT 'Index does not exist. create' AS result,database_name,index_exists,@db_table_name,index_exists_action;
SET @query = CONCAT('ALTER TABLE `', database_name, '`.`', table_name, '` ADD INDEX `', index_name, '` (', index_columns, ')');
PREPARE stmt FROM @query;
EXECUTE stmt;
DEALLOCATE PREPARE stmt;
select count(t2.INDEX_ID) INTO index_exists_action from information_schema.INNODB_TABLES t1 left join information_schema.INNODB_INDEXES t2 on t1.TABLE_ID=T2.TABLE_ID where t1.NAME=@db_table_name and t2.NAME=index_name;
SELECT 'Index added successfully.' AS result ,database_name,index_exists,@db_table_name,index_exists_action;
END IF;
ELSEIF index_action = 'delete' THEN
-- 删除索引
IF index_exists > 0 THEN
SET @query = CONCAT('ALTER TABLE `', database_name, '`.`', table_name, '` DROP INDEX `', index_name, '`');
PREPARE stmt FROM @query;
EXECUTE stmt;
DEALLOCATE PREPARE stmt;
select count(t2.INDEX_ID) INTO index_exists_action from information_schema.INNODB_TABLES t1 left join information_schema.INNODB_INDEXES t2 on t1.TABLE_ID=T2.TABLE_ID where t1.NAME=@db_table_name and t2.NAME=index_name;
SELECT 'Index deleted successfully.' AS result,database_name,index_exists,@db_table_name,index_exists_action;
ELSE
SELECT 'Index does not exist.' AS result,database_name,index_exists,@db_table_name,index_exists_action;
END IF;
ELSE
SELECT 'Invalid index action.' AS result,database_name,index_exists,@db_table_name,index_exists_action;
END IF;
END //
DELIMITER ;
测试脚本
create table if not exists sys_agent
(
agent_id bigint not null comment '客服唯一id'
primary key,
agent_name varchar(64) null comment '客服名称',
agent_type varchar(30) null comment '客服类型(场地客服、直聘客服)',
district varchar(30) null comment '地区',
service_language varchar(30) null comment '服务语种',
agent_description varchar(500) null comment '客户描述',
status tinyint(1) null comment '状态(0=无效,1=有效),默认为1',
del_flag tinyint(1) null comment '是否删除(0=false,1=true)',
user_id bigint null comment '用户id(关联的用户信息)',
time_zone varchar(50) null comment '时区',
create_by varchar(50) null comment '创建者',
create_time datetime default CURRENT_TIMESTAMP null comment '创建时间',
update_by varchar(50) null comment '修改者',
update_time datetime default CURRENT_TIMESTAMP null on update CURRENT_TIMESTAMP comment '修改时间'
)comment '客服管理';
CALL modify_table_index('sys_agent', 'ix_agentName', 'add', 'agent_name,agent_type');
CALL modify_table_index('sys_agent', 'ix_agentName', 'delete', '');
CALL modify_table_index('sys_agent', 'ix_agentName', 'modify', 'agent_name,agent_type');
新增数据
replace into语句 按照主键或者唯一值,存在则先删除再插入,不存在则直接插入;
注意: 一定要写字段名称。
REPLACE INTO route_config (route_id, route_order, route_uri, route_filters, route_predicates, route_metadata, memo, created, updated, deleted) VALUES ('app-metadata-runtime', 1, 'lb://app-metadata-runtime', '[{"name":"StripPrefix","args":{"parts":"2"}}]', '[{"name":"Path","args":{"pattern":"/api/mr/**"}}]', '{}', '云枢服务app-metadata-runtime', '2020-07-31 21:44:11', '2020-09-07 20:24:13', 0);
小结
按照不同的场景写了对应的存储过程,使得修改字段,修改索引,修改表,插入数据可以重复执行。
如果有使用问题或者优化建议,欢迎提出来。还原跟我交流 ;
原创不易,关注诚可贵,转发价更高!转载请注明出处,让我们互通有无,共同进步,欢迎沟通交流。
程序员:你如何写可重复执行的SQL语句?的更多相关文章
- 程序员为什么要写if else,为什么要和别人不一样
程序员为什么要写if else,为什么要和别人不一样 前言 无聊,睡不着!本文只是随便写写而已!感叹一下程序员的生活! 刚看到一个八级程序员的分级,所以就写了这个随笔,分级如下: 第八级 ...
- 可重复执行的SQL Script
问题 在工作中偶尔会遇到这样的问题:SQL script重复执行时会报错. 理想的状态下,SQL script跑一遍就够了,是不会重复执行的,但是实际情况往往很复杂. 比如Dev同学在开发时在A环境把 ...
- EF-记录程序自动生成并执行的sql语句日志
在EntityFramework的CodeFirst模式中,我们想将程序自动生成的sql语句和执行过程记录到日志中,方便以后查看和分析. 在EF的6.x版本中,在DbContext中有一个Databa ...
- Delphi XE8中开发DataSnap程序常见问题和解决方法 (二)想对DBExpress的TSQLDataSet写对数据库操作的SQL语句出错了!
当我们搞定DataSnap后,我们进入客户端程序开发阶段了,我们建立了客户端模块后,打算按照刚才开发服务器的步骤开发客户端程序,随后加入了DBExpress的TSQLDataSet,设定数据库连接后, ...
- Yii2获取当前程序执行的sql语句
1.Yii2获取当前程序执行的sql语句: $query = model::find(); $dataProvider = new ActiveDataProvider([ ...
- 智能SQL优化工具--SQL Optimizer for SQL Server(帮助提升数据库应用程序性能,最大程度地自动优化你的SQL语句 )
SQL Optimizer for SQL Server 帮助提升数据库应用程序性能,最大程度地自动优化你的SQL语句 SQL Optimizer for SQL Server 让 SQL Serve ...
- IBatis.Net获取执行的Sql语句
前言 IBatis.Net中Sql语句是些在配置文件中的,而且配置文件是在程序启动时读取的(我们开发的时候需要将其设置成较新复制或者是始终复制),而不是程序将其包含在其中(例如NHibernate的映 ...
- [SQL]查询及删除重复记录的SQL语句
一:查询及删除重复记录的SQL语句1.查找表中多余的重复记录,重复记录是根据单个字段(peopleId)来判断select * from peoplewhere peopleId in (select ...
- Oracle 查询并删除重复记录的SQL语句
查询及删除重复记录的SQL语句 1.查找表中多余的重复记录,重复记录是根据单个字段(peopleId)来判断select * from peoplewhere peopleId in (select ...
- MySql使用show processlist查看正在执行的Sql语句
今天上班例行的查看了下服务器的运行状况,发现服务器特卡,是mysqld这个进程占用CPU到了99%导致的. 比较好奇是那个程序在使用mysql导致cpu这么高的,通过show processlist命 ...
随机推荐
- PHP代码审计——ThinkPHP基础
一.ThinkPHP概述 1. ThinPHP是一个轻量级的PHP框架,旨在提供快速开发Web应用程序的工具和资源.它采用了MVC(Model-View-Controller)架构,使开发人员可以更好 ...
- 探索JS中this的最终指向
js 中的this 指向 一直是前端开发人员的一个痛点难点,项目中有很多bug往往是因为this指向不明确(this指向在函数定义时无法确定,只有在函数被调用时,才确定该this的指向为最终调用它的对 ...
- Part2: DDPM as Example of Variational Inference
很多次翻看DDPM,始终不太能理解论文中提到的\(\text{Variational Inference}\)到底是如何在这个工作中起到作用.五一假期在家,无意间又刷到徐亦达老师早些年录制的理论视频, ...
- FreeSWITCH添加自定义endpoint
操作系统 :CentOS 7.6_x64 FreeSWITCH版本 :1.10.9 日常开发过程中会遇到需要扩展FreeSWITCH对接其它系统的情况,这里记录下编写FreeSWITCH ...
- CompTIA Pentest+
关于学习后CompTIA Pentest+笔记 渗透测试工具 讲述了nmap,burp Suite,Metasploit,Nessus,hydra的入门使用 nmap:https://www.cnbl ...
- 尚医通day11-Java中阿里云对象存储OSS
页面预览 用户认证 用户登录成功后都要进行身份认证,认证通过后才可以预约挂号. 认证过程:用户填写基本信息(姓名.证件类型.证件号码和证件照片),提交平台审核 用户认证相关接口: (1)上传证件图片 ...
- Python编程和数据科学中的机器学习:如何处理和可视化具有噪声和干扰的数据
目录 随着数据科学和机器学习的快速发展,处理和分析具有噪声和干扰的数据成为了一个日益重要的挑战.在数据科学和机器学习中,噪声和干扰通常来自于各种因素,例如随机性和非随机性,数据缺失,数据集中的错误或错 ...
- React学习时,自己拟定的一则小案例(table表格组件,含编辑)
某次在Uniapp群看到有人问uniapp如何操作dom元素. 他想对这张表标红的区域,做dom元素获取,因为产品想让红色色块点击时,成为可编辑,渲染1~4月份之间的行程安排. 于是,有小伙伴说让他用 ...
- 使用numpy实现bert模型,使用hugging face 或pytorch训练模型,保存参数为numpy格式,然后使用numpy加载模型推理,可在树莓派上运行
之前分别用numpy实现了mlp,cnn,lstm,这次搞一个大一点的模型bert,纯numpy实现,最重要的是可在树莓派上或其他不能安装pytorch的板子上运行,推理数据 本次模型是随便在hugg ...
- 暴力密码破解工具——Hydra
Hydra简介 hydra中文意思为九头蛇,它是黑客组织thc开发一款开源暴力密码破解工具,可以在线破解多种密码,支持Windows Linux MAC系统安装使用(Kali Linux自带), 支持 ...