mysql进阶
mysql约束类型
约束含义:一种限制,用于限制表中的数据,为了保证表中的数据的准确和可靠性。
约束分类:六大约束
①NOT NULL:非空,用于保证该字段的值不能为空
比如姓名、学号等
②DEFAULT:默认,用于保证该字段有默认值
比如性别
③PRIMARY KEY:主键,用于保证该字段的值具有唯一性,并且非空
比如学号、员工编号等
④UNIQUE:唯一,用于保证该字段的值具有唯一性,可以为空
比如座位号
⑤CHECK:检查约束【mysql中不支持】
比如年龄、性别
⑥FOREIGN KEY:外键,用于限制两个表的关系,用于保证该字段的值必须来自于主表的关联列的值
在从表添加外键约束,用于引用主表中某列的值
比如学生表的专业编号,员工表的部门编号,员工表的工种编号
何时添加约束:
1.创建表时
2.修改表时
约束的添加分类:
列级约束:
六大约束语法上都支持,但外键约束没有效果
表级约束:
除了非空、默认,其他的都支持
主键和唯一的大对比:
保证唯一性 是否允许为空 一个表中可以有多少个 是否允许组合
主键 √ × 至多有1个 √,但不推荐
唯一 √ √ 可以有多个 √,但不推荐
外键:
1、要求在从表设置外键关系
2、从表的外键列的类型和主表的关联列的类型要求一致或兼容,名称无要求
3、主表的关联列必须是一个key(一般是主键或唯一)
4、插入数据时,先插入主表,再插入从表
删除数据时,先删除从表,再删除主表
CREATE TABLE 表名( 字段名 字段类型 列级约束, 字段名 字段类型, 表级约束 ) CREATE DATABASE students; #一、创建表时添加约束 #1.添加列级约束 /* 语法: 直接在字段名和类型后面追加 约束类型即可。 只支持:默认、非空、主键、唯一 */ USE students; DROP TABLE stuinfo; CREATE TABLE stuinfo( id INT PRIMARY KEY,#主键 stuName VARCHAR(20) NOT NULL UNIQUE,#非空 gender CHAR(1) CHECK(gender='男' OR gender ='女'),#检查 seat INT UNIQUE,#唯一 age INT DEFAULT 18,#默认约束 majorId INT REFERENCES major(id)#外键 ); CREATE TABLE major( id INT PRIMARY KEY, majorName VARCHAR(20) ); #查看stuinfo中的所有索引,包括主键、外键、唯一 SHOW INDEX FROM stuinfo; #2.添加表级约束 /* 语法:在各个字段的最下面 【constraint 约束名】 约束类型(字段名) */ DROP TABLE IF EXISTS stuinfo; CREATE TABLE stuinfo( id INT, stuname VARCHAR(20), gender CHAR(1), seat INT, age INT, majorid INT, CONSTRAINT pk PRIMARY KEY(id),#主键 CONSTRAINT uq UNIQUE(seat),#唯一键 CONSTRAINT ck CHECK(gender ='男' OR gender = '女'),#检查 CONSTRAINT fk_stuinfo_major FOREIGN KEY(majorid) REFERENCES major(id)#外键 ); SHOW INDEX FROM stuinfo; #通用的写法:★ CREATE TABLE IF NOT EXISTS stuinfo( id INT PRIMARY KEY, stuname VARCHAR(20), sex CHAR(1), age INT DEFAULT 18, seat INT UNIQUE, majorid INT, CONSTRAINT fk_stuinfo_major FOREIGN KEY(majorid) REFERENCES major(id) ); #二、修改表时添加约束 /* 1、添加列级约束 alter table 表名 modify column 字段名 字段类型 新约束; 2、添加表级约束 alter table 表名 add 【constraint 约束名】 约束类型(字段名) 【外键的引用】; */ DROP TABLE IF EXISTS stuinfo; CREATE TABLE stuinfo( id INT, stuname VARCHAR(20), gender CHAR(1), seat INT, age INT, majorid INT ) DESC stuinfo; #1.添加非空约束 ALTER TABLE stuinfo MODIFY COLUMN stuname VARCHAR(20) NOT NULL; #2.添加默认约束 ALTER TABLE stuinfo MODIFY COLUMN age INT DEFAULT 18; #3.添加主键 #①列级约束 ALTER TABLE stuinfo MODIFY COLUMN id INT PRIMARY KEY; #②表级约束 ALTER TABLE stuinfo ADD PRIMARY KEY(id); #4.添加唯一 #①列级约束 ALTER TABLE stuinfo MODIFY COLUMN seat INT UNIQUE; #②表级约束 ALTER TABLE stuinfo ADD UNIQUE(seat); #5.添加外键 ALTER TABLE stuinfo ADD CONSTRAINT fk_stuinfo_major FOREIGN KEY(majorid) REFERENCES major(id); #三、修改表时删除约束 #1.删除非空约束 ALTER TABLE stuinfo MODIFY COLUMN stuname VARCHAR(20) NULL; #2.删除默认约束 ALTER TABLE stuinfo MODIFY COLUMN age INT ; #3.删除主键 ALTER TABLE stuinfo DROP PRIMARY KEY; #4.删除唯一 ALTER TABLE stuinfo DROP INDEX seat; #5.删除外键 ALTER TABLE stuinfo DROP FOREIGN KEY fk_stuinfo_major; SHOW INDEX FROM stuinfo;
事务
一个或一组sql语句组成一个执行单元,这个执行单元要么全部执行,要么全部不执行。
事务的特性:
ACID
原子性:一个事务不可再分割,要么都执行要么都不执行
一致性:一个事务执行会使数据从一个一致状态切换到另外一个一致状态
隔离性:一个事务的执行不受其他事务的干扰
持久性:一个事务一旦提交,则会永久的改变数据库的数据.
事务的创建:
隐式事务:事务没有明显的开启和结束的标记
比如insert、update、delete语句
delete from 表 where id =1;
显式事务:事务具有明显的开启和结束的标记
前提:必须先设置自动提交功能为禁用
set autocommit=0;
步骤1:开启事务
set autocommit=0;
start transaction;可选的
步骤2:编写事务中的sql语句(select insert update delete)
语句1;
语句2;
...
步骤3:结束事务
commit;提交事务
rollback;回滚事务
savepoint 节点名;设置保存点
事务的隔离级别:
脏读 不可重复读 幻读
read uncommitted:√ √ √
read committed: × √ √
repeatable read: × × √
serializable × ×
mysql中默认 第三个隔离级别 repeatable read
oracle中默认第二个隔离级别 read committed
查看隔离级别
select @@tx_isolation;
设置隔离级别
set session|global transaction isolation level 隔离级别;
SHOW VARIABLES LIKE 'autocommit'; SHOW ENGINES; #1.演示事务的使用步骤 #开启事务 SET autocommit=0; START TRANSACTION; #编写一组事务的语句 UPDATE account SET balance = 1000 WHERE username='张无忌'; UPDATE account SET balance = 1000 WHERE username='赵敏'; #结束事务 ROLLBACK; #commit; SELECT * FROM account; #2.演示事务对于delete和truncate的处理的区别 SET autocommit=0; START TRANSACTION; DELETE FROM account; ROLLBACK; #3.演示savepoint 的使用 SET autocommit=0; START TRANSACTION; DELETE FROM account WHERE id=25; SAVEPOINT a;#设置保存点 DELETE FROM account WHERE id=28; ROLLBACK TO a;#回滚到保存点 SELECT * FROM account;
MySQL视图
视图是一个虚拟表(非真实存在的,可以理解为每次使用这张表都要执行一下这个sql语句,所以原始表只要改变那么这张虚拟的表一定也变化了),其本质是【根据SQL语句获取动态的数据集,并为其命名】,用户使用时只需使用【名称】即可获取结果集,可以将该结果集当做表来使用。
使用视图我们可以把查询过程中的临时表摘出来,用视图去实现,这样以后再想操作该临时表的数据时就无需重写复杂的sql了,直接去视图中查找即可,但视图有明显地效率问题,并且视图是存放在数据库中的,如果我们程序中使用的sql过分依赖数据库中的视图,即强耦合,那就意味着扩展sql极为不便,因此并不推荐使用。
创建语法的关键字 是否实际占用物理空间 使用
视图 create view 只是保存了sql逻辑 增删改查,只是一般不能增删改
表 create table 保存了数据 增删改查
视图相对于表的优势:
1.简单,使用视图的用户不必关系后面的表,只需要使用过滤好的内容就行了;
2.安全,因为对表的全新不能限制到表的行或者是列,所以可以通过视图来限制用户对表的访问权限;
3.数据独立,确定了视图的结构之后,如果给原来的表增加了列,并不会影响视图,增加行,视图的相对于的行也会增加,如果源表的列名称发生了改变,可以通过修改视图来解决。
#两张有关系的表 mysql> select * from course; +-----+--------+------------+ | cid | cname | teacher_id | +-----+--------+------------+ | 1 | 生物 | 1 | | 2 | 物理 | 2 | | 3 | 体育 | 3 | | 4 | 美术 | 2 | +-----+--------+------------+ rows in set (0.00 sec) mysql> select * from teacher; +-----+-----------------+ | tid | tname | +-----+-----------------+ | 1 | 张磊老师 | | 2 | 李平老师 | | 3 | 刘海燕老师 | | 4 | 朱云海老师 | | 5 | 李杰老师 | +-----+-----------------+ rows in set (0.00 sec) #查询李平老师教授的课程名 mysql> select cname from course where teacher_id = (select tid from teacher where tname='李平老师'); +--------+ | cname | +--------+ | 物理 | | 美术 | +--------+ rows in set (0.00 sec) #子查询出临时表,作为teacher_id等判断依据 select tid from teacher where tname='李平老师'
(select tid from teacher_view)这个查询如果多次出现那么可以使用视图来代替,而不需要每次都写临时表。
#语法:CREATE VIEW 视图名称 AS SQL语句 create view teacher_view as select tid from teacher where tname='李平老师'; #于是查询李平老师教授的课程名的sql可以改写为 mysql> select cname from course where teacher_id = (select tid from teacher_view); +--------+ | cname | +--------+ | 物理 | | 美术 | +--------+ rows in set (0.00 sec)
修改查询的表,视图的临时表也会改变。
语法:ALTER VIEW 视图名称 AS SQL语句 mysql> alter view teacher_view as select * from course where cid>3; Query OK, 0 rows affected (0.04 sec) mysql> select * from teacher_view; +-----+-------+------------+ | cid | cname | teacher_id | +-----+-------+------------+ | 4 | xxx | 2 | | 5 | yyy | 2 | +-----+-------+------------+ rows in set (0.00 sec)
语法:DROP VIEW 视图名称 DROP VIEW teacher_view
#案例:查询姓张的学生名和专业名 SELECT stuname,majorname FROM stuinfo s INNER JOIN major m ON s.`majorid`= m.`id` WHERE s.`stuname` LIKE '张%'; CREATE VIEW v1 AS SELECT stuname,majorname FROM stuinfo s INNER JOIN major m ON s.`majorid`= m.`id`; SELECT * FROM v1 WHERE stuname LIKE '张%'; #一、创建视图 /* 语法: create view 视图名 as 查询语句; */ USE myemployees; #1.查询姓名中包含a字符的员工名、部门名和工种信息 #①创建 CREATE VIEW myv1 AS SELECT last_name,department_name,job_title FROM employees e JOIN departments d ON e.department_id = d.department_id JOIN jobs j ON j.job_id = e.job_id; #②使用 SELECT * FROM myv1 WHERE last_name LIKE '%a%'; #2.查询各部门的平均工资级别 #①创建视图查看每个部门的平均工资 CREATE VIEW myv2 AS SELECT AVG(salary) ag,department_id FROM employees GROUP BY department_id; #②使用 SELECT myv2.`ag`,g.grade_level FROM myv2 JOIN job_grades g ON myv2.`ag` BETWEEN g.`lowest_sal` AND g.`highest_sal`; #3.查询平均工资最低的部门信息 SELECT * FROM myv2 ORDER BY ag LIMIT 1; #4.查询平均工资最低的部门名和工资 CREATE VIEW myv3 AS SELECT * FROM myv2 ORDER BY ag LIMIT 1; SELECT d.*,m.ag FROM myv3 m JOIN departments d ON m.`department_id`=d.`department_id`; #二、视图的修改 #方式一: /* create or replace view 视图名 as 查询语句; */ SELECT * FROM myv3 CREATE OR REPLACE VIEW myv3 AS SELECT AVG(salary),job_id FROM employees GROUP BY job_id; #方式二: /* 语法: alter view 视图名 as 查询语句; */ ALTER VIEW myv3 AS SELECT * FROM employees; #三、删除视图 /* 语法:drop view 视图名,视图名,...; */ DROP VIEW emp_v1,emp_v2,myv3; #四、查看视图 DESC myv3; SHOW CREATE VIEW myv3; #五、视图的更新 CREATE OR REPLACE VIEW myv1 AS SELECT last_name,email,salary*12*(1+IFNULL(commission_pct,0)) "annual salary" FROM employees; CREATE OR REPLACE VIEW myv1 AS SELECT last_name,email FROM employees; SELECT * FROM myv1; SELECT * FROM employees; #1.插入 INSERT INTO myv1 VALUES('张飞','zf@qq.com'); #2.修改 UPDATE myv1 SET last_name = '张无忌' WHERE last_name='张飞'; #3.删除 DELETE FROM myv1 WHERE last_name = '张无忌'; #具备以下特点的视图不允许更新 #①包含以下关键字的sql语句:分组函数、distinct、group by、having、union或者union all CREATE OR REPLACE VIEW myv1 AS SELECT MAX(salary) m,department_id FROM employees GROUP BY department_id; SELECT * FROM myv1; #更新 UPDATE myv1 SET m=9000 WHERE department_id=10; #②常量视图 CREATE OR REPLACE VIEW myv2 AS SELECT 'john' NAME; SELECT * FROM myv2; #更新 UPDATE myv2 SET NAME='lucy'; #③Select中包含子查询 CREATE OR REPLACE VIEW myv3 AS SELECT department_id,(SELECT MAX(salary) FROM employees) 最高工资 FROM departments; #更新 SELECT * FROM myv3; UPDATE myv3 SET 最高工资=100000; #④join CREATE OR REPLACE VIEW myv4 AS SELECT last_name,department_name FROM employees e JOIN departments d ON e.department_id = d.department_id; #更新 SELECT * FROM myv4; UPDATE myv4 SET last_name = '张飞' WHERE last_name='Whalen'; INSERT INTO myv4 VALUES('陈真','xxxx'); #⑤from一个不能更新的视图 CREATE OR REPLACE VIEW myv5 AS SELECT * FROM myv3; #更新 SELECT * FROM myv5; UPDATE myv5 SET 最高工资=10000 WHERE department_id=60; #⑥where子句的子查询引用了from子句中的表 CREATE OR REPLACE VIEW myv6 AS SELECT last_name,email,salary FROM employees WHERE employee_id IN( SELECT manager_id FROM employees WHERE manager_id IS NOT NULL ); #更新 SELECT * FROM myv6; UPDATE myv6 SET salary=10000 WHERE last_name = 'k_ing';
#一、创建视图emp_v1,要求查询电话号码以‘011’开头的员工姓名和工资、邮箱 CREATE OR REPLACE VIEW emp_v1 AS SELECT last_name,salary,email FROM employees WHERE phone_number LIKE '011%'; #二、创建视图emp_v2,要求查询部门的最高工资高于12000的部门信息 CREATE OR REPLACE VIEW emp_v2 AS SELECT MAX(salary) mx_dep,department_id FROM employees GROUP BY department_id HAVING MAX(salary)>12000; SELECT d.*,m.mx_dep FROM departments d JOIN emp_v2 m ON m.department_id = d.`department_id`;
MySQL触发器
使用触发器可以定制用户对表进行【增、删、改】操作时前后的行为,在满足定义条件时触发,并执行触发器中定义的语句集合。
# 插入前 CREATE TRIGGER tri_before_insert_tb1(触发器名称) BEFORE INSERT(插入之前) ON tb1(对这个表操作时就会执行begin里面的操作) FOR EACH ROW(每次插入都执行) BEGIN ... END # 插入后 CREATE TRIGGER tri_after_insert_tb1 AFTER INSERT ON tb1 FOR EACH ROW BEGIN ... END # 删除前 CREATE TRIGGER tri_before_delete_tb1 BEFORE DELETE ON tb1 FOR EACH ROW BEGIN ... END # 删除后 CREATE TRIGGER tri_after_delete_tb1 AFTER DELETE ON tb1 FOR EACH ROW BEGIN ... END # 更新前 CREATE TRIGGER tri_before_update_tb1 BEFORE UPDATE ON tb1 FOR EACH ROW BEGIN ... END # 更新后 CREATE TRIGGER tri_after_update_tb1 AFTER UPDATE ON tb1 FOR EACH ROW BEGIN ... END
DELIMITER // CREATE TRIGGER user_log AFTER INSERT ON userinfo FOR EACH ROW BEGIN DECLARE s1 VARCHAR(40); DECLARE s2 VARCHAR(20); SET s2 = " is created"; SET s1 = CONCAT(NEW.username,s2); #函数CONCAT可以将字符串连接 INSERT INTO log(log) values(s1); END // DELIMITER ; #在userinfo里插入一个数据就会增加一条日志到log表中
注:NEW表示即将插入的数据行,OLD表示即将删除的数据行,所以可以进行new.xx等操作。
触发器无法由用户直接调用,是根据对表的操作才进行相应的触发。
drop trigger tri_after_insert_cmd;
mysql> show triggers \G; *************************** 1. row *************************** Trigger: user_log Event: INSERT Table: userinfo Statement: BEGIN DECLARE s1 VARCHAR(40); DECLARE s2 VARCHAR(20); SET s2 = " is created"; SET s1 = CONCAT(NEW.username,s2); INSERT INTO log(log) values(s1); END Timing: AFTER Created: 2017-09-22 21:12:46.02 sql_mode: ONLY_FULL_GROUP_BY,STRICT_TRANS_TABLES,NO_ZERO_IN_DATE,NO_ZERO_DATE,ERROR_FOR_DIVISION_BY_ZERO,NO_AUTO_CREATE_USER,NO_ENGINE_SUBSTITUTION Definer: root@localhost character_set_client: utf8 collation_connection: utf8_general_ci Database Collation: utf8_general_ci 1 row in set (0.00 sec) ERROR: No query specified
注:不能再触发器中使用以显示或隐式方式开始或结束事务的语句,如START TRANS-ACTION,COMMIT或ROLLBACK。
MySQL的触发器是按照BEFORE触发器、行操作、AFTER触发器的顺序执行的,其中任何一步发生错误都不会继续执行剩下的操作,如果对事务表进行的操作,如果出现错误,那么将会被回滚,如果是对非事务表进行操作,那么就无法回滚了,数据可能会出错。
触发器使用的并不多,不要编写过于复杂的触发器,也不要增加过多的触发器,这样会对数据的插入、修改或者删除带来比较严重的影响,同时也会带来可移植性差的后果,所以在设计触发器的时候一定要有所考虑。
MySQL函数
MySQL的函数分为内置函数与自定义函数(有返回值)。
CHAR_LENGTH(str) 返回值为字符串str 的长度,长度的单位为字符。一个多字节字符算作一个单字符。 对于一个包含五个二字节字符集, LENGTH()返回值为 10, 而CHAR_LENGTH()的返回值为5。 CONCAT(str1,str2,...) 字符串拼接 如有任何一个参数为NULL ,则返回值为 NULL。 CONCAT_WS(separator,str1,str2,...) 字符串拼接(自定义连接符) CONCAT_WS()不会忽略任何空字符串。 (然而会忽略所有的 NULL)。 CONV(N,from_base,to_base) 进制转换 例如: SELECT CONV('a',16,2); 表示将 a 由16进制转换为2进制字符串表示 FORMAT(X,D) 将数字X 的格式写为'#,###,###.##',以四舍五入的方式保留小数点后 D 位, 并将结果以字符串的形式返回。若 D 为 0, 则返回结果不带有小数点,或不含小数部分。 例如: SELECT FORMAT(12332.1,4); 结果为: '12,332.1000' INSERT(str,pos,len,newstr) 在str的指定位置插入字符串 pos:要替换位置其实位置 len:替换的长度 newstr:新字符串 特别的: 如果pos超过原字符串长度,则返回原字符串 如果len超过原字符串长度,则由新字符串完全替换 INSTR(str,substr) 返回字符串 str 中子字符串的第一个出现位置。 LEFT(str,len) 返回字符串str 从开始的len位置的子序列字符。 LOWER(str) 变小写 UPPER(str) 变大写 LTRIM(str) 返回字符串 str ,其引导空格字符被删除。 RTRIM(str) 返回字符串 str ,结尾空格字符被删去。 SUBSTRING(str,pos,len) 获取字符串子序列 LOCATE(substr,str,pos) 获取子序列索引位置 REPEAT(str,count) 返回一个由重复的字符串str 组成的字符串,字符串str的数目等于count 。 若 count <= 0,则返回一个空字符串。 若str 或 count 为 NULL,则返回 NULL 。 REPLACE(str,from_str,to_str) 返回字符串str 以及所有被字符串to_str替代的字符串from_str 。 REVERSE(str) 返回字符串 str ,顺序和字符顺序相反。 RIGHT(str,len) 从字符串str 开始,返回从后边开始len个字符组成的子序列 SPACE(N) 返回一个由N空格组成的字符串。 SUBSTRING(str,pos) , SUBSTRING(str FROM pos) SUBSTRING(str,pos,len) , SUBSTRING(str FROM pos FOR len) 不带有len 参数的格式从字符串str返回一个子字符串,起始于位置 pos。带有len参数的格式从字符串str返回一个长度同len字符相同的子字符串,起始于位置 pos。 使用 FROM的格式为标准 SQL 语法。也可能对pos使用一个负值。假若这样,则子字符串的位置起始于字符串结尾的pos 字符,而不是字符串的开头位置。在以下格式的函数中可以对pos 使用一个负值。 mysql> SELECT SUBSTRING('Quadratically',5); -> 'ratically' mysql> SELECT SUBSTRING('foobarbar' FROM 4); -> 'barbar' mysql> SELECT SUBSTRING('Quadratically',5,6); -> 'ratica' mysql> SELECT SUBSTRING('Sakila', -3); -> 'ila' mysql> SELECT SUBSTRING('Sakila', -5, 3); -> 'aki' mysql> SELECT SUBSTRING('Sakila' FROM -4 FOR 2); -> 'ki' TRIM([{BOTH | LEADING | TRAILING} [remstr] FROM] str) TRIM(remstr FROM] str) 返回字符串 str , 其中所有remstr 前缀和/或后缀都已被删除。若分类符BOTH、LEADIN或TRAILING中没有一个是给定的,则假设为BOTH 。 remstr 为可选项,在未指定情况下,可删除空格。 mysql> SELECT TRIM(' bar '); -> 'bar' mysql> SELECT TRIM(LEADING 'x' FROM 'xxxbarxxx'); -> 'barxxx' mysql> SELECT TRIM(BOTH 'x' FROM 'xxxbarxxx'); -> 'bar' mysql> SELECT TRIM(TRAILING 'xyz' FROM 'barxxyz'); -> 'barx'
#1 基本使用 mysql> SELECT DATE_FORMAT('2009-10-04 22:23:00', '%W %M %Y'); -> 'Sunday October 2009' mysql> SELECT DATE_FORMAT('2007-10-04 22:23:00', '%H:%i:%s'); -> '22:23:00' mysql> SELECT DATE_FORMAT('1900-10-04 22:23:00', -> '%D %y %a %d %m %b %j'); -> '4th 00 Thu 04 10 Oct 277' mysql> SELECT DATE_FORMAT('1997-10-04 22:23:00', -> '%H %k %I %r %T %S %w'); -> '22 22 10 10:23:00 PM 22:23:00 00 6' mysql> SELECT DATE_FORMAT('1999-01-01', '%X %V'); -> '1998 52' mysql> SELECT DATE_FORMAT('2006-06-00', '%d'); -> '00' #2 准备表和记录 CREATE TABLE blog ( id INT PRIMARY KEY auto_increment, NAME CHAR (32), sub_time datetime ); INSERT INTO blog (NAME, sub_time) VALUES ('第1篇','2015-03-01 11:31:21'), ('第2篇','2015-03-11 16:31:21'), ('第3篇','2016-07-01 10:21:31'), ('第4篇','2016-07-22 09:23:21'), ('第5篇','2016-07-23 10:11:11'), ('第6篇','2016-07-25 11:21:31'), ('第7篇','2017-03-01 15:33:21'), ('第8篇','2017-03-01 17:32:21'), ('第9篇','2017-03-01 18:31:21'); #3. 提取sub_time字段的值,按照格式后的结果即"年月"来分组 SELECT DATE_FORMAT(sub_time,'%Y-%m'),COUNT(1) FROM blog GROUP BY DATE_FORMAT(sub_time,'%Y-%m'); #结果 +-------------------------------+----------+ | DATE_FORMAT(sub_time,'%Y-%m') | COUNT(1) | +-------------------------------+----------+ | 2015-03 | 2 | | 2016-07 | 4 | | 2017-03 | 3 | +-------------------------------+----------+ rows in set (0.00 sec)
--工资表 mysql> select * from salary; +----+---------+ | id | salary | +----+---------+ | 1 | 1000.00 | | 2 | 2000.00 | | 3 | 3000.00 | | 4 | 4000.00 | | 5 | 5000.00 | | 6 | 6000.00 | | 7 | 7000.00 | | 8 | NULL | +----+---------+ 8 rows in set (0.00 sec) --当工资高于5000就显示高工资,低于5000的就显示低工资: mysql> select id,if(salary>5000,'高工资','低工资') as level from salary; +----+-----------+ | id | level | +----+-----------+ | 1 | 低工资 | | 2 | 低工资 | | 3 | 低工资 | | 4 | 低工资 | | 5 | 低工资 | | 6 | 高工资 | | 7 | 高工资 | | 8 | 低工资 | +----+-----------+ 8 rows in set (0.00 sec) --当没有工资的时候,用0代替: mysql> select ifnull(salary,0) from salary; +------------------+ | ifnull(salary,0) | +------------------+ | 1000.00 | | 2000.00 | | 3000.00 | | 4000.00 | | 5000.00 | | 6000.00 | | 7000.00 | | 0.00 | +------------------+ 8 rows in set (0.00 sec) --用case when来实现例子1: mysql> select id,case when salary>5000 then '高工资' else '低工资' end from salary; +----+-------------------------------------------------------------+ | id | case when salary>5000 then '高工资' else '低工资' end | +----+-------------------------------------------------------------+ | 1 | 低工资 | | 2 | 低工资 | | 3 | 低工资 | | 4 | 低工资 | | 5 | 低工资 | | 6 | 高工资 | | 7 | 高工资 | | 8 | 低工资 | +----+-------------------------------------------------------------+ 8 rows in set (0.00 sec) --当工资是1000的为特低工资,工资为2000的为低工资,其余为高工资: mysql> select id,case salary when 1000 then '特低工资' when 2000 then '低工资' else '高工资' end from salary; +----+-------------------------------------------------------------------------------------------+ | id | case salary when 1000 then '特低工资' when 2000 then '低工资' else '高工资' end | +----+-------------------------------------------------------------------------------------------+ | 1 | 特低工资 | | 2 | 低工资 | | 3 | 高工资 | | 4 | 高工资 | | 5 | 高工资 | | 6 | 高工资 | | 7 | 高工资 | | 8 | 高工资 | +----+-------------------------------------------------------------------------------------------+ 8 rows in set (0.00 sec)
mysql> SELECT DATE_FORMAT('2009-10-04 22:23:00', '%W %M %Y'); +------------------------------------------------+ | DATE_FORMAT('2009-10-04 22:23:00', '%W %M %Y') | +------------------------------------------------+ | Sunday October 2009 | +------------------------------------------------+ 1 row in set (0.00 sec) mysql> SELECT DATE_FORMAT(now(),'%H %k %I %r %T %S %w'); +-------------------------------------------+ | DATE_FORMAT(now(),'%H %k %I %r %T %S %w') | +-------------------------------------------+ | 23 23 11 11:49:57 PM 23:49:57 57 5 | +-------------------------------------------+ 1 row in set (0.00 sec)
自定义函数中不可以写sql语句,函数仅仅是完成一项功能。sql语句可以写在存储过程中。
delimiter // create function f1( i1 int, i2 int) returns int BEGIN declare num int; set num = i1 + i2; return(num); END // delimiter ; delimiter // create function f5( i int ) returns int begin declare res int default 0; if i = 10 then set res=100; elseif i = 20 then set res=200; elseif i = 30 then set res=300; else set res=400; end if; return res; end // delimiter ;
drop function func_name;
MySQL存储过程
存储过程是一个SQL语句集合,当主动去调用存储过程时,其中内部的SQL语句会按照逻辑执行。其目的是减少程序员的工作量,使程序员不需要自己来写sql语句。直接调用数据库中写好的存储过程。类似于python中的函数或者方法,提高代码的重用性,简化操作。
-- 创建存储过程 delimiter // create procedure p1() BEGIN select * from t1; END// delimiter ; -- 执行存储过程 call p1() --pymysql cursor.callproc('p1')
存储过程还可以接收三类参数:
in 仅用于传入参数用
out 仅用于返回值用
inout 既可以传入又可以当作返回值
-- 创建存储过程 delimiter \\ create procedure p1( in i1 int, in i2 int, inout i3 int,--i3既可以传入值也可以传出 out r1 int ) BEGIN DECLARE temp1 int; DECLARE temp2 int default 0; set temp1 = 1; set r1 = i1 + i2 + temp1 + temp2; set i3 = i3 + 100; end\\ delimiter ; -- 执行存储过程 set @t1 =4;--创建session级别的变量t1 set @t2 = 0; CALL p1 (1, 2 ,@t1, @t2);--out接收到这个变量然后再存储过程中被修改,从而得到返回值的概念 SELECT @t1,@t2;--查看返回值 pymysql执行存储过程 cursor.callproc('p3',(12,2))--传参 r1 = cursor.fetchall() print(r1) cursor.execute('select @_p3_0,@_p3_1')--传参给in和out r2 = cursor.fetchall() print(r2)
为什么存储过程有结果集还要有返回值呢?因为存储过程的成功与否程序员很多情况是不知道的除了查操作,这个时候就需要返回一个状态结果告知执行是否正常执行。
out n2 int 用于标识存储过程的执行结果 1,2 1为执行正常 2为执行失败
#存储过程 /* 含义:一组预先编译好的SQL语句的集合,理解成批处理语句 1、提高代码的重用性 2、简化操作 3、减少了编译次数并且减少了和数据库服务器的连接次数,提高了效率 */ #一、创建语法 CREATE PROCEDURE 存储过程名(参数列表) BEGIN 存储过程体(一组合法的SQL语句) END #注意: /* 1、参数列表包含三部分 参数模式 参数名 参数类型 举例: in stuname varchar(20) 参数模式: in:该参数可以作为输入,也就是该参数需要调用方传入值 out:该参数可以作为输出,也就是该参数可以作为返回值 inout:该参数既可以作为输入又可以作为输出,也就是该参数既需要传入值,又可以返回值 2、如果存储过程体仅仅只有一句话,begin end可以省略 存储过程体中的每条sql语句的结尾要求必须加分号。 存储过程的结尾可以使用 delimiter 重新设置 语法: delimiter 结束标记 案例: delimiter $ */ #二、调用语法 CALL 存储过程名(实参列表); #--------------------------------案例演示----------------------------------- #1.空参列表 #案例:插入到admin表中五条记录 SELECT * FROM admin; DELIMITER $ CREATE PROCEDURE myp1() BEGIN INSERT INTO admin(username,`password`) VALUES('john1','0000'),('lily','0000'),('rose','0000'),('jack','0000'),('tom','0000'); END $ #调用 CALL myp1()$ #2.创建带in模式参数的存储过程 #案例1:创建存储过程实现 根据女神名,查询对应的男神信息 CREATE PROCEDURE myp2(IN beautyName VARCHAR(20)) BEGIN SELECT bo.* FROM boys bo RIGHT JOIN beauty b ON bo.id = b.boyfriend_id WHERE b.name=beautyName; END $ #调用 CALL myp2('柳岩')$ #案例2 :创建存储过程实现,用户是否登录成功 CREATE PROCEDURE myp4(IN username VARCHAR(20),IN PASSWORD VARCHAR(20)) BEGIN DECLARE result INT DEFAULT 0;#声明并初始化 SELECT COUNT(*) INTO result#赋值 FROM admin WHERE admin.username = username AND admin.password = PASSWORD; SELECT IF(result>0,'成功','失败');#使用 END $ #调用 CALL myp3('张飞','8888')$ #3.创建out 模式参数的存储过程 #案例1:根据输入的女神名,返回对应的男神名 CREATE PROCEDURE myp6(IN beautyName VARCHAR(20),OUT boyName VARCHAR(20)) BEGIN SELECT bo.boyname INTO boyname FROM boys bo RIGHT JOIN beauty b ON b.boyfriend_id = bo.id WHERE b.name=beautyName ; END $ #案例2:根据输入的女神名,返回对应的男神名和魅力值 CREATE PROCEDURE myp7(IN beautyName VARCHAR(20),OUT boyName VARCHAR(20),OUT usercp INT) BEGIN SELECT boys.boyname ,boys.usercp INTO boyname,usercp FROM boys RIGHT JOIN beauty b ON b.boyfriend_id = boys.id WHERE b.name=beautyName ; END $ #调用 CALL myp7('小昭',@name,@cp)$ SELECT @name,@cp$ #4.创建带inout模式参数的存储过程 #案例1:传入a和b两个值,最终a和b都翻倍并返回 CREATE PROCEDURE myp8(INOUT a INT ,INOUT b INT) BEGIN SET a=a*2; SET b=b*2; END $ #调用 SET @m=10$ SET @n=20$ CALL myp8(@m,@n)$ SELECT @m,@n$ #三、删除存储过程 #语法:drop procedure 存储过程名 DROP PROCEDURE p1; DROP PROCEDURE p2,p3;#× #四、查看存储过程的信息 DESC myp2;× SHOW CREATE PROCEDURE myp2;
#一、创建存储过程实现传入用户名和密码,插入到admin表中 CREATE PROCEDURE test_pro1(IN username VARCHAR(20),IN loginPwd VARCHAR(20)) BEGIN INSERT INTO admin(admin.username,PASSWORD) VALUES(username,loginpwd); END $ #二、创建存储过程实现传入女神编号,返回女神名称和女神电话 CREATE PROCEDURE test_pro2(IN id INT,OUT NAME VARCHAR(20),OUT phone VARCHAR(20)) BEGIN SELECT b.name ,b.phone INTO NAME,phone FROM beauty b WHERE b.id = id; END $ #三、创建存储存储过程或函数实现传入两个女神生日,返回大小 CREATE PROCEDURE test_pro3(IN birth1 DATETIME,IN birth2 DATETIME,OUT result INT) BEGIN SELECT DATEDIFF(birth1,birth2) INTO result; END $ #四、创建存储过程或函数实现传入一个日期,格式化成xx年xx月xx日并返回 CREATE PROCEDURE test_pro4(IN mydate DATETIME,OUT strDate VARCHAR(50)) BEGIN SELECT DATE_FORMAT(mydate,'%y年%m月%d日') INTO strDate; END $ CALL test_pro4(NOW(),@str)$ SELECT @str $ #五、创建存储过程或函数实现传入女神名称,返回:女神 and 男神 格式的字符串 如 传入 :小昭 返回: 小昭 AND 张无忌 DROP PROCEDURE test_pro5 $ CREATE PROCEDURE test_pro5(IN beautyName VARCHAR(20),OUT str VARCHAR(50)) BEGIN SELECT CONCAT(beautyName,' and ',IFNULL(boyName,'null')) INTO str FROM boys bo RIGHT JOIN beauty b ON b.boyfriend_id = bo.id WHERE b.name=beautyName; SET str= END $ CALL test_pro5('柳岩',@str)$ SELECT @str $ #六、创建存储过程或函数,根据传入的条目数和起始索引,查询beauty表的记录 DROP PROCEDURE test_pro6$ CREATE PROCEDURE test_pro6(IN startIndex INT,IN size INT) BEGIN SELECT * FROM beauty LIMIT startIndex,size; END $ CALL test_pro6(3,5)$
事务:事务用于将某些操作的多个SQL作为原子性操作,一旦有某一个出现错误,即可回滚到原来的状态,从而保证数据库数据完整性。
delimiter // create procedure p4( out status int ) BEGIN 1. 声明如果出现异常则执行{ set status = 1; rollback;--回滚 } 开始事务 -- 由秦兵账户减去100 -- 方少伟账户加90 -- 张根账户加10 commit; 结束 set status = 2; END // delimiter ; =============================== delimiter \\ create PROCEDURE p5( OUT p_return_code tinyint ) BEGIN DECLARE exit handler for sqlexception BEGIN -- ERROR set p_return_code = 1; rollback; END; START TRANSACTION; DELETE from tb1; insert into tb2(name)values('seven'); COMMIT; -- SUCCESS set p_return_code = 2; END\\ delimiter ; cursor.execute('select @_p5_0') r=cursor.fetchall() print(r)
sql里的循环操作:游标(性能比较低,能不用就不用)
delimiter // create procedure p6() begin declare row_id int; -- 自定义变量1 循环的每一行数据 declare row_num int; -- 自定义变量2 declare done INT DEFAULT FALSE;--检查循环是否还有数据,循环完没有的flag declare temp int;--声明加为和的变量 declare my_cursor CURSOR FOR select id,num from A;--创建游标并指出从哪取数据 declare CONTINUE HANDLER FOR NOT FOUND SET done = TRUE; open my_cursor;--打开游标 xxoo: LOOP--开始循环 fetch my_cursor into row_id,row_num;--从游标中拿一行数据,赋值给id,num if done then leave xxoo;--跳出循环 END IF; set temp = row_id + row_num; insert into B(number) values(temp); end loop xxoo; close my_cursor; end // delimter ;
防sql注入:
delimiter // create procedure p7( in tpl varchar(255),--参数 in arg int ) begin 1. 预检测某个东西 SQL语句合法性 2. SQL =格式化 tpl + arg 3. 执行SQL语句 set @xo = arg; PREPARE xxx FROM 'select * from student where sid > ?'; EXECUTE xxx USING @xo;--这只能是sesion的变量 DEALLOCATE prepare prod; end // delimter ; call p7("select * from tb where id > ?",9)--?是占位符,就是后面的9 --sql语句传给tpl,9传给arg ======================= delimiter \\ CREATE PROCEDURE p8 ( in nid int ) BEGIN set @nid = nid; PREPARE prod FROM 'select * from student where sid > ?'; EXECUTE prod USING @nid; DEALLOCATE prepare prod; END\\ delimiter ;
MySQL函数
#函数 /* 含义:一组预先编译好的SQL语句的集合,理解成批处理语句 1、提高代码的重用性 2、简化操作 3、减少了编译次数并且减少了和数据库服务器的连接次数,提高了效率 区别: 存储过程:可以有0个返回,也可以有多个返回,适合做批量插入、批量更新 函数:有且仅有1 个返回,适合做处理数据后返回一个结果 */ #一、创建语法 CREATE FUNCTION 函数名(参数列表) RETURNS 返回类型 BEGIN 函数体 END /* 注意: 1.参数列表 包含两部分: 参数名 参数类型 2.函数体:肯定会有return语句,如果没有会报错 如果return语句没有放在函数体的最后也不报错,但不建议 return 值; 3.函数体中仅有一句话,则可以省略begin end 4.使用 delimiter语句设置结束标记 */ #二、调用语法 SELECT 函数名(参数列表) #------------------------------案例演示---------------------------- #1.无参有返回 #案例:返回公司的员工个数 CREATE FUNCTION myf1() RETURNS INT BEGIN DECLARE c INT DEFAULT 0;#定义局部变量 SELECT COUNT(*) INTO c#赋值 FROM employees; RETURN c; END $ SELECT myf1()$ #2.有参有返回 #案例1:根据员工名,返回它的工资 CREATE FUNCTION myf2(empName VARCHAR(20)) RETURNS DOUBLE BEGIN SET @sal=0;#定义用户变量 SELECT salary INTO @sal #赋值 FROM employees WHERE last_name = empName; RETURN @sal; END $ SELECT myf2('k_ing') $ #案例2:根据部门名,返回该部门的平均工资 CREATE FUNCTION myf3(deptName VARCHAR(20)) RETURNS DOUBLE BEGIN DECLARE sal DOUBLE ; SELECT AVG(salary) INTO sal FROM employees e JOIN departments d ON e.department_id = d.department_id WHERE d.department_name=deptName; RETURN sal; END $ SELECT myf3('IT')$ #三、查看函数 SHOW CREATE FUNCTION myf3; #四、删除函数 DROP FUNCTION myf3; #案例 #一、创建函数,实现传入两个float,返回二者之和 CREATE FUNCTION test_fun1(num1 FLOAT,num2 FLOAT) RETURNS FLOAT BEGIN DECLARE SUM FLOAT DEFAULT 0; SET SUM=num1+num2; RETURN SUM; END $ SELECT test_fun1(1,2)$
流程控制
--求两个数的最大值 DELIMITER // CREATE PROCEDURE comp(IN n1 INT,IN n2 INT) BEGIN SET @res=0; IF n1 > n2 THEN SET @res=n1; ELSEIF n1 = n2 THEN SET @res=n1; ELSE SET @res=n2; END IF; END // DELIMITER ;
DELIMITER // CREATE PROCEDURE comp1(IN n1 INT,IN n2 INT) BEGIN SET @res=0; CASE WHEN n1>n2 THEN SET @res=n1; WHEN n1=n2 THEN SET @res=n1; ELSE SET @res=n2; END CASE; END // DELIMITER ;
ITERATE语句
必须在循环中使用,作用是跳过当前循环的剩下的语句,直接进入下一轮循环,相当于一些高级语言中的continue。
delimiter // CREATE PROCEDURE inserinfo() BEGIN set @x=0; ins: LOOP SET @x=@x+1; IF @x=10 THEN LEAVE ins; ELSEIF mod(@x,2)=0 THEN ITERATE ins; END IF; INSERT INTO info(id,name) VALUES(@x,'test'); END LOOP ins; END // delimiter ;
REPEAT语句
有条件的循环控制语句,当满足条件的时候退出循环。
delimiter // CREATE PROCEDURE inserinfo2() BEGIN DECLARE x INT DEFAULT 9; ins: REPEAT SET x=x+1; INSERT INTO info(id,name) VALUES(x,'test'); UNTIL x>18 END REPEAT; END // delimiter ;
WHILE语句
delimiter // CREATE PROCEDURE inserinfo2() BEGIN DECLARE x INT DEFAULT 9; ins: WHILE X<=18 DO SET x=x+1; INSERT INTO info(id,name) VALUES(x,'test'); END WHILE; END // delimiter ;
#变量 /* 系统变量: 全局变量 会话变量 自定义变量: 用户变量 局部变量 */ #一、系统变量 /* 说明:变量由系统定义,不是用户定义,属于服务器层面 注意:全局变量需要添加global关键字,会话变量需要添加session关键字,如果不写,默认会话级别 使用步骤: 1、查看所有系统变量 show global|【session】variables; 2、查看满足条件的部分系统变量 show global|【session】 variables like '%char%'; 3、查看指定的系统变量的值 select @@global|【session】系统变量名; 4、为某个系统变量赋值 方式一: set global|【session】系统变量名=值; 方式二: set @@global|【session】系统变量名=值; */ #1》全局变量 /* 作用域:针对于所有会话(连接)有效,但不能跨重启 */ #①查看所有全局变量 SHOW GLOBAL VARIABLES; #②查看满足条件的部分系统变量 SHOW GLOBAL VARIABLES LIKE '%char%'; #③查看指定的系统变量的值 SELECT @@global.autocommit; #④为某个系统变量赋值 SET @@global.autocommit=0; SET GLOBAL autocommit=0; #2》会话变量 /* 作用域:针对于当前会话(连接)有效 */ #①查看所有会话变量 SHOW SESSION VARIABLES; #②查看满足条件的部分会话变量 SHOW SESSION VARIABLES LIKE '%char%'; #③查看指定的会话变量的值 SELECT @@autocommit; SELECT @@session.tx_isolation; #④为某个会话变量赋值 SET @@session.tx_isolation='read-uncommitted'; SET SESSION tx_isolation='read-committed'; #二、自定义变量 /* 说明:变量由用户自定义,而不是系统提供的 使用步骤: 1、声明 2、赋值 3、使用(查看、比较、运算等) */ #1》用户变量 /* 作用域:针对于当前会话(连接)有效,作用域同于会话变量 */ #赋值操作符:=或:= #①声明并初始化 SET @变量名=值; SET @变量名:=值; SELECT @变量名:=值; #②赋值(更新变量的值) #方式一: SET @变量名=值; SET @变量名:=值; SELECT @变量名:=值; #方式二: SELECT 字段 INTO @变量名 FROM 表; #③使用(查看变量的值) SELECT @变量名; #2》局部变量 /* 作用域:仅仅在定义它的begin end块中有效 应用在 begin end中的第一句话 */ #①声明 DECLARE 变量名 类型; DECLARE 变量名 类型 【DEFAULT 值】; #②赋值(更新变量的值) #方式一: SET 局部变量名=值; SET 局部变量名:=值; SELECT 局部变量名:=值; #方式二: SELECT 字段 INTO 具备变量名 FROM 表; #③使用(查看变量的值) SELECT 局部变量名; #案例:声明两个变量,求和并打印 #用户变量 SET @m=1; SET @n=1; SET @sum=@m+@n; SELECT @sum; #局部变量 DECLARE m INT DEFAULT 1; DECLARE n INT DEFAULT 1; DECLARE SUM INT; SET SUM=m+n; SELECT SUM; #用户变量和局部变量的对比 作用域 定义位置 语法 用户变量 当前会话 会话的任何地方 加@符号,不用指定类型 局部变量 定义它的BEGIN END中 BEGIN END的第一句话 一般不用加@,需要指定类型
#流程控制结构 /* 顺序、分支、循环 */ #一、分支结构 #1.if函数 /* 语法:if(条件,值1,值2) 功能:实现双分支 应用在begin end中或外面 */ #2.case结构 /* 语法: 情况1:类似于switch case 变量或表达式 when 值1 then 语句1; when 值2 then 语句2; ... else 语句n; end 情况2: case when 条件1 then 语句1; when 条件2 then 语句2; ... else 语句n; end 应用在begin end 中或外面 */ #3.if结构 /* 语法: if 条件1 then 语句1; elseif 条件2 then 语句2; .... else 语句n; end if; 功能:类似于多重if 只能应用在begin end 中 */ #案例1:创建函数,实现传入成绩,如果成绩>90,返回A,如果成绩>80,返回B,如果成绩>60,返回C,否则返回D CREATE FUNCTION test_if(score FLOAT) RETURNS CHAR BEGIN DECLARE ch CHAR DEFAULT 'A'; IF score>90 THEN SET ch='A'; ELSEIF score>80 THEN SET ch='B'; ELSEIF score>60 THEN SET ch='C'; ELSE SET ch='D'; END IF; RETURN ch; END $ SELECT test_if(87)$ #案例2:创建存储过程,如果工资<2000,则删除,如果5000>工资>2000,则涨工资1000,否则涨工资500 CREATE PROCEDURE test_if_pro(IN sal DOUBLE) BEGIN IF sal<2000 THEN DELETE FROM employees WHERE employees.salary=sal; ELSEIF sal>=2000 AND sal<5000 THEN UPDATE employees SET salary=salary+1000 WHERE employees.`salary`=sal; ELSE UPDATE employees SET salary=salary+500 WHERE employees.`salary`=sal; END IF; END $ CALL test_if_pro(2100)$ #案例1:创建函数,实现传入成绩,如果成绩>90,返回A,如果成绩>80,返回B,如果成绩>60,返回C,否则返回D CREATE FUNCTION test_case(score FLOAT) RETURNS CHAR BEGIN DECLARE ch CHAR DEFAULT 'A'; CASE WHEN score>90 THEN SET ch='A'; WHEN score>80 THEN SET ch='B'; WHEN score>60 THEN SET ch='C'; ELSE SET ch='D'; END CASE; RETURN ch; END $ SELECT test_case(56)$ #二、循环结构 /* 分类: while、loop、repeat 循环控制: iterate类似于 continue,继续,结束本次循环,继续下一次 leave 类似于 break,跳出,结束当前所在的循环 */ #1.while /* 语法: 【标签:】while 循环条件 do 循环体; end while【 标签】; 联想: while(循环条件){ 循环体; } */ #2.loop /* 语法: 【标签:】loop 循环体; end loop 【标签】; 可以用来模拟简单的死循环 */ #3.repeat /* 语法: 【标签:】repeat 循环体; until 结束循环的条件 end repeat 【标签】; */ #1.没有添加循环控制语句 #案例:批量插入,根据次数插入到admin表中多条记录 DROP PROCEDURE pro_while1$ CREATE PROCEDURE pro_while1(IN insertCount INT) BEGIN DECLARE i INT DEFAULT 1; WHILE i<=insertCount DO INSERT INTO admin(username,`password`) VALUES(CONCAT('Rose',i),'666'); SET i=i+1; END WHILE; END $ CALL pro_while1(100)$ /* int i=1; while(i<=insertcount){ //插入 i++; } */ #2.添加leave语句 #案例:批量插入,根据次数插入到admin表中多条记录,如果次数>20则停止 TRUNCATE TABLE admin$ DROP PROCEDURE test_while1$ CREATE PROCEDURE test_while1(IN insertCount INT) BEGIN DECLARE i INT DEFAULT 1; a:WHILE i<=insertCount DO INSERT INTO admin(username,`password`) VALUES(CONCAT('xiaohua',i),'0000'); IF i>=20 THEN LEAVE a; END IF; SET i=i+1; END WHILE a; END $ CALL test_while1(100)$ #3.添加iterate语句 #案例:批量插入,根据次数插入到admin表中多条记录,只插入偶数次 TRUNCATE TABLE admin$ DROP PROCEDURE test_while1$ CREATE PROCEDURE test_while1(IN insertCount INT) BEGIN DECLARE i INT DEFAULT 0; a:WHILE i<=insertCount DO SET i=i+1; IF MOD(i,2)!=0 THEN ITERATE a; END IF; INSERT INTO admin(username,`password`) VALUES(CONCAT('xiaohua',i),'0000'); END WHILE a; END $ CALL test_while1(100)$ /* int i=0; while(i<=insertCount){ i++; if(i%2==0){ continue; } 插入 } */ 案例: /*一、已知表stringcontent 其中字段: id 自增长 content varchar(20) 向该表插入指定个数的,随机的字符串 */ DROP TABLE IF EXISTS stringcontent; CREATE TABLE stringcontent( id INT PRIMARY KEY AUTO_INCREMENT, content VARCHAR(20) ); DELIMITER $ CREATE PROCEDURE test_randstr_insert(IN insertCount INT) BEGIN DECLARE i INT DEFAULT 1; DECLARE str VARCHAR(26) DEFAULT 'abcdefghijklmnopqrstuvwxyz'; DECLARE startIndex INT;#代表初始索引 DECLARE len INT;#代表截取的字符长度 WHILE i<=insertcount DO SET startIndex=FLOOR(RAND()*26+1);#代表初始索引,随机范围1-26 SET len=FLOOR(RAND()*(20-startIndex+1)+1);#代表截取长度,随机范围1-(20-startIndex+1) INSERT INTO stringcontent(content) VALUES(SUBSTR(str,startIndex,len)); SET i=i+1; END WHILE; END $ CALL test_randstr_insert(10)$
在pymysql中调用存储过程
pymysql中使用callproc()方法可以实现存储过程的调用。
delimiter // CREATE PROCEDURE maxone(IN x INT,IN y INT) BEGIN SET @k=0; IF x>y THEN SET @k=x; ELSE SET @k=y; END IF; END // delimiter ; =================== import pymysql config={ "host":"127.0.0.1", "user":"root", "password":"LBLB1212@@", "database":"db2", "charset":"utf8" } db = pymysql.connect(**config) with db.cursor(cursor=pymysql.cursors.DictCursor) as cursor: cursor.callproc('maxone',(18,10)) #调用存储过程 cursor.execute('select @k') res = cursor.fetchall() print(res) cursor.close() db.close() #运行结果 [{'@k': 18}]
mysql批量数据库脚本
1.建表
create table dept( id int unsigned primary key auto_increment, deptno mediumint unsigned not null default 0, dname varchar(20) not null default "", loc varchar(13) not null default "" )engine=innodb; create table emp( id int unsigned primary key auto_increment, empno mediumint unsigned not null default 0,/*编号*/ ename varchar(20) not null default "",/*姓名*/ job varchar(9) not null default "",/*工作*/ mgr mediumint unsigned not null default 0,/*上级编号*/ hiredate date not null,/*入职时间*/ sal decimal(7,2) not null, /*薪水*/ comm decimal(7,2) not null,/*红利*/ deptno mediumint unsigned not null default 0/*部门编号*/ )engine=innodb;
2.设置参数
show variables like 'log_bin_trust_function_creators'; set global log_bin_trust_function_creators=1;
3.创建函数
a.随机产生字符串 delimiter $$ create function rand_string(n int) returns varchar(255) begin declare chars_str varchar(100) default 'qwertyuiopasdfghjklzxcvbnm'; declare return_str varchar(255) default ''; declare i int default 0; while i<n do set return_str=concat(return_str,substring(chars_str,floor(1+rand()*52),1)); set i=i+1; end while; return return_str; end $$ b.随机产生部门编号 delimiter $$ create function rand_num() returns int(5) begin declare i int default 0; set i=floor(100+rand()*10); return i; end $$
4.创建存储过程
a.创建往emp表中插入数据的存储过程 delimiter $$ create procedure insert_emp(in start int(10),in max_num int(10)) begin declare i int default 0; /*把autocommit设置成0*/ set autocommit= 0; repeat set i=i+1; insert into emp(empno,ename,job,mgr,hiredate,sal,comm,deptno) values((start+i),rand_string(6),'SALESMAN',0001,curdate(),2000,400,rand_num()); until i=max_num end repeat; commit; end $$ b.创建往dept表中插入数据的存储过程 delimiter $$ create procedure insert_dept(in start int(10),in max_num int(10)) begin declare i int default 0; set autocommit=0; repeat set i=i+1; insert into dept(deptno,dname,loc) values((start+i),rand_string(10),rand_string(8)); until i=max_num end repeat; commit; end $$
5.调用存储过程
call insert_dept(100,10);//从deptno为100起插入10条随机生成数据 call insert_emp(100001,5000000);//从empno为100001起插入5000000条随机生成数据