1、添加适当索引(index) [四种: 普通索引、主键索引、唯一索引unique、全文索引]
2、SQL语句优化
3、分表技术(水平分割、垂直分割)
4、读写[写: update/delete/add]分离
5、存储过程 [模块化编程,可以提高速度]
6、对mysql配置优化 [配置最大并发数my.ini, 调整缓存大小 ]
7、mysql服务器硬件升级
8、定时的去清除不需要的数据,定时进行碎片整理(MyISAM)
表的设计合理化(符合3NF)
1、第一范式:原子性约束,不可再分解(字段内容,如地址,可拆分成省/市/区)
-------1NF是对属性的原子性约束,要求属性(列)具有原子性,不可再分解;(只要是关系型数据库都满足1NF)
2、第二范式:惟一性约束 (id主键)
-------2NF是对记录的惟一性约束,表中的记录是唯一的, 就满足2NF, 通常我们设计一个主键来实现,主键不能包含业务逻辑。
3、第三范式:子段冗余性的约束 (重复数据拆分成多个表保存)
-------3NF是对字段冗余性的约束,它要求字段没有冗余。 没有冗余的数据库设计可以做到。但是,没有冗余的数据库未必是最好的数据库,有时为了提高运行效率,就必须降低范式标准,适当保留冗余数据。具体做法是: 在概念数据模型设计时遵守第三范式,降低范式标准的工作放到物理数据模型设计时考虑。降低范式就是增加字段,允许冗余
慢查询的作用?
------- 查询慢的语句会已日志的方式记录下来,通过日志记录查看哪些sql 查询慢,在调优查询慢的语句
什么是慢查询?
------ MySQL默认10秒内没有响应SQL结果,则为慢查询
------ 可以去修改MySQL慢查询默认时间
--查询慢查询时间
show variables like 'long_query_time';
--修改慢查询时间
set long_query_time=1; ---但是重启mysql之后,long_query_time依然是my.ini中的值
在默认情况下,我们的mysql不会记录慢查询,需要在启动mysql时候,指定记录慢查询才可以,在 my.ini 中指定,
本文表sql+数据,https://download.csdn.net/download/qq_41463655/10991818
语法:Select * |列名 from 表名
Select * from ws_user; #查询所有
Select username from ws_user; #只查询username列
Select username,password from ws_user; #只查询username,password列
语法:Select 列名 别名… from 表名
Select username '昵称',password '密码' from ws_user; #使用别名
语法:Select distinct *|列名 from 表名
Select distinct password from ws_user; #去除重复数据,所有列数据相同才去除
如下,需加表的的别名 或 表.字段名称
`select m.rank*12 from ws_menu m;` # 使用四则运算 + - * /
`select * from ws_menu m where m.rank > 100;` # 只查询 rank字段大于100的
select * from ws_menu m where m.rank is not null; # 查询 rank 不为空的
select * from ws_menu m where m.rank is null; # 查询 rank 为空的
# 查询 rank 不为空 且 > 500 的 # and
select * from ws_menu m where m.rank is not null and m.rank > 500;
# 查询 rank > 500 且小于 1500 的
select * from ws_menu m where m.rank >500 and m.rank < 1500;
select * from ws_menu m where m.rank >= 500 and m.rank <= 1500;
# 查询 rank 不为空 或者 rank > 500 的 (满足其中一个 or)
select * from ws_menu m where m.rank is not null or m.rank > 500;
# 查询 rank > 500 或者小于 1500 的
select * from ws_menu m where m.rank >500 or m.rank < 1500;
时间区间查询 between 升序排序:order by o.start_time asc
select * from ws_order o where o.start_time between '2017-05-06' and '2019-06-6' order by o.start_time asc;
时间比较查询 > < 降序排序:order by o.start_time desc
select * from ws_order o where o.start_time > '2017-05-06' and o.start_time <'2019-06-6' order by o.start_time desc;
select * from ws_menu where menu_id = 1; # 查询id=1的;
select * from ws_menu where text = '用户管理'; # 使用字符串查询
select * from ws_menu where menu_id = 1 or menu_id = 2; # 同时查询id=1 和 2 的;
select * from ws_menu where menu_id in(1,2); # 同时查询id=1 和 2 的;
select * from ws_menu where text in('用户管理','部门管理');
select * from ws_menu where menu_id not in(1); # 查询除id不等于1的;
select * from ws_menu where menu_id not in(1,2); # 查询除id不等于1 和 2 的;
select * from ws_menu where menu_id != 1; # 查询除id不等于1的;
select * from ws_menu where menu_id <> 1; # 查询除id不等于1的;
select * from ws_menu m where m.text like '_户管理'; # 匹配一个任意字段内容
select * from ws_menu m where m.text like '__管理'; # 匹配两个任意字段
select * from ws_menu m where m.text like '%理'; # 匹配无数个任意字段
select * from ws_menu m where m.text like '%管%'; # 中间带管字的全部查询处理
select * from ws_menu m order by m.rank asc; # 降序,新数据在后
select * from ws_menu m order by m.rank desc; # 升序,新数据在前
# 可以指定多个排序字段,优先指定前一个,前一个字段相同或空根据第二个字段排序
select * from ws_menu m order by m.rank asc,m.menu_id asc;
select * from ws_menu m order by m.rank asc,m.menu_id desc;
select * from ws_order o where o.start_time >=(NOW() - interval 24 hour)
select * from ws_order o where to_days(o.start_time) = to_days(now());
SELECT * FROM ws_order o WHERE TO_DAYS( NOW( ) ) - TO_DAYS(o.start_time) <= 1;
SELECT * FROM ws_order o where DATE_SUB(CURDATE(), INTERVAL 7 DAY) <= date(o.start_time);
SELECT * FROM ws_order o where DATE_SUB(CURDATE(), INTERVAL 30 DAY) <= date(o.start_time);
SELECT * FROM ws_order o WHERE DATE_FORMAT(o.start_time, '%Y%m' ) = DATE_FORMAT( CURDATE( ) , '%Y%m' );
SELECT * FROM ws_order o WHERE PERIOD_DIFF( date_format( now( ) , '%Y%m' ) , date_format(o.start_time, '%Y%m' ) ) =1;
select * from ws_order o where QUARTER(o.start_time)=QUARTER(now());
select * from ws_order o where QUARTER(o.start_time)=QUARTER(DATE_SUB(now(),interval 1 QUARTER));
select * from ws_order o where YEAR(o.start_time)=YEAR(NOW());
select * from ws_order o where year(o.start_time)=year(date_sub(now(),interval 1 year));
SELECT * FROM ws_order o WHERE YEARWEEK(date_format(o.start_time,'%Y-%m-%d')) = YEARWEEK(now());
SELECT * FROM ws_order o WHERE YEARWEEK(date_format(o.start_time,'%Y-%m-%d')) = YEARWEEK(now())-1;
SELECT * FROM ws_order o WHERE YEARWEEK(date_format(o.start_time,'%Y-%m-%d')) = YEARWEEK(now())-2;
select * from ws_order o where date_format(o.start_time,'%Y-%m')=date_format(now(),'%Y-%m')
select * from ws_order o where o.start_time between date_sub(now(),interval 6 month) and now();
select * from ws_order o where date_format(o.start_time,'%Y-%m')=date_format(DATE_SUB(curdate(), INTERVAL 1 MONTH),'%Y-%m');
语法:SELECT {DISTINCT} *|列名… FROM 表名 别名,表名1 别名
{WHERE 限制条件 ORDER BY 排序字段 ASC|DESC…}
# 双表查询(商品订单+订单详情(笛卡尔积,数据多+乱))
select * from ws_order,ws_order_detail;
#去掉笛卡尔积,只留下有关系的数据
select * from ws_order o,ws_order_detail d where o.order_id = d.order_id;
# 查询订单号,下单时间,价格,数量及总价格
select o.order_no,o.start_time,d.order_price,d.order_num,d.oeder_money from ws_order o,ws_order_detail d where o.order_id = d.order_id;
select * from ws_order o,ws_order_detail d,ws_order_log l
where o.order_id = d.order_id and o.order_id = l.order_id;
# 只看 1429 的数据
select * from ws_order o,ws_order_detail d,ws_order_log l
where o.order_id = d.order_id and o.order_id = l.order_id and o.order_id =1429;
# 只看 1429 的具体数据
select o.order_no,d.oeder_money,l.log_notes,log_time
from ws_order o,ws_order_detail d,ws_order_log l
where o.order_id = d.order_id and o.order_id = l.order_id and o.order_id =1429;
交叉查询 cross join
select * from ws_order cross join ws_order_detail; #产生笛卡尔积
## 交叉查询 using子句 (类似于连接条件查询)
select * from ws_order cross join ws_order_detail using(order_id) where order_id = 1308;
## 交叉查询 on子句 (),相当于 where
select * from ws_order o cross join ws_order_detail d on o.order_id = d.order_id;
## 上一句同下
select * from ws_order o,ws_order_detail d where o.order_id = d.order_id;
select * from ws_order o left join ws_order_detail d on o.order_id = d.order_id;
select * from ws_order o right join ws_order_detail d on o.order_id = d.order_id;
select count(*) from ws_menu; ## 不建议使用*
select count(m.rank) from ws_menu m; ## 随意指定一列无空字段的数据
select min(m.rank) from ws_menu m;
select max(m.rank) from ws_menu m;
select avg(m.rank) from ws_menu m;
select sum(m.rank) from ws_menu m;
#注意:只有分组条件在结果集中是重复的分组才有意义
语法:SELECT * |列名 FROM 表名
{WEHRE 查询条件}
{GROUP BY 分组字段}
ORDER BY 列名1 ASC|DESC,列名2…ASC|DESC
select o.type from ws_order o group by o.type;
select o.type,count(type) from ws_order o group by o.type;
select o.type,count(type) from ws_order o group by o.type having count(type) > 1;
select o.type,max(d.order_price) from ws_order o,ws_order_detail d where o.order_id = d.order_id group by o.type;
select o.type,min(d.order_price) from ws_order o,ws_order_detail d where o.order_id = d.order_id group by o.type;
select o.type,avg(d.order_price) from ws_order o,ws_order_detail d where o.order_id = d.order_id group by o.type;
select o.type,avg(d.order_price) from ws_order o,ws_order_detail d where o.order_id = d.order_id group by o.type
having avg(d.order_price) > 2700;
select o.type,sum(d.oeder_money) from ws_order o,ws_order_detail d where o.order_id = d.order_id group by o.type;
# 子sql 返回 1310 的价格
select * from ws_order_detail o where o.order_price > (
select o1.order_price from ws_order_detail o1 where o1.order_detail_id = 1310
);
# 子sql 一返回 1310 的价格
# 子sql 二返回 1310 的订单数量
select * from ws_order_detail o
where o.order_price > (
select o1.order_price from ws_order_detail o1 where o1.order_detail_id = 1310
)
and o.remain_num > (
select o2.remain_num from ws_order_detail o2 where o2.order_detail_id = 1310
);
# typenum 是统计的数量字段名
# 统计分组数量 大于1 条以上的
select * from ws_order o,(select o1.type,count(type) typenum from ws_order o1 group by o1.type) o1
where o.type = o1.type
and o1.typenum > 1;
# 子sql:查询所有类别下的每个类别最低价的商品集
select * from ws_order_detail d
where d.order_price in (
select min(d1.order_price) minprice from ws_order_detail d1 where d1.order_price group by d1.order_price
);
# 子sql 去重查询所有sql
select * from ws_user u where u.password in(
select distinct password from ws_user u1 where u.password = u1.password
);
# 子sql 去重查询所有sql
# 同上(not exists 假,exists 真)
select * from ws_user u where not exists(
select * from ws_user u1 where u.password = u1.password
);
select * from ws_order_detail o where o.order_price > 2400
union
select * from ws_order_detail o where o.order_price > 2800;
select * from ws_order_detail o where o.order_price > 2400
union all
select * from ws_order_detail o where o.order_price > 2400;
select * from ws_user limit 8,2;
select * from ws_user order by user_id limit 8,2;
select * from ws_user where 1=1 limit 8,2;
# 子sql 开始获取开始的第一条数据的id
# 查询大于该id 的后一百条数据
select * from ws_user u where u.user_id >=(select user_id from ws_user limit 5,1) limit 100;
# 查询id 1到1000范围内的,从1开始后的100条数据
select * from ws_user u where u.user_id between 1 and 1000 limit 100;
# 查询id从1开始后的100条数据
select * from ws_user where user_id >= 5 limit 100;
## 把查询的sql 放中间一套就好了 select * from eb_item
select * from(
select rownum rw,a.* from (
## 查询sql 区
select * from eb_item t
where t.brand_id = 1003
and t.audit_status = 1
and t.show_status = 1
and t.item_name like '%星%'
## 查询sql 区
) a where rownum < 11
) b where b.rw > 1
select * from ws_user
select * from ws_order
## 语法 : INSERT INTO表名[(列名1,列名2,...)]VALUES(值1,值2,...)
## 或 : INSERT INTO 表名VALUES(值1,值2,...)
insert into ws_user(user_id,username,password) values(10,"zhangsan",'123456');
insert into ws_user(user_id,username,password) values(null,"zhangsan",'123456');
insert into ws_order(order_id,start_time) values(999,now());
insert into ws_order(order_id,start_time) values(1001,'1996-3-2 16:00:00');
insert into ws_order(order_id,start_time) values(1001,'1996/3/2 16:00:00');
insert into ws_file(file_id,fileMp3) values(1001,'C:\Users\Administrator\Music\虾米音乐\黄嘉雯-情一动心就痛.mp3');
## 全部修改语法:UPDATE 表名 SET 列名1=值1,列名2=值2,....(且勿随意使用)
## 指定修改语法:UPDATE 表名 SET 列名1=值1,列名2=值2,....WHERE 修改条件;
update ws_user set username = "李四",password="123456" where user_id = 10;
# 语法 : DELETE FROM 表名 WHERE 删除条件;
delete from ws_user where user_id = 10;
事务,我们打开另外一个窗口,发现上面的操作并没有生效
数据库变更会发生锁的情况(此处应用可以解决项目多线程并发带来的数据安全问题)
当两个数据库的连接同时来修改同一数据时,一定会有一连接先修改,另一个连接就会等待直到第一个连接修改完毕再修改
commit;
rollback;
# 语法:ALTER TABLE 表名称 (列名1 类型 [DEFAULT 默认值],列名1 类型 [DEFAULT 默认值]...)
# 如果你不想字段为 NULL 可以设置字段的属性为 NOT NULL, 在操作数据库时如果输入该字段的数据为NULL ,就会报错。
# AUTO_INCREMENT 定义列为自增的属性,一般用于主键,数值会自动加1。
# PRIMARY KEY关键字用于定义列为主键。 您可以使用多列来定义主键,列间以逗号分隔。
# ENGINE 设置存储引擎,CHARSET 设置编码。
CREATE TABLE runoob_tbl(
runoob_id INT NOT NULL AUTO_INCREMENT, # NOT NULL:不为空,AUTO_INCREMENT:自增
runoob_title VARCHAR(100) NOT NULL,
runoob_author VARCHAR(40) NOT NULL,
submission_date DATE,
PRIMARY KEY ( runoob_id ) # PRIMARY KEY :定义主键列
)ENGINE=InnoDB DEFAULT CHARSET=utf8; # ENGINE :设置存储引擎,CHARSET:设置编码。
alter table runoob_tbl rename runoob_tb2; # 修改表名
alter table runoob_tb2 add column name varchar(10); # 添加表列
alter table runoob_tb2 modify name char(20) # 修改表列类型
alter table runoob_tb2 change name username char(40) # 修改表列类型+表列名
alter table runoob_tb2 drop column name; # 删除表列
alter table runoob_tb2 change column username username1 varchar(30) #修改表列名
# 语法:DROP TABLE 表名
DROP TABLE runoob_tb2;
truncate table runoob_tb2;
# 主键约束:PRIMARY KEY ,主键是唯一的,不可重复
# 非空约束:NOT NULL ,表示该字段不能为空
# 唯一约束:UNIQUE,表示该字段字段值不能重复
# 检查约束:CHECK(GENDER IN(1,2)),表示该字段只能填写 1或2
# 外键约束:constraint
CREATE TABLE runoob_tbl(
runoob_id INT NOT NULL AUTO_INCREMENT,
runoob_title VARCHAR(100) NOT NULL UNIQUE,
runoob_author VARCHAR(40) NOT NULL,
runoob_type INT CHECK(GENDER IN(1,2)),
submission_date DATE,
PRIMARY KEY ( runoob_id )
# constraint 外键_pk primary key(order_id)
)ENGINE=InnoDB DEFAULT CHARSET=utf8;
oracle使用 mysql不需要
CREATE SEQUENCE seqpersonid;
select seqpersonid.nextval from dual;##### 3、CurrVal :取得序列的当前内容
select seqpersonid.currval from dual;
oracle自增列使用 seqpersonid.nextval,mysql 输入null 就好了
insert into ws_user(user_id,username,password) values(seqpersonid.nextval,"zhangsan",'123456');
什么情况下所有索引?
1、数据量大,查询慢的表
2、经常查询的字段,where条件中经常使用
优点:
1、索引的作用:通过索引查找记录至少要比顺序扫描记录大致快100倍
2、索引实现原理:生成二叉数索引文件,通过二叉数,B+树,折半查找
3、数据量越大,索引查询提升的效率越高
缺点:
1、占用磁盘空间
2、影响增删改ddl效率
如图:
不使用索引:按左边图顺序查询,效率低
使用索引后:根据查询数据的大小判断,比如查9,小于六的直接不考虑,直接查询大于6的,在往下找,如图查找2次就找到9了,顺序查询则需要查9次
explain 关键字,放在查询前面可以通过type查看查询是否使用了索引
# 创建索引一语法:CREATE index 索引名 on 表名(列名((length)))
CREATE INDEX uname1 ON ws_user(username);
# 创建索引二语法:CREATE index 表名 ADD INDEX 索引名(列名)
ALTER TABLE ws_user ADD INDEX uname(`username`);
# 语法: alter table 表名 add primary key (列名);
alter table articles drop primary key;
1、索引列不能有重复数据,必须唯一约束字段, 唯一约束可以为null
2、CHAR,VARCHAR类型,length可以小于字段的实际长度
3、BLOB,TEXT类型就必须指定长度
# 语法:CREATE UNIQUE INDEX IndexName ON `TableName`(`字段名`(length));
CREATE UNIQUE INDEX nam ON ws_user(`username`(10));
说明:
1.在mysql中fulltext 索引只针对 myisam生效
2.mysql自己提供的fulltext针对英文生效->sphinx (coreseek) 技术处理中文
3.使用方法是 match(字段名…) against(‘关键字’)
4.全文索引:停止词, 因为在一个文本中创建索引是一个无穷大的数,因此,对一些常用词和字符,就不会创建这些词,称为停止词.比如(a,b,mysql,the)
创建全文索引
CREATE TABLE articles (
id INT UNSIGNED AUTO_INCREMENT NOT NULL PRIMARY KEY,
title VARCHAR(200),
body TEXT,
FULLTEXT (title,body) -- 添加全文索引
)engine=myisam charset utf8;
查询每行和database的匹配度
select match(title,body) against ('database') from articles;
查询
# 错误用法:
select * from articles where body like '%mysql%'; 错误用法 索引不会生效
# 正确用法
select * from articles where match(title,body) against ( 'database')
1、不按索引最左列开始查询(多列索引)
2、查询中某个列有范围查询,则其右边的所有列都无法使用查询(多列查询)
3、不能跳过某个字段来进行查询,这样利用不到索引
# 语法: CREATE INDEX 索引名 On `表名`(`字段名`(字段长度),`password`(10),....);
CREATE INDEX up On `ws_user`(`username`(10),`password`(10));
desc 表名; 不能显示索引名称
show index from 表名
show keys from 表名
DROP INDEX up ON `ws_user`;
show status like ‘handler_read%’;
大家可以注意:
handler_read_key:这个值越高越好,越高表示使用索引查询到的次数。
handler_read_rnd_next:这个值越高,说明查询低效。
创建视图: CREATE VIEW 视图名(列1,列2...) AS SELECT (列1,列2...) FROM ...;
使用视图: 当成表使用就好
修改视图: CREATE OR REPLACE VIEW 视图名 AS SELECT [...] FROM [...];
查看数据库已有视图: SHOW TABLES [like...];(可以使用模糊查找)
查看视图详情: DESC 视图名 或 SHOW FIELDS FROM 视图名
视图条件限制: [WITH CHECK OPTION]
创建视图1,vuser
create view vuser as select * from ws_user u;
创建视图2,vuser,视图名存在会覆盖
create or replace view vuser AS select * from ws_user u;
创建视图3,vuser ,视图名存在覆盖,且视图数据不可修改
create or replace view vuser AS select * from ws_user with check option;
select * from vuser;
– 视图插入数据(同sql操作,需有视图数据可改权限)
insert into vuser(user_id,username,password) values(9880,'柴油','35');
– 视图修改数据(同sql操作,需有视图数据可改权限)
update vuser set username = '66666',password = '1232131' where user_id= 9878;
– 查看视图详情
DESC vuser;
或
SHOW FIELDS FROM vuser;
– 查看已有视图(视图 = 临时表)
SHOW TABLES;
创建临时表,断开连接释放 (系统自动删除)
create TEMPORARY table Test(
regName varchar(20),
mobilePhone char(11)
);
查询
select * from Test;
删除临时表
drop table Test;
查询所有表
show tables;
复制的是建表sql,直接在次复制字段内容
show create table ws_user;
复制结构,直接出来一个新的ws_user1 表(无数据)
create table ws_user1 like ws_user;
复制结构和数据(会丢掉一些特性,比如主键自增等)
create table ws_user2 as select * from ws_user;
复制+临时表( 密码等于123456的全部复制到临时表数据中)
create TEMPORARY table leave_user as select * from ws_user u where u.password = '123456';
查询临时表
select * from leave_user;
if,第一个参数为判断表达式,true返回第二个参数,false返回第三个参数
select username,password,if(password='123456','yes','no') from ws_user
#case 判断,满足条件进入, 可以定义多个判断条件,断条件可以and连接 whene password = ‘123456’ and____ then ‘yes’ ,
select username,password,case when password = '123456' then 'yes' else 'no' end from ws_user
案例一
select case 2
when 1 then 'one'
when 2 then 'two'
else 'more'
end;
案例二
select case
when 1 > 2 then 'false'
when 1 < 2 then 'true'
else 'error'
end;
– 获取空字段数据
select * from ws_user where password is null;
– 判空,空返回第二个值,不为空返回第一个参数的值
select password,ifnull(password,"空值") from ws_user where password is null;
说明:储存函数调用为 select + 函数名
此为修改分饰符 ;为 //,防止函数中的sql 出现 ; 结束报错
delimiter ;
# 平均值
-- select avg(leaveAmount) FROM member;
-- select CONCAT('hello','-','lemon')
# 字符串拼接
select CONCAT(username,'-',password) from ws_user;
DROP FUNCTION IF EXISTS f_say_hello;
show variables like 'log_bin_trust_function_creators';
set global log_bin_trust_function_creators=1;
show variables like 'log_bin_trust_function_creators';
delimiter // # 把结束分号改为 //
DROP FUNCTION IF EXISTS f_is_tuhao; # 删除原有函数
CREATE FUNCTION f_is_tuhao(memberId INT) # 创建函数 函数名(传入值 值数据类型)
RETURNS VARCHAR(20) # 返回值类型
BEGIN # 程序体开始
RETURN _msg; # 返回值
END// # 程序体结束
delimiter; # 把结束还原为 ;
调用方法
select 函数名
该函数返回 Hello World!
delimiter //
CREATE FUNCTION f_say_hello() # 函数名
RETURNS VARCHAR(20) # 返回的数据类型
BEGIN
-- 多条sql。。。。 # 函数体,也就是sql
RETURN 'Hello World!'; # return 返回值
END
//
delimiter ;
-- 调用函数
select f_say_hello();
delimiter //
DROP FUNCTION IF EXISTS f_say_a_word; # 删除
CREATE FUNCTION f_say_a_word(word VARCHAR(20))
RETURNS VARCHAR(20)
BEGIN
RETURN word;
END//
delimiter ;
-- 调用函数
select f_say_a_word('Hello World!!!');
delimiter //
DROP FUNCTION IF EXISTS f_add_num;
CREATE FUNCTION f_add_num(a INT,b INT)
RETURNS INT
BEGIN
RETURN a+b;
END
//
delimiter ;
-- 调用函数
select f_add_num(1,2);
-- 得到某个会员的平均的消费额
delimiter //
# 删除存在
DROP FUNCTION IF EXISTS f_get_avg_amount;
# 创建
CREATE FUNCTION f_get_avg_amount(userId INT)
RETURNS DECIMAL(18,2)
BEGIN
-- 定义一个变量:保存该用户的消费总额
DECLARE _sumAmount DECIMAL(18,2) DEFAULT 0.00;
-- 定义一个变量:保存该用户的消费次数
DECLARE _investTimes INT DEFAULT 0;
-- 为变量赋值
-- SET _sumAmount=100.00;
-- 查询该用户的消费总额 (自定义查询sql) INTO _sumAmount 为赋值
SELECT SUM(amount) FROM invest WHERE memberId=userId;
-- 查询该用户的消费次数 (自定义查询sql)
SELECT COUNT(1) INTO _investTimes FROM invest WHERE memberId=userId;
-- IFNULL 为空判断,出现除0为空
RETURN IFNULL(_sumAmount/_investTimes,0.00);
END//
delimiter ;
– 传入一个memberId,返回会员是否是土豪(>40000)、无产(<10000)、中产(>=10000 and <40000)
delimiter //
DROP FUNCTION IF EXISTS f_is_tuhao; # 删除原有函数
CREATE FUNCTION f_is_tuhao(memberId INT) # 创建函数 函数名(传入值 值数据类型)
RETURNS VARCHAR(20) # 返回值类型
BEGIN # 程序体开始
DECLARE _msg VARCHAR(20); # 定义msg保存输出信息
DECLARE _amount DECIMAL(18,2); # 定义一个局部变量保存查询出来的该会员的余额
# 查询余额并赋值给变量 INTO (sql 数据库查询)
SELECT leaveAmount INTO _amount FROM member WHERE id=memberId; _amount
if _amount<10000 THEN # if分支判断变量值的大小
set _msg = '我是无产阶级';
elseif _amount>=10000 AND _amount<40000 THEN
set _msg = '我是中产阶级';
else
set _msg = '我是土豪';
end if; # if分支判断结束标识
RETURN _msg;
END// # 程序体结束标识
delimiter;
– 创建一个函数,传入一个memberId,返回用户的类型(1:普通 2:内部 其他)
delimiter //
DROP FUNCTION IF EXISTS f_get_member_type_msg; # 删除原有函数
CREATE FUNCTION f_get_member_type_msg(memberId INT) # 创建函数 函数名(传入值 值数据类型)
RETURNS VARCHAR(20) # 返回值类型
BEGIN # 程序体开始
DECLARE _msg VARCHAR(20); # 定义retuen 变量
DECLARE _type TINYINT; # 定义类型变量
SELECT type INTO _type FROM member WHERE id=memberId; # 查询sql并把结果赋值变量(值为 1或2或其他)
CASE _type # 判断开始
WHEN 1 THEN SET _msg='普通用户'; # type = 1 执行 set
WHEN 2 THEN SET _msg='内部用户'; # type = 2 执行 set
ELSE SET _msg='其他用户'; # type 不等于1和2 执行 set
END CASE; -- !!!!! # 判断结束
RETURN _msg; # 返回值
END// # 程序体结束
delimiter;
– 传入一个memberId,返回会员是否是土豪(>=40000)、无产(<10000)、中产(>=10000 and <40000)
delimiter //
DROP FUNCTION IF EXISTS f_is_tuhao_case;
CREATE FUNCTION f_is_tuhao_case(memberId INT)
RETURNS VARCHAR(20)
BEGIN
DECLARE _msg VARCHAR(20); -- 定义_msg保存输出信息
DECLARE _amount DECIMAL(18,2); -- 定义一个局部变量保存查询出来的该会员的余额
SELECT leaveAmount INTO _amount FROM member WHERE id=memberId; -- 查询出来的该会员的余额,保存到局部变量_amount中
-- case的条件判断、分支控制
CASE
WHEN _amount >= 40000 THEN SET _msg='我是土豪';
WHEN _amount >= 10000 AND _amount<40000 THEN SET _msg='我是中产';
ELSE SET _msg = '我是无产阶级';
END CASE;
RETURN _msg;
END//
delimiter;
– 向表中循环插入一百条数据
delimiter //
DROP FUNCTION IF EXISTS f_is_tuhao; # 删除原有函数
CREATE FUNCTION f_is_tuhao(memberId INT) # 创建函数 函数名(传入值 值数据类型)
RETURNS VARCHAR(20) # 返回值类型
BEGIN # 程序体开始
DECLARE _num INT; # 创建变量
SET _num=1; # 初始化变量
insert_loop:LOOP # 循环开始 (定义循环名:LOOP)
## 循环体
INSERT INTO tb_lemon_grade(s_name,score,c_name) VALUES('Tom',80,'30期');
SET _num=_num+1; # 迭代语句,每循环一次_num +1,
IF _num%2=0 # 判断逻辑-偶数执行下两行代码
THEN ITERATE insert_loop; # 跳出本次循环
END IF; # 判断结束标识
IF _num>100 # 判断逻辑(是否执行if内代码跳出循环)
THEN LEAVE insert_loop; # 跳出整个循环
END IF; # 判断结束标识
END LOOP insert_loop; # 循环结束标识
RETURN '1'; # 返回值
END// # 程序体结束标识
delimiter;
-- 向表中循环插入一百条数据
delimiter //
DROP FUNCTION IF EXISTS f_loop_insert_3; # 删除原有函数
CREATE FUNCTION f_loop_insert_3() # 创建函数 函数名(传入值 值数据
RETURNS VARCHAR(20) # 返回值类型
BEGIN # 程序体开始
DECLARE _num INT; # 创建变量
SET _num=0; # 初始化变量
insert_repeat:REPEAT # 循环开始 (定义循环名:REPEAT)
SET _num=_num+1; # 迭代语句,每循环一次_num +1,
## 循环体
INSERT INTO tb_lemon_grade(s_name,score,c_name) VALUES(CONCAT('Tom_',_num),80,CONCAT(_num,'期'));
UNTIL _num=100 END REPEAT insert_repeat; # _num=100 跳出整个循环
RETURN "1"; # 返回值
END
//
delimiter;
– 向表中循环插入一百条数据
delimiter //
DROP FUNCTION IF EXISTS f_loop_insert_4; # 删除原有函
CREATE FUNCTION f_loop_insert_4() # 创建函数 函
RETURNS VARCHAR(20) # 返回值类型
BEGIN # 程序体开始
DECLARE _num INT; # 创建变量
SET _num=1; # 初始化变量
insert_while:WHILE _num<=100 DO # 循环开始 (定义循环名:WHILE 循环条件)
## 循环体
INSERT INTO tb_lemon_grade(s_name,score,c_name) VALUES(CONCAT('Tom_',_num),80,CONCAT(_num,'期'));
SET _num=_num+1; # 迭代语句,每循环一次_num +1,
END WHILE insert_while; # 满足while条件跳出循环
RETURN "1"; # 返回值
END//
delimiter;
注意:
1、储存过程中可以调用储存函数,
2、储存函数中不可以调用储存过程
3、储存过程可以传值,储存函数不可以
#调用储存过程语法: Call 储存过程名
– 创建存储过程
语法:CREATE PROCEDURE 过程名([[IN|OUT|INOUT] 参数名 数据类型[,[IN|OUT|INOUT] 参数名 数据类型…]]) [特性 …] 过程体
如:
DELIMITER //
CREATE PROCEDURE Test(OUT s int)
BEGIN
SELECT COUNT(*) FROM ws_user;
END
//
DELIMITER ;
DELIMITER ;”的意为把分隔符还原
IN:参数的值必须在调用存储过程时指定,在存储过程中修改该参数的值不能被返回,为默认值 OUT:该值可在存储过程内部被改变,并可返回
INOUT:调用时指定,并且可被改变和返回
BEGIN与END 中间表示的是过程体
DELIMITER //
CREATE PROCEDURE in_param(out p_in int)
BEGIN
select p_in;
set p_in=2;
select p_in;
END;
//
DELIMITER ;
set @p_in=1; # 设置值
Call in_param(@p_in); # 调用储存过程,in过程值可更改数据
select @p_in; # 查询结果,还是1
#存储过程OUT参数
DELIMITER //
CREATE PROCEDURE out_param(OUT p_out int)
BEGIN
SELECT p_out;
SET p_out=2;
SELECT p_out;
END;
//
DELIMITER ;
#调用
SET @p_out=1; # 设置值
CALL out_param(@p_out); # 调用储存过程,out 改变的值返回(原数据置空)
SELECT @p_out; # 查询结果,变成2了
#存储过程INOUT参数
DELIMITER //
CREATE PROCEDURE inout_param(INOUT p_inout int)
BEGIN
SELECT p_inout;
SET p_inout=2;
SELECT p_inout;
END;
//
DELIMITER ;
#调用
SET @p_inout=1; # 设置值
CALL inout_param(@p_inout) ; # 调用储存过程,inout 改变的值返回
SELECT @p_inout; # 查询结果,变成2了
delimiter //
DROP PROCEDURE if EXISTS proc_in_out_inout;
CREATE PROCEDURE proc_in_out_inout(
IN in_num INT UNSIGNED,
OUT out_num INT UNSIGNED,
INOUT inout_num INT UNSIGNED
)
BEGIN
SET in_num=100; # 值该变了不会返回
SET out_num=out_num+1; # 返回null,赋值具体的值可返回
SET inout_num=inout_num+in_num; # 返回改变后的值
END
//
delimiter ;
## 定义及查询
set @a =1;
set @b =10;
set @c =100;
CALL proc_in_out_inout(@a,@b,@c);
select @a,@b,@c;
set @name = 'wangsong'; # 定义变量
select @name; # 查询变量
set @num = 1+2+3;
select @num;
CREATE PROCEDURE GreetWorld() SELECT CONCAT(@greeting,' World');
SET @greeting='Hello';
CALL GreetWorld();
CREATE PROCEDURE p1() SET @last_proc='p1';
CREATE PROCEDURE p2() SELECT CONCAT('Last procedure was ',@last_proc);
CALL p1();
CALL p2();
#SELECT name FROM mysql.proc WHERE db='jpa';
SELECT routine_name FROM information_schema.routines WHERE routine_schema='jpa';
SHOW PROCEDURE STATUS WHERE db='jpa';
SHOW CREATE PROCEDURE jpa.p2;
ALTER {PROCEDURE | FUNCTION} sp_name [characteristic ...]
characteristic:
{ CONTAINS SQL | NO SQL | READS SQL DATA | MODIFIES SQL DATA }
| SQL SECURITY { DEFINER | INVOKER }
| COMMENT 'string'
sp_name 参数表示存储过程或函数的名称;
characteristic 参数指定存储函数的特性。
CONTAINS SQL 表示子程序包含SQL语句,但不包含读或写数据的语句;
NO SQL 表示子程序中不包含SQL语句;
READS SQL DATA 表示子程序中包含读数据的语句;
MODIFIES SQL DATA 表示子程序中包含写数据的语句。
SQL SECURITY { DEFINER | INVOKER }指明谁有权限来执行,DEFINER表示只有定义者自己才能够执行;INVOKER表示调用者可以执行。
COMMENT 'string’是注释信息。
ALTER PROCEDURE num_from_employee
MODIFIES SQL DATA
SQL SECURITY INVOKER ;
ALTER PROCEDURE name_from_employee
READS SQL DATA
COMMENT 'FIND NAME' ;
DROP PROCEDURE [过程1[,过程2…]]
内部变量在其作用域范围内享有更高的优先权,当执行到end时,内部变量消失,不再可见了,在存储
过程外再也找不到这个内部变量,但是可以通过out参数或者将其值指派给会话变量来保存其值。
DELIMITER //
CREATE PROCEDURE proc()
BEGIN # 储存体
DECLARE x1 VARCHAR(5) DEFAULT 'outer';
BEGIN # 内储存体
DECLARE x1 VARCHAR(5) DEFAULT 'inner';
SELECT x1;
END;
SELECT x1;
END;
//
DELIMITER ;
#调用
CALL proc();
#条件语句IF-THEN-ELSE
DROP PROCEDURE IF EXISTS proc3;
DELIMITER //
CREATE PROCEDURE proc3(IN parameter int) # 创建储存过程
BEGIN # 储存过程开始
DECLARE var int; # 定义变量
SET var=parameter+1; # 定义变量
IF var=0 THEN # var=0 执行
INSERT INTO t VALUES (17);
END IF ;
IF parameter=0 THEN # parameter=0 执行
UPDATE t SET s1=s1+1;
ELSE # parameter 不等于 0 执行
UPDATE t SET s1=s1+2;
END IF ;
END ;
//
DELIMITER ;
定义异常
delimiter //
DROP PROCEDURE IF EXISTS p_condition_handler;
CREATE PROCEDURE p_condition_handler()
BEGIN
#一:定义异常
-- 1:sqlstate_value
-- DECLARE does_not_exist CONDITION FOR SQLSTATE '42S02';
-- 2:mysql_error_code
DECLARE does_not_exist CONDITION FOR 1146;
SELECT * FROM hello;
# 查询异常不在执行下列sql
INSERT INTO member(id) VALUES(10090);
END//
delimiter;
delimiter //
DROP PROCEDURE IF EXISTS p_condition_handler;
CREATE PROCEDURE p_condition_handler()
BEGIN
# 遇到SQLSTATE '42S02' 错误程序继续下走,并给变量flag赋值为1
-- DECLARE CONTINUE HANDLER FOR SQLSTATE '42S02' SET @=1;
# 遇到 1146 错误程序继续下走,并给变量flag赋值为1
-- DECLARE CONTINUE HANDLER FOR 1146 SET @flag=1;
# 遇到自定义异常 错误程序继续下走,并给变量flag赋值为1
-- DECLARE CONTINUE HANDLER FOR does_not_exist SET @flag=1;
DECLARE EXIT HANDLER FOR SQLEXCEPTION SET @flag=1;
SET @flag=2;
# 查询出了异常,还希望执行 insert 语句,可定义相应的异常
SELECT * FROM hello;
INSERT INTO member(id) VALUES(10090);
END//
delimiter;
-- ERROR 1146 (42S02): Table 'lemon.hello' doesn't exist
-- 1:已经发生了错误
-- 2: 1146:错误码
-- 3:42S02:sql状态值
当向 ws_user表中插入一行记录时,触发储存过程直接向 ws_user2 表插入一条记录
delimiter //
DROP TRIGGER IF EXISTS t_insert_invest; # 删除储存过程
CREATE TRIGGER t_insert_invest AFTER INSERT # 创建储存过程 + INSERT触发(插入数据触发)
ON ws_user FOR EACH ROW # on 表名(触发器关联的表)
BEGIN
## 过程体
INSERT INTO ws_user2(password,username) VALUES('123456','wangwu');
END//
delimiter ;
# 当向 ws_user表中插入一行记录时,触发 t_insert_invest 储存过程
INSERT INTO ws_user(password,username) VALUES('123456','wangwu3')
# 查询
select count(*) from ws_user;
select count(*) from ws_user2;
select * from ws_user;
select * from ws_user2;
– 创建一个触发器,再删除一个投资用户时,删除其相关的信息:投资、回款计划信息、用户信息
OLD.id 的说明
old :删除触发时之前的老数据(表对象)
new :新增触发时添加的新数据(表对象)
delimiter //
CREATE TRIGGER t_delete_member_info BEFORE DELETE
ON member FOR EACH ROW
BEGIN
-- 删除所有的回款计划信息
DELETE FROM repayment WHERE investId in(select id from invest where memberId=OLD.id);
-- 删除投资记录
delete from invest where memberId = OLD.id;
END//
delimiter;
1、使用事件需开启定时器
2、定义事件,使用sql 还是储存过程看业务需求
下方有的使用过程,有的使用sql
SHOW VARIABLES LIKE 'event_scheduler'; # 查看是否开启定时器(事件)
SET GLOBAL event_scheduler = 1; # 开启定时器 0:off 1:on
-- 开启事件,event_test为事件名
ALTER EVENT event_insert_member ON
COMPLETION PRESERVE ENABLE;
-- 关闭事件,event_test为事件名
ALTER EVENT event_insert_member ON
COMPLETION PRESERVE DISABLE;
定义事件
CREATE EVENT IF NOT EXISTS event_test
ON SCHEDULE EVERY 1 SECOND
ON COMPLETION PRESERVE
DO CALL e_test();
delimiter //
CREATE EVENT event_insert_member # 定义:CREATE EVENT 函数名
ON SCHEDULE EVERY 1 MINUTE # ON SCHEDULE 定义时间
ON COMPLETION PRESERVE ENABLE DO
BEGIN
# 执行语句
INSERT INTO ws_user2(password,username) VALUES('123456','wangwu');
END//
delimiter ;
delimiter //
CREATE EVENT IF NOT EXISTS temp_event
ON SCHEDULE EVERY 1 DAY STARTS DATE_ADD(DATE_ADD(CURDATE(), INTERVAL 1 DAY), INTERVAL 1 HOUR)
ON COMPLETION PRESERVE ENABLE
DO INSERT INTO ws_user2(password,username) VALUES('123456','wangwu');
//
delimiter ;
delimiter //
CREATE EVENT dele_from_thinkyh
ON SCHEDULE EVERY 1 DAY STARTS '2016-12-30 00:05:00'
ON COMPLETION PRESERVE ENABLE COMMENT '每天清除数据表think_youhui中的过期的记录'
DO DELETE FROM think_youhui WHERE think_youhui.yhendtime
delimiter //
CREATE EVENT EVENT1
ON SCHEDULE EVERY 9 DAY STARTS NOW()
ON COMPLETION PRESERVE ENABLE
DO
BEGIN
INSERT INTO ws_user2(password,username) VALUES('123456','wangwu');
END
//
delimiter ;
delimiter //
CREATE EVENT EVENT2
ON SCHEDULE EVERY 1 MONTH STARTS DATE_ADD(DATE_ADD(DATE_SUB(CURDATE(),INTERVAL DAY(CURDATE())-1 DAY), INTERVAL 1 MONTH),INTERVAL 1 HOUR)
ON COMPLETION PRESERVE ENABLE
DO
BEGIN
INSERT INTO ws_user2(password,username) VALUES('123456','wangwu');
END
//
delimiter ;
delimiter //
CREATE EVENT TOTAL_SEASON_EVENT
ON SCHEDULE EVERY 1 QUARTER STARTS DATE_ADD(DATE_ADD(DATE( CONCAT(YEAR(CURDATE()),'-',ELT(QUARTER(CURDATE()),1,4,7,10),'-',1)),INTERVAL 1 QUARTER),INTERVAL 2 HOUR)
ON COMPLETION PRESERVE ENABLE
DO
BEGIN
# CALL SEASON_STAT(); 可以调用储存过程
INSERT INTO ws_user2(password,username) VALUES('123456','wangwu');
END
//
delimiter ;
delimiter //
CREATE EVENT TOTAL_YEAR_EVENT
ON SCHEDULE EVERY 1 YEAR STARTS DATE_ADD(DATE(CONCAT(YEAR(CURDATE()) + 1,'-',1,'-',1)),INTERVAL 4 HOUR)
ON COMPLETION PRESERVE ENABLE
DO
BEGIN
# CALL YEAR_STAT(); 可以调用储存过程
INSERT INTO ws_user2(password,username) VALUES('123456','wangwu');
END
//
delimiter ;