Mysql 学习之DDL
1、Mysql 学习之DDL
DDL:数据库定义语言
2、DDL相关学习
数据库定义语言(DDL) 一、库的管理 创建、修改、删除 二、表的管理 创建、修改、删除 创建 create 修改 alter 删除 drop 三、表的复制 仅仅复制表的结构 create table 新表 like 旧表 CREATE TABLE copy like SC 复制表的结构+数据 CREATE TABLE copy2 SELECT * from SC WHERE sid = '01' 仅仅复制某些字段同时不要数据 CREATE TABLE copy3 select sid,cid from SC WHERE 0 特别的注意啊 mysql 中 0 表示 false 非零表示true 映射到java 对象属性的时候也是true或者false CREATE TABLE copy4 select sid,cid from SC WHERE 3 四、表的删除 drop table 表名 如下: drop TABLE copy4 drop table if EXISTS t2 五、数据类型 分类 数值型 整型 小数 定点数 浮点数 字符型 较短的文本: char,varchar 较长的文本: text,blob(较长的二进制数据图片) 日期型 整型 TinyInt, SmallInt, MediumInt, int, BigInt 字节数: 1 2 3 4 8 每个字节由8个比特组成 对应的java数据类型 byte short 无 int Long 字节数: 1 2 4 8 每个字节由8个比特组成 整型的特点 1、创建表的时候如果不指定长度会有默认的长度,长度的意思是显示的最大宽度,不是可以插入的最大最小值,插入的数值 大小由数据类型来决定的,如果指定了长度如果再插入时长度不够会再左边由零补(配合zerofill才有效果),一般可以不指定长度。 int(4) zerofill 如果插入的数据长度超过4为 例如 插入 23 会用0来补充 结果是0023 小数 浮点型 float(M,D) double(M,D) 定点数 decimal(M,D) float double DECIMAL(M,D) 字节数: 4 8 对应的java数据类型 float double BigDecimal 字节数: 4 8 特点: 1、M代表的意思整数位和小数位的个数和 DECIMAL(3,1) 表示小数个数是1,整数个数是2; double(4,1) 表示小数个数是1,整数个数是3 2、D表示小数点后的个数 3、M和D都可以省略但是如果定点型小数(decimal) 默认是(10,0),float和double不用指定精度(小数点个数) 4、如果涉及到了金额运算就要用decimal了,如果只是数据的展示就用float就行了。 5、浮点型的数据在计算时会有精度的损失===> 和java中一样 浮点数类型有个缺陷,就是不精准。 比如,我们设计一个表,有f1这个字段,插入值分别为0.47,0.44,0.19,我们期待的运行结果是:0.47 + 0.44 + 0.19 = 1.1。而使用sum之后查询 查询结果是 1.0999999999999999。看到了吗?虽然误差很小,但确实有误差。 你也可以尝试把数据类型改成 FLOAT,然后运行求和查询,得到的是, 1.0999999940395355。显然,误差更大了 字符型 较短的文本 char(M),varchar(M) 较长的文本: text(系列),blob(较长的二进制数据图片) 较短的二进制 binary和varbinary 一般图片不会放到数据库里面,一般放图片地址,这两个类型用的不多 特点: 1、M代表的意思是 最多字符数 2、char代表的是固定长度的字符,char比较耗费存储空间,char 效率比较好 3、varchar代表的是可变长度的字符,varchar比较省存储空间,varchar 效率比较低,表示能存的字符长度 4、char类型的字段长度可以省略默认是1,varchar不能省略 什么时候用char 或者varchar 例如存储字符比较固定的就用char,不固定的用varchar,例如sex字段 用char(1),name用varchar(20) 枚举 这个枚举是补充说明的,一般枚举对应的是数据库表中的字段的类型是varchar类型 日期型 Timestamp:时间戳模式 年月日 时分秒 取值范围 1970-01-01 00:00:00 至 2038-01-19 03:14:07 Datetime:日期模式 年月日 时分秒 取值范围 1000-01-01 00:00:00 至 9999-12-31 23:59:59 DATE:日期模式 年月日 取值范围 1000-01-01 至 9999-12-31 以上三个用的最多 mysql的当前时间和系统时间不一致问题解决 https://blog.csdn.net/guiyin1150/article/details/116171503?spm=1001.2101.3001.6650.1&utm_medium=distribute.pc_relevant.none-task-blog-2%7Edefault%7ECTRLIST%7Edefault-1-116171503-blog-121224663.pc_relevant_multi_platform_whitelistv2&depth_1-utm_source=distribute.pc_relevant.none-task-blog-2%7Edefault%7ECTRLIST%7Edefault-1-116171503-blog-121224663.pc_relevant_multi_platform_whitelistv2&utm_relevant_index=1 六、常见的约束 含义: 一种限制,用于限制表中的数据,为了保证表中的数据的准确性和可靠性 分类: FOREIGN KEY: 外键,用于限制两个表的关系,用于保证该字段的值必须来自于主表关联列的值,用于保证数据的准确,在从表添加外键 从表的外键一般是主表的主键同时先要创建主表,从表的外键才能使用主表的key 插入数据时应该先插入主表的数据,才能添加从表 删除数据时先删除从表的数据,然后才能删除主表的数据 非空约束: 限制该字段的数据不能为null; 唯一约束: UNIQUE,保证该字段的所有数据都是唯一,不重复的,可以是多个字段组成唯一约束; 主键约束: 主键是一行数据的唯一标识,要求非空且唯一 建表案例1: DROP TABLE IF EXISTS `t_seckill_order`; CREATE TABLE `t_seckill_order` ( `id` bigint(20) NOT NULL PRIMARY key AUTO_INCREMENT COMMENT '秒杀商品id', `user_id` bigint(20) DEFAULT NULL, `order_id` bigint(20) DEFAULT NULL COMMENT '订单id', `goods_id` bigint(20) DEFAULT NULL COMMENT '商品ID' ); 给表添加一个或多个字段的唯一约束 alter table t_seckill_order add constraint seckill_uid_gid UNIQUE(user_id,goods_id); constraint [kənˈstreɪnt] 约束的意思。 alter table t_seckill_order add constraint seckill_uid_gid UNIQUE(user_id); 删除表的唯一性约束 alter table t_seckill_order drop key seckill_uid_gid; 查看表的约束 show keys from 表名 主键和唯一主键的大对比 1、主键和唯一索引都能保证唯一性(添加、修改、删除、查询) 2、主键不允许为空唯一索引可以为空(只能一个) 3、主键一个表只能有一个,唯一索引可以有多个(多个字段) 4、主键和唯一索引都可以由多个字段组合 什么时候创建唯一索引场景 我们一般主键如果是自增或者代码生成的,在高并发的时候如果单单的靠主键,不能保证业务数据的唯一性,这时候就要用上了唯一所以
3、表结构修改
表的修改 alter table 表名 add|drop|modify|change column 修改列名 alter table author change COLUMN au_name au_name1 VARCHAR(20) DEFAULT null COMMENT '作者名称'; 修改列的类型或约束 alter table author MODIFY COLUMN id varchar(30); 如果id 是主键,此时修改列类型后 id依然是主键 添加新列 alter table author add COLUMN annual DOUBLE;
添加修改时间,默认每次修改数据都会自动的修改 update_time 的字段值
alter table st_user add COLUMN update_time timestamp not null DEFAULT CURRENT_TIMESTAMP on UPDATE CURRENT_TIMESTAMP COMMENT '创建时间';
删除列
alter table author drop column annual;
修改表名
alter table author RENAME to book_author;
4、存储过程
存储过程使用说明 定义 存储过程是存储在数据库目录中的一组为了完成特定功能的SQL语句集合。 使用存储过程的目的是将常用或复杂的工作预先用SQL语句写好并用一个指定名称存储起来,这个过程经编译和优化后存储在数据库服务器中,因此称为存储过程。 存储过程开发思想是面向过程的编程方式。 优点 在生产环境中,可以通过直接修改存储过程的方式修改业务逻辑(或 bug),而不用重启服务器 执行速度快,存储过程经过编译之后会比单独一条一条执行要快 减少网路传输 减少了和数据库的交互 方便优化 可以直接改sql 缺点 过程化编程,复杂业务处理的维护成本高。---面向过程的编程方式。 调试不便 不同数据库之间可移植性差,---不同的数据库语法不一致 存储过程语法 基本语法结构 -- 声明结束符。因为MySQL默认使用 分号(;)作为结束符,而在存储过程中,会使用 分号(;) 作为一段语句的结束,导致 分号(;)使用冲突。所以存储过程需要声明自己的结束符 delimiter $$ create proceduer 存储过程名字() begin SQL业务逻辑 end $$ 调用存储过程 call 存储过程名字(); 类似java中的方法。 案例1: delimiter $$ CREATE PROCEDURE hello_proceduer() BEGIN select * from score; END $$ call hello_proceduer(); 注意: 每次写存储过程时,都要重新的声明一个结束符。 存错过程的删除 DROP PROCEDURE if EXISTS `sp_var01`; 存储过程中声明变量和赋值 局部变量 declare 变量名 类型 . 局部变量在begin end中有效 set方式赋值 案例1: DROP PROCEDURE if EXISTS `sp_var01`; delimiter $$ CREATE PROCEDURE sp_var01() BEGIN DECLARE var_name varchar(30) DEFAULT 'SF'; set var_name = 'UG'; select var_name; END $$ call sp_var01(); 用户变量 用户变量在存储过程中不需要声明。可以直接使用和赋值,用户变量是基于会话变量来实现的。 set @变量名 = 值 set @变量名 := 值 select @变量 := 值; select 只能用:= select 字段 into @变量名 from 表; 把表中的某个字段的值放到变量里,字段的值必须是一个 位置 begin end中 案例1: drop PROCEDURE if EXISTS `sp_var02`; delimiter $$ CREATE PROCEDURE sp_var02() begin set @nick_name = 'gao'; end $$ call sp_var02(); select @nick_name; 案例2 drop PROCEDURE if EXISTS sp_var_into; delimiter // CREATE PROCEDURE sp_var_into() BEGIN select c_name INTO @c_name from course where c_id = '01'; END // call sp_var_into(); select @c_name; IF条件判断 语法 if 条件 then 自定义SQL elesif 条件 then 自定义SQL else 自定义SQL end if; 案例1 查询某员工的入职年限判断是是新手、老员工、元老等。 drop PROCEDURE if EXISTS `judge_year`; delimiter // create PROCEDURE judge_year(in emp_id int) BEGIN DECLARE e_year int; DECLARE res varchar(32); select TIMESTAMPDIFF(year,hiredate,now()) INTO e_year from employees WHERE employee_id = emp_id; if e_year > 25 THEN set res = '元老'; ELSEIF e_year < 25 and e_year > 20 then set res = '老员工'; ELSE set res = '新手'; end if; select first_name,res from employees where employee_id = emp_id; END // CALL judge_year(101); 注意点 1、if使用的频率远远高于case,推荐使用if 2、条件判断成功后写的sql要用分号结束,否则会报错,case语法也是。 3、end if 后面也要加 分号 CASE条件判断 case when 条件 then 逻辑; else 逻辑; end case; 案例1 查询某员工的入职年限判断是是新手、老员工、元老等。 drop PROCEDURE if EXISTS `judge_by_case`; delimiter // create PROCEDURE judge_by_case(in emp_id int) BEGIN DECLARE res VARCHAR(32); DECLARE e_year int(4); SELECT TIMESTAMPDIFF(YEAR,hiredate,now()) INTO e_year FROM employees where employee_id = emp_id; CASE WHEN e_year > 25 THEN set res = '元老'; when e_year > 20 AND e_year <= 25 THEN set res = '老员工'; ELSE set res = '新手'; END CASE; select first_name,res from employees where employee_id = emp_id; end // 条件循环 while循环 条件满足进行循环。 语法: while 条件 do SQL逻辑; end while; 案例: 计算从1 累加到n的值,n为传入的值。 drop PROCEDURE if EXISTS `cc_while1`; delimiter // create PROCEDURE cc_while1(in num int) BEGIN DECLARE res int; DECLARE num1 int; set res = 0; set num1 = num; WHILE num1 > 0 DO SET res := res + num1; set num1 = num1 -1; END WHILE; select res; end // call cc_while1(10); repeat循环 理解: repeat是有条件的循环控制语句,当满足条件的时候退出循环。类似于java中的 do - while循环。 java中的do while 是如果条件成立继续循环,条件不成立退出循环,Mysql中的循环是条件不成立就继续 循环,条件成立退出循环。 语法: repeat sql 逻辑 until 条件 end repeat; 案例1 计算从1 累加到n的值,n为传入的值 drop PROCEDURE if EXISTS `xh_repeat`; delimiter // create PROCEDURE xh_repeat(in num int,out num1 int) begin DECLARE res int DEFAULT 0; REPEAT set res = res + num; set num = num - 1; UNTIL num = 0 end REPEAT; set num1 = res; end // 注意点 UNTIL sql 这里最后不用加分号; loop 循环 理解: 使用该关键字来实现循环,如果不加控制语句很可能出现死循环,一般要配合另外两个关键字来使用,leave和iterate 该语法类似java中的for循环 使用 continue和break; leave:结束整个循环,类似java中的break. iterate:必须用在循环中,作用是跳过当前的循环剩下的语句,直接进入下个循环,类似java中的continue; 语法: 当前循环的自定义名字:loop sql逻辑;(leave 当前循环的自定义名字) end loop 当前循环的自定义名字; 案例1 计算从1 累加到n的值,n为传入的值 drop PROCEDURE if EXISTS `xh_loop`; delimiter // create PROCEDURE xh_loop(in num int) begin DECLARE res int DEFAULT 0; qh:loop set res = res + num; set num = num -1; if num = 0 THEN LEAVE qh; --注意这里要加循环的名字。 end if; end loop qh; SELECT res; END // 案例2 计算从1到n之间的偶数累加的值,n为传入的参数值 drop PROCEDURE if EXISTS `qh_js_loop`; delimiter // create PROCEDURE qh_js_loop(in num int) BEGIN DECLARE res int DEFAULT 0; sum:LOOP if num = 0 THEN LEAVE sum; end if; if num % 2 = 0 THEN set res = res + num; set num = num -1 ; ELSE set num = num -1 ; ITERATE sum; end if; end LOOP sum; SELECT res; end // call qh_js_loop(4); 游标 定义: 游标是用来存储查询结果集的数据类型,在存储过程中和函数中可以使用游标对结果集进行循环处理。 游标的使用包括游标的声明,open,fetch和close. 和变量的区别 变量只能存储单行单列数据,游标可以存储多行多列的数据。 语法 声明游标 declare 游标名称 cursor for 查询语句; 打开游标 open 游标名称; //使用游标之前必须要打开游标 获取游标记录 fetch 游标名称 into 变量1[变量2] 把游标中的数据,赋值给变量1,2,3。 关闭游标 close 游标名称; 案例1 根据传入的参数uage,来查询学生表student 中,所有学生年龄小于等于uage的用户姓名和专业 并将用户的姓名和专业插入到所创建的一张新表(id,name,age)中. BEGIN DECLARE id int; DECLARE student_name VARCHAR(32); DECLARE age int; DECLARE u_cursor CURSOR for select s_id,s_name, TIMESTAMPDIFF(year,s_birth,NOW()) as 'age' from student where TIMESTAMPDIFF(year,s_birth,NOW()) < 33; drop table if EXISTS `my_student`; create table my_student( s_id int PRIMARY KEY not null, s_name VARCHAR(32) COMMENT '学生姓名', age int ); open u_cursor; #获取游标中的数据 while true DO FETCH u_cursor into id,student_name,age; insert into my_student values(id,student_name,age); end WHILE; CLOSE u_cursor; end 注意点 1、游标变量的声明需要放到声明的普通变量之后。 2、从游标中获取数据完毕后的处理方式 条件处理程序 定义: 可以用来定义在流程控制结构执行过程中遇到问题时相应的处理步骤 作用: 什么时候被触发,触发了之后执行什么样的动作,类似于java中的try catch 的作用。 语法: declare handler_action(动作) HANDLER for condition_value 自定义的业务SQL; handler_action exit 终止执行当前程序 continue 继续执行当前程序 condition_value SQLSTATE sql状态码 如 02000 not found: 所有以 02 开头的SQLSTATE 状态码的简写 sqlwarning: 所有以 01 开头的 SQLSTATE 状态码的简写 sqlexception: 所有没有被 sqlwarning 或 not found 捕获的 SQLSTATE 的简写 案例1 游标执行报02000 状态码的错误后,关闭游标 DECLARE exit HANDLER for SQLSTATE '02000' CLOSE u_cursor; 意思是存储过程报的错误状态码是 02000 就退出存储过程,然后关闭游标 DECLARE exit HANDLER for NOT FOUND CLOSE u_cursor; 意思是存储过程报的错误状态码是 02 开头的 就退出存储过程,然后关闭游标 游标在执行过程执行的查询报表不存错误后,关闭游标 DECLARE exit HANDLER for SQLEXCEPTION CLOSE u_cursor; 案例2: 存储过程执行错误时,提示错误信息 drop PROCEDURE if EXISTS `xh_while_exp`; delimiter // create PROCEDURE xh_while_exp (in num int) BEGIN DECLARE my_num int DEFAULT 1; DECLARE res int DEFAULT 0; DECLARE exit HANDLER for SQLEXCEPTION SELECT '程序执行错误' as res; set my_num = num; WHILE my_num > 0 DO set res = res + my_num; set my_num = my_num - 1; if num = 10 THEN select * from student2; #这里的表不存在 end if; END WHILE; select res; END // 注意点 1、定义的条件处理程序放在存储过程中定义的变量最后 2、定义的条件处理程序前面不能设置变量值。
5、存储函数
存储函数 定义: 存储函数是有返回值的存储过程,存储函数的参数只能是in类型的 语法 delimiter // create function 函数名(一个或多个参数) returns type(int,varchar,decimal,long等) begin --SQL逻辑 return ...; end // 注意点: 1、必须设置返回值 2、存储函数用的比较少 存储函数能做的,存储过程也能做 3、不能使用SELECT在存储函数中显示消息,您只能通过RETURN语句返回单个值 、 意思是在函数中不能使用select 语句来查询。即使是 4、mysql 5.7 函数的参数不用写 in CREATE FUNCTION my_function_2(in num int) 这样写不对 CREATE FUNCTION my_function_2(num int) 这样写才对 函数的调用 select 函数名(参数); 案例1 计算从1 累加到n的值,n为传入的参数值 drop FUNCTION if EXISTS `my_first_function`; delimiter // create FUNCTION my_first_function(num int) RETURNS INT BEGIN DECLARE my_num int DEFAULT 0; DECLARE res int DEFAULT 0; set my_num = num; WHILE my_num > 0 DO set res := res + my_num; set my_num := my_num - 1; end WHILE; RETURN res; end // select my_first_function(100); 案例2: 计算从1 到n 累加 偶数的值,n为传入的参数值 drop FUNCTION if EXISTS `my_function_2`; delimiter // CREATE FUNCTION my_function_2(num int,res int) RETURNS int BEGIN declare my_num int default 0; set my_num := num; set res := 0; sum: LOOP if my_num = 0 THEN LEAVE sum; END if ; if my_num % 2 = 0 THEN SET res := my_num + res; set my_num := my_num - 1; ELSE set my_num := my_num - 1; end if; END LOOP sum; RETURN res; end // select my_function_2(100,2);
6、触发器
触发器 定义: 触发器是与表有关的数据库对象,指在 insert/update/delete 之前或之后,触发并执行触发器中定义的SQL语句集合。触发器的这种特性可以 协助应用在数据库端确保数据的完整性,日志记录,数据校验等操作。 使用别名 OLD 和 NEW 来引用触发器中发生的变化的记录内容。 现在触发器只支持行及触发,不支持语句级触发。 行及触发触发的意思是当前的SQL,影响了原始表的那些行,每一行的变化都会是触发器执行。 语句级触发的意思是,当前的sql执行一次就让触发器执行一次。 语法: 创建 create trigger 触发器名字 before/after insert/update/delete on 表名 for each row --行级触发器 begin 自定义的SQL end 查看 SHOW TRIGGERS; 删除 DROP TRIGGER [schema_name] 自定义的触发器名字 触发器的类型 insert 型的触发器 NEW 表示将要或者已经新增的数据 update 型的触发器 OLD 表示修改之前的数据,new 表示将要或已经修改后的数据 delete 型触发器 OLD 表示将要或已经删除的数据 案例1: 通过触发器记录tb_user 表的数据变更日志,将变更日志插入到日志表user_logs中,记录添加数据的日志; 创建表 create table user_logs( id int(11) primary key not null auto_increment, operation varchar(20) not null comment '操作类型 insert / update / delete', operate_time datetime not null comment '操作时间', operate_id int(11) not null comment '操作用户表的id', operate_params varchar(500) comment '操作参数' )engine=innodb default charset=utf8; 创建新增触发器 drop TRIGGER if EXISTS `my_first_trigger`; delimiter // create TRIGGER my_first_trigger BEFORE INSERT on `user` FOR each row BEGIN INSERT INTO user_logs(id,operation,operate_time,operate_id,operate_params) values( null,'insert',now(),NEW.id,CONCAT("插入的数据内容为:id = ",new.id,",user_name=",new.user_name,",age=",new.age,',gender=',new.gender)); END // 案例2: 通过触发器记录tb_user 的变更日志,将变更日志插入到日志表user_logs中。 drop TRIGGER if EXISTS `my_trigger_update`; delimiter // create TRIGGER my_trigger_update AFTER UPDATE on `user` FOR each row BEGIN insert into user_logs(id,operation,operate_time,operate_id,operate_params) values(null,'update',now(),new.id,CONCAT('修改后的数据是:','id=',new.id,',user_name=',new.user_name,',age=',new.age, '更新前的数据是:','id=',old.id,',user_name=',old.user_name,',age=',old.age)); END // 测试 UPDATE user SET age = 20 WHERE id <= 2; 结果是user_logs 添加了两条数据。 案例三: 通过触发器记录tb_user 的变更日志,将删除的日志插入到日志表user_logs中。 drop TRIGGER if EXISTS `my_trigger_delete`; delimiter // create TRIGGER my_trigger_delete AFTER DELETE on `user` FOR each row BEGIN insert into user_logs(id,operation,operate_time,operate_id,operate_params) values(null,'delete',now(),old.id,CONCAT('删除前的数据是:','id=',old.id,',user_name=',old.user_name,',age=',old.age)); END // 测试: DELETE FROM user WHERE id <= 6; 注意点: 1、truncate 表时,删除的触发器,不执行。 2、触发器里面执行的sql,和原表的操作不是事务的关系。