相关数据库sql文件
链接:https://pan.baidu.com/s/1RynVEnBL2nak5aJf0rXYmA
提取码:11gz
语法
create view 视图名
as
查询语句
#例题
#创建视图
#查询邮箱中包含a字符的员工名、部门名和工种信息
CREATE VIEW view1
AS
SELECT e.last_name,e.`email`,d.department_name,j.*
FROM employees e,departments d,jobs j
WHERE e.`department_id` = d.`department_id`
AND e.`job_id` = j.`job_id`;
#使用视图
SELECT * FROM view1 WHERE last_name LIKE '%a%';
优点:
两种方式
#方式一(若视图存在则修改,若不存在则创建)
create or replace view 视图名
as
查询语句
#例一
CREATE OR REPLACE VIEW view2
AS
SELECT * FROM jobs;
#方式二
alter view 视图名
as
查询语句
#例二
ALTER VIEW view2
AS
SELECT * FROM jobs;
查看视图
DESC 视图名;
删除视图
DROP VIEW 视图名,视图名,....;
插入
INSERT INTO 视图名 VALUES(字段值,字段值...);
修改
UPDATE 视图名 SET 属性 = 值,... WHERE 条件
删除
DELETE FROM 视图名 WHERE 条件;
注意:视图的可更新性和视图中查询的定义有关,以下类型的视图是不能更新的
视图 | 表 | |
---|---|---|
创建语法的关键字 | create view | create table |
是否实际占用物理空间 | 否(只保存了SQL逻辑) | 是(保存了数据) |
使用 | 一般不能增删改 | 可以增删改 |
系统变量
概念:变量由系统提供,不是用户自定义,属于服务器层面
分类
语法
1.查看所有的系统变量
SHOW GLOBAL VARIABLES; #查看全局变量
SHOW SESSION VARIABLES; #查看会话变量
SHOW GLOBAL|【SESSION 】 VARIABLES;
2.查看满足条件的部分系统变量
SHOW GLOBAL|【SESSION 】 VARIABLES LIKE '%char%';
3.查看指定的某个系统变量的值
SELECT @@GLOBAL|【SESSION 】.变量名
4.为某个系统变量赋值
方式一:
SET GLOBAL|【SESSION 】系统变量 名 = 值;
方式二:
SET @@GLOBAL|【SESSION 】.系统变量 名 = 值;
注意:如果是全局变量,则需要加global,如果是会话级别,则需要加session,如果不写,则默认session;
自定义变量
用户变量
作用域:针对于当前会话(连接)有效,同于会话变量。可以应用在任何地方(begin end里外皆可)
语法
1.声明并初始化
SET @用户变量名 = 值;
SET @用户变量名: = 值;
SELECT @用户变量名: = 值;
2.赋值(更新用户变量的值)
方式一:通过set或select
SET @用户变量名 = 值;
SET @用户变量名: = 值;
SELECT @用户变量名: = 值;
方式二:通过 select into
select 字段名 into @变量名
from 表
例:SELECT COUNT(*) INTO @count
FROM employees;
3.使用(查看用户变量的值)
select @用户变量名;
局部变量
作用域:仅仅在定义他的begin end中有效(应用在begin end 中的第一句话!!!)
语法
1.声明
declare 变量名 类型;
declare 变量名 类型 default 值; #附上初始化值
2.赋值
方式一:通过set或select
SET 局部变量名 = 值;
SET 局部变量名: = 值;
SELECT @局部变量名: = 值;
方式二:通过 select into
select 字段名 into 变量名
from 表
例:SELECT COUNT(*) INTO count
FROM employees;
3.使用
select 局部变量名;
用户变量与局部变量对比
用户变量 | 局部变量 | |
---|---|---|
作用域 | 当前会话 | begin end 中 |
定义和使用的位置 | 会话中的任何地方 | 只能在begin end 中,且为第一句话 |
语法 | 必须加@符号,不用限定类型 | 一般不用加@符号,需要限定类型 |
含义:一组预先编译好的SQL语句的集合,理解成批处理语句
优点:
语法
1.创建语法
create procedure 存储过程名(参数列表)
begin
存储过程体(一组合法的SQL语句)
end
2.调用语法
call 存储过程名(实参列表);
案例:
#创建
DELIMITER $
CREATE PROCEDURE myp1()
BEGIN
INSERT INTO admin(username,PASSWORD)
VALUES('tom','7896'),('jack','7896'),('rose','7896'),('laoying','7896');
END $
#调用
CALL myp1();
****************************带IN模式的存储过程*****************************
案例1
#创建
#根据女生名,查询相应男生信息
CREATE PROCEDURE myp2(IN bname VARCHAR(20))
BEGIN
SELECT bo.* FROM boys bo
RIGHT JOIN beauty b ON bo.id = b.boyfriend_id
WHERE b.name = bname;
END$
#调用
CALL myp2('赵敏');
#案例2
#创建存储过程实现,用户是否登陆成功
DELIMITER $
CREATE PROCEDURE myp3(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('tom','7896');
****************************带out模式的存储过程*****************************
案例1
#根据女生名返回对应的男生名
DELIMITER $
CREATE PROCEDURE myp4(IN bename VARCHAR(20),OUT boname VARCHAR(20))
BEGIN
SELECT bo.boyName INTO boname
FROM boys bo
INNER JOIN beauty b ON bo.id = b.boyfriend_id
WHERE b.name = bename;
END$
CALL myp4('小昭',@bnames);
SELECT @bnames;
案例二
#根据女生名返回对应的男生名和魅力值
DELIMITER $
CREATE PROCEDURE myp5(IN bename VARCHAR(20),OUT boname VARCHAR(20),OUT usercp INT)
BEGIN
SELECT bo.boyName,bo.userCP INTO boname,usercp
FROM boys bo
INNER JOIN beauty be ON bo.id = be.boyfriend_id
WHERE be.name = bename;
END$
CALL myp5('赵敏',@boname,@usercp);
SELECT @boname,@usercp;
****************************带inout模式的存储过程*****************************
DELIMITER $
CREATE PROCEDURE myp6(INOUT a INT,INOUT b INT)
BEGIN
SET a = a*2;
SET b = b*2;
END$
SET @m = 2;
SET @n = 3;
CALL myp6(@m,@n);
SELECT @m,@n;
in stuname varchar(20)
语法
delimiter 结束标记
存储过程的删除
语法
drop procedure 存储过程名; #只能一个一个删除
存储过程的查看
语法
show create procedure 存储过程名;
含义:一组预先编译好的SQL语句的集合,理解成批处理语句
优点:
语法
1.创建语法
create function 函数名(参数列表) returns 返回类型
begin
函数体
end
*注意:
1.参数列表包括两部分: 参数名、参数类型
2.函数体:肯定会有return语句,如果没有会报错
如果return语句没有放在函数体的最后也不会报错,但不建议
3.函数体中仅有一句话,则可以省略 begin end
4.使用delimiter语句设置结束标记
2.调用语法
select 函数名(参数列表)
案例:
#返回公司的员工个数(无参)
DELIMITER $
CREATE FUNCTION myf1() RETURNS INT
BEGIN
DECLARE a INT DEFAULT 0;#定义变量
SELECT COUNT(*) INTO a#赋值
FROM employees;
RETURN a;
END$
SELECT myf1();
#根据部门名返回平均工资(有参)
DELIMITER $
CREATE FUNCTION myf3(dename 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 = dename;
RETURN sal;
END$
SELECT myf3('Exe');
3.查看函数
show create function 函数名;
4.删除函数
drop function 函数名;
函数与存储过程的区别
存储过程 | 函数 | |
---|---|---|
返回值 | 可以有0个返回,也可以有多个返回 | 有且仅有1个返回 |
适用途径 | 批量插入、更新 | 处理数据后返回一个结果 |
功能:能够实现简单的双分支
语法:
if(表达式1,表达式2,表达式3)
执行顺序:如果表达式1成立,则返回表达式2的值,不成立则返回表达式3的值
应用:任何地方
语法:
case 变量|表达式|字段
when 要判断的值 then 返回的值1或语句1;
when 要判断的值 then 返回的值2或语句2;
...
else 要返回的值n或语句n;
end case;
语法:
case
when 要判断的条件1 then 返回的值1或语句1;
when 要判断的条件2 then 返回的值2或语句2;
...
else 要返回的值n或语句n;
end case;
#案例
#创建存储过程,根据传入的成绩,来显示等级,比如传入的成绩:
#90-100显示A,80-90显示B,60-80显示C,否则显示D
DELIMITER /
CREATE PROCEDURE test_case(IN score INT)
BEGIN
CASE
WHEN score>=90 AND score<=100 THEN SELECT 'A';
WHEN score>=80 THEN SELECT 'B';
WHEN score>=60 THEN SELECT 'C';
ELSE SELECT 'D';
END CASE;
END/
CALL test_case(87);
功能:实现多重分支
语法
if 条件1 then 语句1;
else if 条件2 then 语句2;
...
【else 语句n;】
end if;
应用在 begin end 中
#案例
#创建存储过程,根据传入的成绩,来显示等级,比如传入的成绩:
#90-100返回A,80-90返回B,60-80返回C,否则返回D
DELIMITER $
CREATE FUNCTION test_if(score INT) RETURNS CHAR
BEGIN
IF score >= 90 AND score <= 100 THEN RETURN 'A';
ELSEIF score >= 80 THEN RETURN 'B';
ELSEIF score >= 60 THEN RETURN 'C';
ELSE RETURN 'D';
END IF;
END$
SELECT test_if(88);
语法:
【标签:】while 循环条件 do
循环体;
end while 【标签】;
#案例1
#批量插入十条语句
DELIMITER $
CREATE PROCEDURE pro_while(IN insertcount INT)
BEGIN
DECLARE i INT DEFAULT 1;
WHILE i<=insertcount DO
INSERT INTO admin(username,PASSWORD) VALUES(CONCAT('Rose',i),'146');
SET i = i+1;
END WHILE;
END$
CALL pro_while(10);
#案例2:添加leave语句
#批量插入,根据次数插入到admin表中多条记录,如果次数>20则停止
DELIMITER $
CREATE PROCEDURE test_while(IN insertcount INT)
BEGIN
DECLARE i INT DEFAULT 1;
a:WHILE i<insertcount DO
INSERT INTO admin(username,PASSWORD) VALUES(CONCAT('Jack',i),'666');
IF i>=20 THEN LEAVE a;
END IF;
SET i = i+1;
END WHILE a;
END$
CALL test_while(50);
#案例3:添加iterate语句
#
语法:
【标签:】loop
循环体;
end loop【标签】;
语法:
【标签:】repeat
循环体;
until 结束循环的条件
end repeat【标签】;
while | loop | repeat | |
---|---|---|---|
特点 | 先判断后执行 | 先执行后判断 | 没有条件的死循环 |
位置 | begin end 中 | begin end 中 | begin end 中 |
对比 | MyISAM | InnoDB |
---|---|---|
主外键 | 不支持 | 支持 |
事务 | 不支持 | 支持 |
行表锁 | 表锁,即使操作一条记录也会锁住整个表,不适合高并发操作 | 行锁,操作时只锁某一行,不对其他行有影响。适合高并发操作 |
缓存 | 只缓存索引 | 不仅要缓存索引还要缓存真实数据,对内存要求性能较高,且内存大小对性能有决定性的影响 |
表空间 | 小 | 大 |
关注点 | 性能 | 事务 |
默认安装 | Y | Y |
单值索引:即一个索引包含单个列,一个表可以有多个单列索引
唯一索引:索引列的值必须唯一,但允许有空值
复合索引:即一个索引包含多个列
基本语法 :
#创建
create 【unique】index index_name on table_name(column_name(length));
alter table table_name add 【unique】index 【index_name】 on (column_name(length));
#删除
drop index 【index_name】 on table_name;
#查看
show index from table_name\G
#使用alert命令,有四种方式来添加数据表的索引
alert table table_name add primary key(column_list);#该语句添加一个主键,这意味着索引值必须是唯一的,且不能是null
alter table table_name add unique index_name(column_list);#该语句创建的索引值是唯一的(除了null以外,null可能会出现多次)
alter table table_name add index index_name(column_list);#添加普通索引,索引值可出现多次
alter table table_name add fulltext index_name(column_list);#该语句指定了索引为fulltext,用于全文索引
单表
* 建表SQL:
CREATE TABLE IF NOT EXISTS article(
id INT(10) UNSIGNED NOT NULL PRIMARY KEY AUTO_INCREMENT,
author_id INT(10) UNSIGNED NOT NULL,
category_id INT(10) UNSIGNED NOT NULL,
views INT(10) UNSIGNED NOT NULL,
comments INT(10) UNSIGNED NOT NULL,
title VARBINARY(255) NOT NULL,
content TEXT NOT NULL
);
INSERT INTO article(author_id,category_id,views,comments,title,content)
VALUES(1,1,1,1,'1','1'),(2,2,2,2,'2','2'),(3,3,3,3,'3','3');
#索引
CREATE INDEX idx_article_ccv ON article(category_id,comments,views);
案例:
#查询category_id为1且comments大于1的情况下,views最多的artice_id
EXPLAIN
SELECT id,author_id
FROM article
WHERE category_id=1
AND comments>1
ORDER BY views
DESC LIMIT 1;
结果(未建索引之前)
结果(建索引之后)
很明显创建索引后,虽然解决了全表扫描的问题,却又多了文件内排序的问题;这是因为 comments>1
范围查询和order by 和group by会中断联合索引,导致索引失效;
解决办法:删除ccv,创建cv
DROP INDEX idx_article_ccv ON article;
CREATE INDEX idx_article_cv ON article(category_id,views);
再次查询,完美解决
两表
* 建表SQL:
CREATE TABLE IF NOT EXISTS class(
id INT(10) UNSIGNED NOT NULL AUTO_INCREMENT,
card INT(10) UNSIGNED NOT NULL,
PRIMARY KEY(id)
);
CREATE TABLE IF NOT EXISTS book(
bookid INT(10) UNSIGNED NOT NULL AUTO_INCREMENT,
card INT(10) UNSIGNED NOT NULL,
PRIMARY KEY(bookid)
);
INSERT INTO class(card) VALUES(FLOOR(1+RAND()*20));
INSERT INTO class(card) VALUES(FLOOR(1+RAND()*20));
INSERT INTO class(card) VALUES(FLOOR(1+RAND()*20));
INSERT INTO class(card) VALUES(FLOOR(1+RAND()*20));
INSERT INTO class(card) VALUES(FLOOR(1+RAND()*20));
INSERT INTO class(card) VALUES(FLOOR(1+RAND()*20));
INSERT INTO class(card) VALUES(FLOOR(1+RAND()*20));
INSERT INTO class(card) VALUES(FLOOR(1+RAND()*20));
INSERT INTO book(card) VALUES(FLOOR(1+RAND()*20));
INSERT INTO book(card) VALUES(FLOOR(1+RAND()*20));
INSERT INTO book(card) VALUES(FLOOR(1+RAND()*20));
INSERT INTO book(card) VALUES(FLOOR(1+RAND()*20));
INSERT INTO book(card) VALUES(FLOOR(1+RAND()*20));
INSERT INTO book(card) VALUES(FLOOR(1+RAND()*20));
INSERT INTO book(card) VALUES(FLOOR(1+RAND()*20));
INSERT INTO book(card) VALUES(FLOOR(1+RAND()*20));
案例
#左连接
EXPLAIN
SELECT *
FROM class
LEFT JOIN book ON class.card = book.card;
结果:出现全表扫描(All)
解决:添加索引优化
#加在右表上
ALTER TABLE book ADD INDEX Y(card);
#加在左表上
ALTER TABLE class ADD INDEX Y(card);
结果:
三表
* 建表SQL:
CREATE TABLE IF NOT EXISTS phone(
phoneid INT(10) UNSIGNED NOT NULL AUTO_INCREMENT,
card INT(10) UNSIGNED NOT NULL,
PRIMARY KEY(phoneid)
)ENGINE = INNODB;
INSERT INTO phone(card) VALUES(FLOOR(1+RAND()*20));
INSERT INTO phone(card) VALUES(FLOOR(1+RAND()*20));
INSERT INTO phone(card) VALUES(FLOOR(1+RAND()*20));
INSERT INTO phone(card) VALUES(FLOOR(1+RAND()*20));
INSERT INTO phone(card) VALUES(FLOOR(1+RAND()*20));
INSERT INTO phone(card) VALUES(FLOOR(1+RAND()*20));
INSERT INTO phone(card) VALUES(FLOOR(1+RAND()*20));
INSERT INTO phone(card) VALUES(FLOOR(1+RAND()*20));
Join语句的优化
CREATE TABLE staffs(
id INT PRIMARY KEY AUTO_INCREMENT,
NAME VARCHAR(24) NOT NULL DEFAULT '' COMMENT '姓名',
age INT NOT NULL DEFAULT 0 COMMENT '年龄',
pos VARCHAR(20) NOT NULL DEFAULT '' COMMENT '职位',
add_time TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '入职时间'
)CHARSET utf8 COMMENT '员工记录表';
INSERT INTO staffs(NAME,age,pos,add_time) VALUES('z3',22,'manage',NOW());
INSERT INTO staffs(NAME,age,pos,add_time) VALUES('July',23,'dev',NOW());
INSERT INTO staffs(NAME,age,pos,add_time) VALUES('2000',23,'dev',NOW());
SELECT * FROM staffs
ALTER TABLE staffs ADD INDEX idx_nameAgePos(NAME,age,pos);
全值匹配
#查询一
EXPLAIN SELECT * FROM staffs WHERE NAME = 'July';
#查询二
EXPLAIN SELECT * FROM staffs WHERE NAME = 'July' AND age = 23;
#查询三
EXPLAIN SELECT * FROM staffs WHERE NAME = 'July' AND age = 23 AND pos = 'dev';
#查询四
EXPLAIN SELECT * FROM staffs WHERE age = 25 AND pos = 'dev';
#查询五
EXPLAIN SELECT * FROM staffs WHERE NAME = 'July' AND pos = 'dev';
★最佳左前缀法则(若索引多列,要遵守最左前缀法则 ):
不在索引列上做任何操作(计算、函数、(自动or手动)类型转换),会导致索引失效而转向全表扫描
EXPLAIN SELECT * FROM staffs WHERE LEFT(NAME,4) = 'July';
存储引擎不能使用索引中范围条件右边的列
EXPLAIN SELECT * FROM staffs WHERE NAME = 'July' AND age > 25 AND pos = 'dev';
尽量使用覆盖索引(只访问索引的查询(索引列和查询列一致)),减少select *;
EXPLAIN SELECT NAME,age,pos FROM staffs WHERE NAME = 'July' AND age = 25 AND pos = 'dev';
EXPLAIN SELECT NAME,age,pos FROM staffs WHERE NAME = 'July' AND age > 25 AND pos = 'dev';
mysql在使用不等于(!= 或者 <>)的时候无法使用索引会导致全表扫描
EXPLAIN SELECT * FROM staffs WHERE NAME != 'July';
is null,is not null也无法使用索引
EXPLAIN SELECT * FROM staffs WHERE NAME IS NULL;
EXPLAIN SELECT * FROM staffs WHERE NAME IS NOT NULL;
like以通配符开头(‘%abc…’)mysql索引失效会变成全表扫描的操作
EXPLAIN SELECT * FROM staffs WHERE NAME LIKE '%July%';
EXPLAIN SELECT * FROM staffs WHERE NAME LIKE '%July';
EXPLAIN SELECT * FROM staffs WHERE NAME LIKE 'July%';
很明显,当%
在右边时才能够用上索引,但是这也存在这一个问题就是只能查询以July
开头的数据,局限性太大
问题:解决like ‘%字符串%’ 时索引不被使用的方法(覆盖索引)
#建表SQL
CREATE TABLE tbl_user(
id INT(11) NOT NULL AUTO_INCREMENT,
NAME VARCHAR(24) DEFAULT NULL,
age INT(11) DEFAULT NULL,
email VARCHAR(20) DEFAULT NULL,
PRIMARY KEY(id)
)ENGINE = INNODB AUTO_INCREMENT = 1 DEFAULT CHARSET = utf8;
INSERT INTO tbl_user(NAME,age,email) VALUES('1aa1',21,'[email protected]');
INSERT INTO tbl_user(NAME,age,email) VALUES('2aa2',22,'[email protected]');
INSERT INTO tbl_user(NAME,age,email) VALUES('3aa3',23,'[email protected]');
INSERT INTO tbl_user(NAME,age,email) VALUES('4aa4',24,'[email protected]');
#建索引
CREATE INDEX idx_user_nameAge ON tbl_user(NAME,age);
EXPLAIN SELECT NAME,age FROM tbl_user WHERE NAME LIKE '%aa%';
EXPLAIN SELECT NAME FROM tbl_user WHERE NAME LIKE '%aa%';
EXPLAIN SELECT age FROM tbl_user WHERE NAME LIKE '%aa%';
EXPLAIN SELECT id FROM tbl_user WHERE NAME LIKE '%aa%';
EXPLAIN SELECT id,NAME,age FROM tbl_user WHERE NAME LIKE '%aa%';
EXPLAIN SELECT id,age FROM tbl_user WHERE NAME LIKE '%aa%';
EXPLAIN SELECT id,NAME FROM tbl_user WHERE NAME LIKE '%aa%';
建索引之前
建索引之后
字符串不加单引号索引失效(会引起类型转换)
EXPLAIN SELECT * FROM staffs WHERE NAME = '2000'
EXPLAIN SELECT * FROM staffs WHERE NAME = 2000
少用or,用它来连接时会索引失效(8.0显示type是range)
EXPLAIN SELECT * FROM staffs WHERE NAME = '2000' OR NAME = 'July'
总结
where语句 | 索引是否被使用 |
---|---|
where a = 3 | 是,使用到a |
where a = 3 and b = 5 | 是,使用到a,b |
where a = 3 and b = 5 and c = 4 | 是,使用到a,b,c |
where a = 3 and c = 5 | 使用到a,但是c不可以,b中间断了 |
where a = 3 and b > 4 and c = 5 | 能使用到a、b,c 在范围之后不能使用 |
where a = 3 and b like ‘kk%’ and c = 4 | 是, a、b、c都能用 |
对于单键索引,尽量选择针对当前query过滤性更好的索引。
在选择组合索引时,当前Query中过滤性最好的字段在索引字段顺序中,位置越靠前越好。
在选择组合索引的时候,尽量选择可以能够包含当前query中的where子句中更多字段的索引
尽可能通过分析统计信息和调整query的写法来达到选择合适索引的目的
总结口诀:
全职匹配我最爱,最左前缀要遵守
带头大哥不能死,中间兄弟不能断
索引列上少计算,范围之后全失效
Like百分写最右,覆盖索引不写星
不等空值还有or,索引失效要少用
建表SQL
CREATE TABLE tblA(
age INT,
birth TIMESTAMP NOT NULL
);
INSERT INTO tblA(age,birth) VALUES(22,NOW());
INSERT INTO tblA(age,birth) VALUES(23,NOW());
INSERT INTO tblA(age,birth) VALUES(24,NOW());
CREATE INDEX idx_A_age_birth ON tblA(age,birth);
case
case1:
EXPLAIN SELECT * FROM tblA WHERE age > 20 ORDER BY age;
EXPLAIN SELECT * FROM tblA WHERE age > 20 ORDER BY age,birth;
EXPLAIN SELECT * FROM tblA WHERE age > 20 ORDER BY birth;
EXPLAIN SELECT * FROM tblA WHERE age > 20 ORDER BY birth,age;
case2
EXPLAIN SELECT * FROM tblA ORDER BY birth;
EXPLAIN SELECT * FROM tblA ORDER BY age ASC,birth DESC;
MySQL支持两种方式排序,filesort和index,index效率高,它指MySQL扫描索引本身完成排序。filesort方式效率低
order by满足两种情况,会使用index排序:
mysql两种排序方式:文件排序或扫描有序索引排序
mysql能为排序和查询使用相同的索引
order by能使用索引最左前缀
如果where使用索引的最左前缀定义为常量,则order by能使用索引(where a = 1 order by b,c)
不能使用索引进行排序的情况
index:key a_b_c(a,b,c)
#排序不一致
order by a asc,b desc,c desc
#丢失a索引
order by b,c
#丢失b索引
order by a,c
#d不是索引的一部分
order by a,d
#对于排序来说,多个相等条件也是范围查询
where a in(...) order by b,c
默认:show variables like '%slow_query_log%';
开启:set global slow_query_log = 1;
(只临时修改当前数据库)
要永久生效需要修改配置文件my.cnf文件,【mysqld】下增加或修改参数
slow_query_log = 1
slow_query_log_file=/usr/local/mysql/data/localhost-slow.log
查看low_query_time 参数:show variables like 'long_query_time%';
#建表dept
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 DEFAULT CHARSET = utf8;
#建表emp
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 DEFAULT CHARSET = utf8;
#随机产生字符串
DELIMITER$
CREATE FUNCTION rand_string(n INT) RETURNS VARCHAR(255)
BEGIN
DECLARE chars_str VARCHAR(100) DEFAULT 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ';
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$
#随机产生部门编号
DELIMITER $
CREATE FUNCTION rand_num() RETURNS INT(5)
BEGIN
DECLARE i INT DEFAULT 0;
SET i = FLOOR(100+RAND()*10);
RETURN i;
END$
#创建往emp表中插入数据的存储过程
DELIMITER $
CREATE PROCEDURE insert_emp(IN START INT(10),IN max_num INT(10))
BEGIN
DECLARE i INT DEFAULT 0;
SET autocommit = 0;#自动提交设置为0
REPEAT
SET i = i + 1;
INSERT INTO emp(empno,ename,job,mgr,hiredate,sal,comm,deptno)
VALUES((START+1),rand_string(6),'salesman',0001,CURDATE(),2000,400,rand_num());
UNTIL i = max_num
END REPEAT;
COMMIT;
END $
#创建往dept表中插入数据的存储过程
DELIMITER $
CREATE PROCEDURE insert_dept(IN START INT(10),IN max_num INT(10))
BEGIN
DECLARE i INT DEFAULT 0;
SET autocommit = 0;#自动提交设置为0
REPEAT
SET i = i + 1;
INSERT INTO dept(deptno,dname,loc)
VALUES((START+1),rand_string(10),rand_string(8));
UNTIL i = max_num
END REPEAT;
COMMIT;
END $
CALL insert_dept(100,10);
CALL insert_emp(100,10);
show variables like 'profiling';
set profiling = on;
show profiles
show profile cpu,block io for query + 上一步前面的问题SQL数字号码
在 my.cnf中,设置如下:
#开启
general_log = 1
#记录日志文件的路径
general_log_file = /path/logfile
#输出格式
log_output = file
命令
set global general_log = 1;
set global log_output = 'table';
#此后,你所编写的SQL语句,将会记录到mysql库里的general_log表,可以用下列的命令查看
select * from mysql.general_log;
建表SQL
CREATE TABLE mylock(
id INT NOT NULL PRIMARY KEY AUTO_INCREMENT,
NAME VARCHAR(20)
)ENGINE MYISAM;
INSERT INTO mylock(NAME) VALUES('a');
INSERT INTO mylock(NAME) VALUES('b');
INSERT INTO mylock(NAME) VALUES('c');
INSERT INTO mylock(NAME) VALUES('d');
INSERT INTO mylock(NAME) VALUES('e');
SELECT * FROM mylock;
SHOW OPEN TABLES;#查看表上加过的锁
格式:LOCK TABLE 表名1 read(write),表名2 read(write),...;
LOCK TABLE mylock READ,book WRITE;
unlock tables;#解除所有锁
加读锁
LOCK TABLE mylock READ;
#session1表示终端、session2代表连接终端的客户端
session1 session2
获得表的read锁定 连接终端
当前session可以查询该表记录 其他session也可以查询该表的记录
当前session不能查询其他没有锁定的表 其他session可以查询或更新未锁定的表
当前session插入或者更新锁定的表都会提示错误 其他session插入或者更新锁定表会一直等待获得锁
释放锁 获得锁,插入操作完成
加写锁
LOCK TABLE mylock WRITE;
session1 session2
可以读当前表 不可以查询当前表,阻塞
可以修改当前表 不可以修改当前表,阻塞
不能查询其他表,报错
SHOW STATUS LIKE 'table%';
特点:
案例分析
#行锁定基本演示
session1 session2
set autocommit = 0 set autocommit = 0
更新但是不提交,没有手写commit 被阻塞,只能等
提交更新 解除阻塞,更新正常进行
commit提交
结论
优化建议
mysql版本一致且后台以服务运行
主从都配置在【mysqld】结点下,都是小写
主机修改my.ini配置文件(Windows)
server-id = 1
log-bin = mysql本地的路径/mysqlbin
log-err = mysql本地的路径/mysqlerr
basedir = "mysql本地的路径/"
tmpdir = "mysql本地的路径/"
datadir = "mysql本地的路径/Data/"
read-only = 0
:主机,读写都可以binlog-ignore-db = 库名
binlog-do-db = 需要复制的主数据库的名字
从机修改my.cnf配置文件(Linux)
server-id = 2
因修改过配置文件,mysql需重启服务
主从机皆关闭防火墙
在Windows主机上建立账户并授权salve
grant replication slave on *.* to 'zhangsan'@'从机数据库IP' identified by '123456';
flush privileges;
show master status;
在Linux从机上配置需要复制的主机
change master to master_host = ‘主机IP’,
master_user = ‘zhangsan’,
master_password = ‘123456’,
master_log_file = ‘mysqlbin.具体数字’,
master_log_pos = Position数字;
start slave;
主机新建库、新建表、insert记录、从机复制
如何停止从服务复制功能:stop slave;