一文掌握如何编写可重复执行的SQL

embedded/2024/12/25 13:08:49/

一文掌握如何编写可重复执行的SQL

文章已同步个人博客:一文掌握如何编写可重复执行的SQL

背景

先提出问题,这里的可重复执行是指什么?我们为什么要编写可重复执行的sql

可重复执行是指一条sql重复多次执行都不会报错,不会因为报错而中断同sql脚本的其它sql语句。
比如如下的建表sql只能执行一次,再次执行就会报错,提示我们example_table 表已存在。

sql">CREATE TABLE `example_table` (`id` INT NOT NULL AUTO_INCREMENT,`name` VARCHAR(100) NOT NULL,`age` INT DEFAULT 0,`email` VARCHAR(200),PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;

通常来说项目发版的执行SQL脚本语句是由DDL 和 DML 组成的,如果SQL脚本文件中某个SQL执行异常就会中断整个SQL脚本文件的执行。我们还要根据报错SQL的位置,重新将SQL脚本文件中未执行的SQL摘出来重新整理执行。一旦报错,整个SQL脚本执行过程就变得繁琐起来。

编写可重复执行的SQL,变成了解决这一痛点的利器。当SQL脚本中的SQL语句都是可重复执行的,脚本中某个SQL有问题,直接在当前SQL脚本中就可以修改,我们若需要重新执行,只需要重新执行该SQL脚本就可以。其他已经执行成功的SQL,依然会成功执行,也不会对库表数据造成影响。接下来我们梳理一下各类SQL可重复执行的写法和注意点,本文中的SQL均基于MySql语法。

如何实现

表格列举出编写SQL中常见的sql需求:

SQLSQL类型
创建表DDL
对表新增字段DDL
对表修改字段DDL
对表新增索引DDL
插入一条新记录DML
对表中记录进行更新DML
对表中记录进行删除DML

sql_37">编写SQL涉及到的Mysql语法和系统表

  1. IF NOT EXISTS
  2. MySql 预处理语句原生语法
  3. 字段表 INFORMATION_SCHEMA.COLUMNS
  4. 索引表 INFORMATION_SCHEMA.STATISTICS
  5. 查询当前数据库名称 SCHEMA()
预处理语句语法介绍

预处理语句(Prepared Statements)是一种将 SQL 查询与其参数分离的机制。与传统的查询方式不同,预处理语句首先会将 SQL 查询进行编译、优化,并将其缓存,随后可以多次执行该查询,而不必每次都重新编译和解析 SQL 语句。每次执行时,预处理语句只需要提供不同的参数即可,这使得它在需要执行多次相同 SQL 查询的场景中具有明显的性能优势。

sql">-- 准备预处理语句
PREPARE stmt FROM 'SELECT name, age FROM users WHERE id = ?';-- 设置参数并执行语句
SET @userId = 1;
EXECUTE stmt USING @userId;-- 释放预处理语句
DEALLOCATE PREPARE stmt;
  • PREPARE 将带有占位符 ? 的 SQL 语句预处理并编译。
  • SET 用于设置查询参数。
  • EXECUTE 执行预处理语句并传递参数。
  • DEALLOCATE PREPARE 释放预处理语句,避免占用资源。

接下来具体说明一下各种sql如何改写为可重复执行的写法。

创建表

原写法:

sql">CREATE TABLE `example_table` (`id` INT NOT NULL AUTO_INCREMENT,`name` VARCHAR(100) NOT NULL,`age` INT DEFAULT 0,`email` VARCHAR(200),PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;

创建表可以使用IF NOT EXISTS 语法进行判断,仅当表不存在的时候才会创建表。

可重复执行的写法:

sql">CREATE TABLE IF NOT EXISTS `example_table` (`id` INT NOT NULL AUTO_INCREMENT,`name` VARCHAR(100) NOT NULL,`age` INT DEFAULT 0,`email` VARCHAR(200),PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
对表新增字段

原写法:

sql">ALTER TABLE example_table ADD COLUMN create_time datetime null comment '创建时间';

我们能不能也判断如果这个表中没有这个字段才执行新增字段,答案是可以的。在这里就要用到 MySql 预处理语句的语法了。

判断,如果在当前数据库中,存在当前这个表,表中没有这个字段,那么才会执行新增该字段。

  • INFORMATION_SCHEMA.COLUMNS 系统级的字段表,记录了全部的字段信息
  • SCHEMA() 当前数据库名称

可重复执行的写法:

sql">set @sql = 'select 1 from dual;';
select ' ALTER TABLE example_table add COLUMN create_time datetime NULL comment ''创建时间'' ;' into @sql
from dual where (select count(1) FROM INFORMATION_SCHEMA.COLUMNS WHERE TABLE_SCHEMA=SCHEMA() AND TABLE_NAME='example_table' AND COLUMN_NAME='sort')=0;
PREPARE stmt FROM @sql;
EXECUTE stmt;
DEALLOCATE PREPARE stmt;
对表修改字段

原写法:

sql">ALTER TABLE example_table MODIFY COLUMN email VARCHAR(500) DEFAULT '' comment '邮箱';

同样使用 MySql 预处理语句的语法,判断在当前数据库中,存在这个表,且有这个字段,那么才会执行修改字段的语句。

可重复执行的写法:

sql">set @sql = 'select 1 from dual;';
select ' ALTER TABLE example_table MODIFY COLUMN email VARCHAR(500) DEFAULT '''' comment ''邮箱'' ;' into @sql
from dual where (select count(1) FROM INFORMATION_SCHEMA.COLUMNS WHERE TABLE_SCHEMA=SCHEMA() AND TABLE_NAME='example_table' AND COLUMN_NAME='email')=1;
PREPARE stmt FROM @sql;
EXECUTE stmt;
DEALLOCATE PREPARE stmt;
对表新增索引

原写法:

sql">ALTER TABLE example_table add index idx_name (name) COMMENT '名称索引';

要判断当前数据库中,这个表中,根据索引名查询,未查到该索引就进行添加索引。

  • INFORMATION_SCHEMA.STATISTICS 系统级的索引表,记录了全部的索引信息

可重复执行的写法:

sql">set @sql = 'select 1 from dual;';
select 'ALTER TABLE example_table add index idx_name (name) COMMENT ''名称索引'';' into @sql from dual
where (select count(1) FROM INFORMATION_SCHEMA.STATISTICS where TABLE_SCHEMA=SCHEMA() and TABLE_NAME='example_table' and index_name='idx_name')=0;
PREPARE stmt FROM @sql;
EXECUTE stmt;
DEALLOCATE PREPARE stmt;
插入一条新记录

原写法:

sql">insert into example_table(name, age, email, create_time) VALUE ('jack', 18, 'jackaaa@gmail.com', now());

在这里我们判断如果这个表中没有jack这个name,才进行插入记录。使用DUAL虚拟表帮助我们添加判断条件。

可重复执行的写法:

sql">INSERT INTO example_table
(name, age, email, create_time)
SELECT 'jack', 18, 'jackaaa@gmail.com', now()
FROM DUAL
WHERE NOT EXISTS (select id from example_table where name = 'jack');
对表中记录进行更新

update 语句因为其天生的幂等特质,不需要改写,就支持可重复执行。

sql">update example_table set age = 20 where name = 'jack';
对表中记录进行删除

delete 语句因为其天生的幂等特质,不需要改写,就支持可重复执行。

sql">delete from example_table where name = 'tom';

总结

至此,SQL脚本中常见的7类SQL写法如何改写为可重复执行的SQL,就整理完成了。可重复执行的SQL脚本不仅在执行时提供了便利,也为项目迁移,项目本地化部署带来了便利。

参考

  1. mysql性能优化-预处理语句(Prepared Statements)

http://www.ppmy.cn/embedded/148627.html

相关文章

【网络云计算】2024第51周-每日【2024/12/20】小测-理论-周测

文章目录 1、软件RAID的常用级别和硬件RAID的区别,制作RAID生产环境有哪些注意事项2、交换机常用的技术有哪些3、NFS服务器如何结合LVM4、写两个if语句,判断文件和目录是否存在5、链路聚合和VRRP有哪些区别? 【网络云计算】2024第51周-每日【…

SSM 架构下 Vue 电脑测评系统:为电脑性能评估赋能

2相关技术 2.1 MYSQL数据库 MySQL是一个真正的多用户、多线程SQL数据库服务器。 是基于SQL的客户/服务器模式的关系数据库管理系统,它的有点有有功能强大、使用简单、管理方便、安全可靠性高、运行速度快、多线程、跨平台性、完全网络化、稳定性等,非常适…

DeepSeek-VL2部署指南

DeepSeek-VL2是一款力图学习和实现深度学习和视觉语言结合的工具包。本文将介绍如何在本地环境中安装和部署DeepSeek-VL2。 环境要求 在部署DeepSeek-VL2前,您需要确保以下环境充分满足要求: 确保硬件资源 处理器:最小8核字中处理器 内存…

C# 异步编程与多线程简析

引言 在现代软件开发中,异步编程和多线程是实现高性能、高响应性应用程序的关键技术。C# 提供了多种机制来支持异步编程和多线程,其中Thread、ThreadPool 和Task 是最为常用的三种。本文将对这三种机制进行简要分析,探讨它们的使用场景、优缺点以及如何在实际开发中选择合适…

微信小程序用户信息解密 AES/CBC/NoPadding 解密失败问题

解密文档如下 java springboot 微信小程序登录,获取获取手机号,获取unionId和解密用户信息 问题: 突然有一天解密失败,之前使用AES/CBC/NoPadding都可以,然后debug查看多出一些字符 原因: **AES/CBC/NoPadding**** 模式下解密后的数据可能包含额外的填充字符**(例…

【pytorch】多层感知机

将许多全连接层堆叠在一起。每一层都输出到上面的层,直到生成最后的输出。我们可以把前L−1层看作表示,把最后一层看作线性预测器。这种架构通常称为多层感知机通常缩写为MLP。 1 激活函数 激活函数(activation function)通过计…

群晖利用acme.sh自动申请证书并且自动重载证书的问题解决

前言 21年的时候写了一个在群晖(黑群晖)下利用acme.sh自动申请Let‘s Encrypt的脚本工具 群晖使用acme自动申请Let‘s Encrypt证书脚本,自动申请虽然解决了,但是自动重载一直是一个问题,本人也懒,一想到去…

vue中proxy代理配置(测试一)

接口地址:http://jsonplaceholder.typicode.com/posts 1、配置一(代理没起作用) (1)设置baseURL为http://jsonplaceholder.typicode.com (2)proxy为 ‘/api’:’ ’ (3&a…