数据库Mysql——sql语句大全

注意:练习sql语句之前推荐先安装可视化软件如:SQLyog、Navicat 15 for MySQL
不然就在cmd小黑窗口敲命令练习。

一篇掌握Mysql——sql语句

#注意:sql语句不区分大小写!!!
#开启、关闭服务
    管理员启动cmd,net start/stop mysql
#登录数据库
    mysql 【-h localhost -P 端口号】 -u 用户名  -p,,
#退出数据库
    exit或 快捷键ctrl+c
#查看Mysql版本号
    select version();
#查看当前所有数据库
    show databases
#打开指定的库use 库名;
#查看某库所有的表
    show tables from 库名;
#创建表
    create table(
    字段名称 字段类型,
    字段名称 字段类型
    );
#查看表结构
    desc 表名;
#修改字符集
    set names gbk;
#查看表内容
    select * from 表名;
#插入内容
    insert into 表名 (id , name) values1,‘yangbo’);
#更新修改内容
    update  表名 set name='yb' where id=1;
#删除内容
    delete from 表名  where id=1;
#注释
单行注释   #注释    -- 注释
多行注释  /* 注释 */
    
#DQL数据查询语言
    
##进阶1 基础查询-----------------------------------------
# select  (字段、常量、表达式、函数) from  表名;
USE myemployees;
 SELECT
  job_id          #F12变规范
FROM
  jobs;

SELECT
  `manager_id`,  #`` 区分系统sql关键字和字段名
  `first_name`,
  #逗号自己加
   `email`
FROM
  `employees`;

SELECT * FROM employees;

SELECT    1+4;

#查询函数
SELECT VERSION();

#起别名alias  便于理解 ,防止重名
SELECT last_name AS, first_name ASFROM employees;
SELECT last_name 姓 FROM employees ;  # 无AS
SELECT last_name 'x  #i ng' FROM employees;

#去重
SELECT  DISTINCT department_id FROM employees;

# + 号的作用
SELECT 10+10;   #==20
SELECT "10"+10; #==20
SELECT '1o'+10; #==11
SELECT 'oo'+10; #==10
SELECT NULL+10; #==null

#拼接文本,判断是否为null
SELECT CONCAT(last_name,first_name)  AS 姓名 FROM employees;
SELECT CONCAT(`first_name`,`job_id`,IFNULL(`commission_pct`,0)) AS infor
FROM employees;

##进阶2 条件查询--------------------------------------------
 /*
select 查询列表  #3
from 表名        #1
where 条件       #2

条件表达 <  >  =  !=  <>  <=  >=
#逻辑表达  &&  ||  !   连接多条件表达
#          and  or  not
#模糊查询
    like
    between  and
    in
    is null
*/
USE `myemployees`;

SELECT
  *
FROM
  employees
WHERE salary > 12000;



SELECT
  last_name,
  `department_id`
FROM
  `employees`
WHERE `department_id` <> 90;


SELECT
    last_name,
    salary
FROM
    employees
WHERE
    salary>=10000 AND salary<=20000;

#查询部门编号不是在90-120,或者工资大于15000的员工信息
SELECT *
FROM  employees
#where not(`department_id`>=90 and `department_id`<=120) or salary>15000;
#where department_id not between 90 and 15000 or salary>15000;
WHERE NOT(department_id BETWEEN 90 AND 15000) OR salary>15000;


#模糊查询 通配符
# % 表示任意多个字符  _ 表示1个字符 , %%不能代表null!!!!!!!!!!
SELECT *
FROM employees
WHERE last_name LIKE '%a%';  #姓包含a
SELECT *
FROM employees
WHERE last_name LIKE '_i%'; #查询第二个字符为i,注意%的使用
SELECT *
FROM `employees`
WHERE last_name LIKE '_\_%'; #查询第二个字符为下划线。\为转义符
SELECT *
FROM employees
WHERE last_name LIKE '_$_%' ESCAPE '$'; #escape指定转义符为$

#between A and B ===  >=A and <=B,所以 A<=B
SELECT *
FROM employees
WHERE `employee_id` BETWEEN 100 AND 120;

#in
/*
in === multiple or , 不支持通配符,因为or是=,不是like
*/
SELECT *
FROM employees
WHERE `job_id` IN ('AD_VP','IT_PROG');
# where `job_id`='AD_VP' OR `job_id`='IT_PROG';


#is null  /  is not null
SELECT *
FROM  employees
WHERE commission_pct IS NULL;

SELECT ISNULL(`commission_pct`),commission_pct  #判断为null=1,notnull=0
FROM `employees`;

#安全等于 <=>
SELECT *
FROM employees
WHERE `job_id` <=> 'AD_VP';

SELECT *
FROM employees
WHERE salary <=> 12000;

SELECT *
FROM employees
WHERE commission_pct <=> NULL;


##进阶3 排序---------------------------------------------

#ORDER BY 默认asc ,降序,放在查询最后(除limit之外)
#工资从高到低
SELECT * FROM `employees` ORDER BY salary DESC;

#加筛选条件
#按表达式排序
#ifnull(字段,0),字段值为null,赋值为0,不为null,为原值
SELECT salary, salary*12*(1+IFNULL(`commission_pct`,0)) AS 年薪
FROM  employees
ORDER BY salary*12*(1+IFNULL(`commission_pct`,0));

#按照别名排序
SELECT salary, salary*12*(1+IFNULL(`commission_pct`,0)) AS 年薪
FROM  employees
ORDER BY 年薪 DESC;  #说明order by 最后一步执行

#按函数排序
SELECT `last_name`,LENGTH(`last_name`)
FROM `employees`
ORDER BY LENGTH(`last_name`) DESC;

#多条件排序 员工信息 先按工资升序,后按 员工编号降序
SELECT *
FROM employees
ORDER BY salary ASC,`employee_id` DESC;


##进阶4 常见函数-------------------------------------------------

/*
函数分为单行和多行函数
单行比如length(),肯定有返回值
    1.字符函数(处理字符串)
多行函数,又称组函数,统计用
*/

#一、 字符函数##########################

#1.length,获取字符长度
SELECT LENGTH('莫默123aaa');
#UTF8中文为3个字节,GBK中文2个字节
SHOW VARIABLES LIKE '%char%';

#2.concat
SELECT CONCAT(`last_name`,'_',`first_name`) AS 姓名 FROM employees;

#3.upper lower

SELECT CONCAT(UPPER(last_name),LOWER(first_name)) 姓名 FROM employees;

#4.substr / substring
SELECT SUBSTR('李莫愁爱上了陆展元',7) AS output; #从第7个位置开始
SELECT SUBSTR('李莫愁爱上了陆展元',1,3) AS output; #从第1个位置开始的三个字符

#大写首字母
SELECT CONCAT(UPPER(SUBSTR(last_name,1,1)),LOWER(SUBSTR(last_name,2)))
FROM employees;

#5 instr
#返回substr在str中第一次出现的索引,若不匹配,返回0
SELECT INSTR('杨不悔爱上了殷六侠','殷六侠') AS output;
#substr和instr连用,取邮箱用户名
SELECT SUBSTR(email,1,INSTR(email,'@')-1) FROM ..;
#6. trim
#去掉首位空格或指定str
 SELECT TRIM('  莫默  ') AS output;
SELECT TRIM('a' FROM 'aa默aa') AS output;

#7. lpad
#左填充str为10个字符长,用padstr填充
SELECT LPAD('莫默',10,'*') AS output;

#8. rpad
SELECT RPAD('y',5,'-') AS output;

#9 replace
# 在str中,from_str被to_str替代
SELECT REPLACE('张无忌爱上了周芷若','周芷若','赵敏') AS output;

#二、数学函数############################

SELECT ROUND(1.4);
SELECT ROUND(1.459,2);

SELECT CEIL(1.01); # 取右  取≥该参数的最小整数

SELECT FLOOR(-1.09); #往数轴 取左

SELECT TRUNCATE(1.123456,4); #取小数点前4位

#取余
SELECT MOD(10,3);
SELECT MOD(-10,3);  #结果和被除数正负一致
SELECT 10%3;

#出一个随机数,范围0-1
SELECT RAND(0123456);
#三、日期函数##############################
SELECT NOW();

SELECT CURDATE();


SELECT CURTIME();
#获得指定的部分 year年 month月 date日 hour小时 minute分 second秒
SELECT YEAR(NOW());
SELECT YEAR('1994-09-10');
SELECT YEAR(hiredate) FROM employees;
SELECT MONTH(NOW());
SELECT MONTHNAME(NOW()); #返回英文月

#日期格式的字符  转换成指定的格式
#%Y1994 %y94 %m01 %c1 %d30 %H24 %h12 %i00 59 %s00 59
SELECT MONTHNAME( STR_TO_DATE('1994-4-2','%Y-%c-%d') )output ;
#           日期型字符    该字符的格式
/*
#查询入职日期为1992-4-3的员工信息
select * from employees where hiredate = '1992-4-3';
若日期型字符格式为04-03 92
select * from employees where hiredate = str_to_date('04-03 1992','%m-%d %Y');
*/
#时间格式的转换为特定格式的字符串
SELECT DATE_FORMAT(NOW(),'%Y年%m月%d日') 日期;
#查询有奖金的员工的员工名和入职日期,要求该日期格式输出为xx月/xx日 xxxx年
 SELECT
  last_name 姓名,
  DATE_FORMAT (hiredate, '%m月/%d日 %Y年') 入职日期
FROM
  employees
WHERE commission_pct IS NOT NULL;

#求日期之差
SELECT DATEDIFF(CURDATE(),'1994-01-21');


#四、 其他函数###################################
SELECT VERSION();
SELECT DATABASE();
SELECT USER();
SELECT PASSWORD('yb'); #加密
SELECT MD5('yb');    #加密
#五、流程控制函数###################################
#if
SELECT IF(10>5,1,0); #和excel的if一样
SELECT IF(salary<20000,IF(salary<10000,'small','media'),'high')
    grade #nesting 嵌套
FROM employees;

SELECT last_name, commission_pct,
 IF(commission_pct IS NULL,'呵呵','哈哈')   备注
FROM employees;

#case多条件判断,可以和select搭配,也可单独使用
/*
case 常量
when 值1 then 显示的值1
when 值2 then 显示的值2
。。。
else 要显示的值n
end
*/
#在部门30的,工资×1.1
#在部门40的,工资×1.2
#在部门50的,工资×1.3
#其他部门,原工资
SELECT `department_id`, salary,
CASE `department_id`
WHEN 30 THEN salary*1.1
WHEN 40 THEN salary*1.2
WHEN 50 THEN salary*1.3
ELSE salary
END
AS 新工资
FROM employees;
#excel嵌套大法
SELECT department_id,salary,
IF(department_id=30,salary*1.1,
IF(department_id=40,salary*1.2,
IF(department_id=50,salary*1.3,salary)))AS 新工资
FROM employees;

/* 多重if
case
when 条件1 then 显示的值1
when 条件2 then 显示的值2
。。。
else 要显示的值n
end
*/
#工资<10000,small;10000<=工资<20000,media;20000<工资,high
SELECT salary,
CASE
WHEN salary>20000 THEN 'high'
WHEN salary>10000 THEN 'media'
ELSE 'small'
END
AS grade
FROM employees;
#excel嵌套大法
SELECT salary,IF(salary<20000,IF(salary<10000,'small','media'),'high')
                    grade #nesting 嵌套
FROM employees;

##二、统计函数

#sum avg max min count
#sum avg只能处理数字型,max 、min、count可以字符型
#全部忽略null值
#可以搭配distinct,去重
#和统计函数一同使用的时group by 后的字段。☆☆☆☆☆

SELECT SUM(salary),AVG(salary),MAX(salary),MIN(salary),COUNT(salary)
FROM employees;

SELECT SUM(salary),ROUND(AVG(salary),2),MAX(salary),MIN(salary),COUNT(salary)
FROM employees;

SELECT SUM(`commission_pct`),MAX(hiredate)
FROM employees;

SELECT SUM(DISTINCT salary), COUNT(DISTINCT `commission_pct`),
    SUM(salary),COUNT(`commission_pct`)
FROM `employees`;

#count单独使用
SELECT COUNT(*) FROM `employees`;#☆☆☆
SELECT COUNT(1) FROM `employees`;

#进阶5 分组查询 ------------------------------------------------
/*
select 统计函数,字段
from 表名
【where 包含表名字段的条件】  #不可使用别名
group by 字段   #可使用别名
【having 包含统计函数的字段】  #可使用别名
【order by 字段】 #可使用别名

*/
#简单分组查询:每个部门平均工资
SELECT AVG(salary),`department_id`
FROM employees
GROUP BY `department_id`;
#前筛选--分组查询:邮箱中包含a字符的,每个部门平均工资,
#where要跟在from 后面一行
SELECT AVG(salary),department_id,email
FROM employees
WHERE email LIKE '%a%'
GROUP BY department_id;
#有奖金的每个领导手下的员工最高工资
SELECT MAX(salary),manager_id
FROM employees
WHERE commission_pct IS NOT NULL
GROUP BY manager_id;
#后筛选--分组查询:领导编号>102的每个领导手下员工的最低工资>5000的领导编号
#,以及其最低工资
SELECT MIN(salary),manager_id
FROM employees
WHERE manager_id>102           #优先考虑前筛选☆☆☆☆☆
GROUP BY manager_id
HAVING MIN(salary)>5000;
#按函数--分组查询:按员工姓名长度分组,查询每组个数,筛选员工个数>5的有哪些
SELECT COUNT(*),LENGTH(last_name)
FROM employees
GROUP BY LENGTH(last_name)
HAVING COUNT(*)>5;
#多字段--分组查询
#每个部门每个工种的平均员工工资
SELECT AVG(salary),department_id,job_id
FROM employees
GROUP BY department_id,job_id;
#排序--分组查询
#每个部门每个工种的员工平均工资,按照平均工资大小降序
SELECT AVG(salary),department_id,job_id
FROM employees
GROUP BY department_id,job_id
ORDER BY AVG(salary) DESC;

#进阶6 多表连接查询-----------------------------------------------
#当查询的字段来自多个表
#分类:内连接:等值连接、非等值连接、自连接
#      外连接:左外连接、右外连接、全外连接
#      交叉连接

##92版 内连接
USE girls;
SELECT NAME,boyName FROM beauty ,boys
WHERE beauty.`boyfriend_id`=boys.`id`;

#查询员工名和对应的部门名
SELECT last_name,department_name
FROM employees , departments
WHERE employees.`department_id`=departments.`department_id`;
#为表名起别名,且from先执行,所以select 里面的job.id有歧义,只能用别名去标明
#查询员工名、工种号、工种名 #
#SELECT last_name,employees.job_id,job_title
SELECT last_name,e.job_id,job_title
FROM employees e, jobs j
WHERE e.`job_id`=j.`job_id`;


#筛选--内连接
#有奖金的员工名、部门名
SELECT last_name,department_name
FROM employees e,departments d
WHERE e.`department_id`=d.`department_id`
AND e.`commission_pct` IS NOT NULL;
#查询城市名中第二个字符为o的部门名和城市名
EXPLAIN SELECT department_name,city
FROM departments d,locations l
WHERE d.`location_id`=l.`location_id`
AND l.`city` LIKE '_o%';

#分组-内连接
#查询每个城市的部门个数
SELECT COUNT(*), city
FROM departments d,locations l
WHERE d.`location_id`=l.`location_id`
GROUP BY l.`city`;
#查询有奖金的每个部门的部门名和部门领导编号,以及该部门最低工资
SELECT department_name,e.manager_id,MIN(salary)
FROM departments d,employees e
WHERE d.`department_id`=e.`department_id`
AND e.`commission_pct` IS NOT NULL;

#三表连接
#查询员工名、部门名、所在城市
SELECT last_name,department_name,city
FROM employees e,departments d,locations l
WHERE e.`department_id`=d.`department_id`
AND d.`location_id`=l.`location_id`;

#非等值连接
#查询员工工资和工资级别
SELECT salary, grade_level
FROM employees e,job_grades g
WHERE e.`salary` BETWEEN g.`lowest_sal` AND g.`highest_sal`;

#自连接
#查询员工名和上级名称
SELECT a.last_name,b.last_name
FROM employees a,employees b
WHERE a.`manager_id`=b.`employee_id`;

#99版内连接
#查询员工名和对应的部门名
SELECT last_name,department_name
FROM employees e
JOIN departments d
ON e.`department_id`=d.`department_id`;

#为表名起别名,且from先执行,所以select 里面的job.id有歧义,只能用别名去标明
#查询员工名、工种号、工种名 #
SELECT last_name,e.job_id,job_title
FROM employees e
JOIN jobs j
ON e.`job_id`=j.`job_id`;

#筛选--内连接
#有奖金的员工名、部门名
SELECT last_name,department_name
FROM employees e
JOIN departments d
ON e.`department_id`=d.`department_id`
WHERE e.`commission_pct` IS NOT NULL;


#查询城市名中第二个字符为o的部门名和城市名
SELECT department_name,city
FROM departments d
JOIN locations l
ON d.`location_id`=l.`location_id`
WHERE l.`city` LIKE '%o%';

#分组-内连接
#查询每个城市的部门个数
SELECT COUNT(*),city
FROM departments d
JOIN locations l
ON d.`location_id`=l.`location_id`
GROUP BY l.`city`;

#查询有奖金的每个部门的部门名和部门领导编号,以及该部门最低工资
SELECT department_name,d.manager_id,MIN(salary)
FROM employees e
JOIN departments d
ON e.`department_id`=d.`department_id`
GROUP BY d.`department_name`;



#三表连接
#查询员工名、部门名、所在城市
SELECT last_name,department_name,city
FROM departments d
JOIN  employees e ON e.`department_id`=d.`department_id`
JOIN locations l ON d.`location_id`=l.`location_id`;

#非等值连接
#查询员工工资和工资级别
SELECT salary, grade_level
FROM employees e
JOIN job_grades j
ON e.`salary` BETWEEN j.`lowest_sal` AND j.`highest_sal`;

#自连接
#查询员工名和上级名称
SELECT a.last_name,b.last_name
FROM employees a
JOIN employees b ON  a.`manager_id`=b.`employee_id`;

SELECT department_name,COUNT(*) 员工个数
FROM departments d INNER
JOIN employees e
ON d.`department_id`=e.`department_id`
GROUP BY d.`department_name`
HAVING COUNT(*)>3
ORDER BY  员工个数 DESC;

#左外连接#查询非交集(内连接查询的即为交集)
SELECT b.*, bo.boyName
FROM beauty b       #主表
LEFT JOIN boys bo
ON b.`boyfriend_id`=bo.`id`
WHERE bo.`boyName` IS  NULL;
#右外连接
SELECT bo.boyName,b.*
FROM boys bo
RIGHT JOIN beauty b
ON bo.`id`=b.`boyfriend_id`
WHERE bo.`boyName` IS  NULL;
#-----
SELECT bo.*,b.name
FROM boys bo
LEFT JOIN beauty b
ON bo.`id`=b.`boyfriend_id`;

#交叉连接(笛卡尔乘积)
SELECT * FROM beauty;
SELECT * FROM boys;
SELECT b.*,bo.*
FROM beauty b
CROSS JOIN boys bo;
SELECT b.*,bo.*
FROM beauty b,boys bo;
####7种join大法
#左拼
SELECT * FROM a LEFT JOIN b ON a.id=b.id;
#左拼左独有
SELECT * FROM a LEFT JOIN b ON a.id=b.id WHERE b.id IS NULL;

#右拼
SELECT * FROM a RIGHT JOIN b ON a.id=b.id;
#右拼右独有
SELECT * FROM a RIGHT JOIN b ON a.id=b.id WHERE b.id IS NULL;

#内连接
SELECT * FROM a INNER JOIN b ON a.id=b.id ;

##全外连接(mysql不支持全外,使用union连接去重的特性,实现full join)
SELECT * FROM a LEFT  JOIN b ON a.id=b.id
UNION
SELECT * FROM a RIGHT JOIN b ON a.id=b.id;

##左独有  拼  右独有
SELECT * FROM a LEFT JOIN b ON a.id=b.id WHERE b.id IS NULL;
UNION
SELECT * FROM a RIGHT JOIN b ON a.id=b.id WHERE b.id IS NULL;


#习题 那个城市没有部门
SELECT l.city ,d.department_name
FROM locations l
LEFT JOIN departments d
ON l.`location_id`=d.`location_id`
WHERE d.`department_name` IS NULL;

#习题 查询部门名为SAL/IT的员工信息
SELECT d.department_name,e.*
FROM departments d
LEFT JOIN employees e  #注意主表不同,结果不同(把lfet换成right,39行)
ON e.`department_id`=d.`department_id`
WHERE d.`department_name` IN( 'SAL' ,'IT');

##进阶7 子查询---------------------------------------
#出现在其他语句的select查询语句为子查询
#子查询出现的位置:    select后面
#               仅支持标量子查询
#            from后面
#               仅支持表子查询(多行多列)
#            where后面(**)/having后面(**)
#               标量子查询(一行一列)√
#               列子查询(多行一列)  √
#               行子查询(一行多列)
#            exists后面
#               表子查询
/*
#where和having后面:标量、列、行子查询
特点: 子查询放在小括号内
    一般放在条件的右侧
    标量子查询一般搭配单行操作符使用:> < >= <= = <>
    列子查询一般搭配多行操作符使用 in any/some all
    查询过程中,子查询优先执行
*/
##标量子查询
#案例1 谁的工资比Abel高?
SELECT *
FROM employees
WHERE salary>( SELECT salary FROM employees WHERE last_name='Abel');
#案例2 返回job_id和141号员工相同,salary比143号员工高的员工信息
SELECT *
FROM employees
WHERE job_id =
    (SELECT job_id FROM employees WHERE employee_id=141)
AND   salary>
    (SELECT salary FROM employees WHERE employee_id = 143);
#案例3 返回工资最少的员工的信息
SELECT *
FROM employees
WHERE salary = (SELECT MIN(salary) FROM employees );
#案例4 查询最低工资大于50号部门的最低工资的部门name和其最低工资(外加多表查询)
SELECT MIN (salary), d.department_name
FROM employees e
JOIN departments d
ON e.`department_id` = d.`department_id`
GROUP BY d.department_name
HAVING MIN (salary) >
  (SELECT
    MIN (salary)
  FROM
    employees
  WHERE department_id = 50);
##列子查询
#案例1 返回location_id是1400或者1700的部门中所有员工姓名
SELECT *
FROM employees
WHERE department_id IN (
 SELECT DISTINCT department_id
 FROM departments
 WHERE location_id IN(1400,1700)
);
SELECT *
FROM employees
WHERE department_id =ANY (
 SELECT DISTINCT department_id
 FROM departments
 WHERE location_id IN(1400,1700)
);
# is not in == <>all
# is in == =any
#返回其他工种中比job_id为'IT-PROG'工种任一工资低的员工号、姓名、jobid、salary
SELECT employee_id,last_name,job_id,salary
FROM employees
WHERE salary < ANY(
    SELECT DISTINCT salary FROM employees WHERE job_id='IT_PROG'
) AND job_id <> 'IT_PROG';
SELECT employee_id,last_name,job_id,salary
FROM employees
WHERE salary < (
    SELECT DISTINCT MAX(salary) FROM employees WHERE job_id='IT_PROG'
) AND job_id <> 'IT_PROG';
#返回其他工种中比job_id为'IT-PROG'工种*所有*工资低的员工号、姓名、jobid、salary
SELECT employee_id,last_name,job_id,salary
FROM employees
WHERE salary < ALL(
    SELECT DISTINCT salary FROM employees WHERE job_id='IT_PROG'
) AND job_id <> 'IT_PROG';
SELECT employee_id,last_name,job_id,salary
FROM employees
WHERE salary < (
    SELECT DISTINCT MIN(salary) FROM employees WHERE job_id='IT_PROG'
) AND job_id <> 'IT_PROG';
##3、行子查询
#查询员工编号最小,工资最高的员工信息

SELECT * FROM employees
WHERE employee_id = (SELECT MIN(employee_id) FROM employees)
AND  salary = (SELECT MAX(salary) FROM employees);

SELECT * FROM employees e
WHERE (e.`employee_id` , e.`salary`) =(
    SELECT MIN(employee_id) ,MAX(salary) FROM employees);


#二、select后面子查询,**仅支持标量子查询
#每个部门的员工个数
SELECT d.*,(
    SELECT COUNT(*)
    FROM employees e
    WHERE e.department_id = d.`department_id`) N
FROM departments d;
SELECT d.* ,COUNT(*) N
FROM employees e,departments d
WHERE e.`department_id`=d.`department_id`
GROUP BY e.`department_id`;
#员工号102的部门名
SELECT (SELECT d.department_name
    FROM employees e,departments d
    WHERE e.department_id=d.department_id
    AND e.employee_id=102) N
;
##from 后面跟表子查询,表子查询必须要起别名
#案例:每个部门的平均工资的工资等级
SELECT a.avge,a.department_id, j.`grade_level`
FROM (
    SELECT AVG(salary) avge,department_id
    FROM employees
    GROUP BY department_id
) a
, job_grades j
WHERE a.avge BETWEEN j.`lowest_sal` AND j.`highest_sal`;
SELECT a.*, j.`grade_level`
FROM (
    SELECT AVG(salary) avge,department_id
    FROM employees
    GROUP BY department_id
) a
JOIN job_grades j
ON a.avge BETWEEN j.`lowest_sal` AND j.`highest_sal`;

##exists (相关子查询)
SELECT EXISTS(
    SELECT salary FROM employees WHERE salary = 23000
);
#返回布尔向量,0或1
#有员工的部门名
SELECT d.department_name
FROM departments d
WHERE d.department_id IN (SELECT e.department_id FROM employees e);

SELECT d.department_name
FROM departments d
WHERE EXISTS
(SELECT * FROM employees e WHERE e.`department_id`=d.`department_id`);

(SELECT
  e.*
FROM
  employees e ,departments d
WHERE e.`department_id` = d.`department_id`);
#案例2 没有女朋友的男神信息
USE girls;
SELECT bo.*
FROM boys bo
WHERE NOT EXISTS (
    SELECT * FROM beauty b WHERE b.`boyfriend_id`=bo.`id`
);
##习题:查询各部门中工资比本部门平均工资高的员工信息
SELECT e.*
FROM employees e,(
    SELECT AVG(salary) av,department_id
    FROM employees
    GROUP BY department_id
)a
WHERE e.`department_id`=a.department_id
AND e.`salary`>a.av;


SELECT e.* ,e.`department_id` FROM employees e
JOIN (
    SELECT AVG(salary) av,department_id
    FROM employees
    GROUP BY department_id
)a
ON e.`department_id`=a.department_id
WHERE e.`salary`>a.av;

##进阶8 分页查询--------------------------------------------
#伪代码
#    select 查询列表
#    from 表名
#    【join type
#      where group by  having  order by 】
#    limit 【起始页=0】,size
#公式:******limit (page-1)*size,size*********
#*******************查询虚拟表的极值order by xx desc limit 1*******************
#案例1 查询前5条员工信息
SELECT * FROM employees LIMIT 0,5; #从0开始!!!!!!,substr是1开始
SELECT * FROM employees LIMIT 5;
#案例2 有奖金的员工信息,工资较高的前10名
SELECT *
FROM employees
WHERE commission_pct IS NOT NULL
ORDER BY salary DESC
LIMIT 0,10;
#执行顺序
/*
select 查询列表        7
from 表名        1
连接类型 join 表2    2 #产生一个笛卡尔乘积
on 连接条件        3 #根据连接条件筛选
where 筛选条件        4
group by 分组列表    5
having 分组后筛选    6
order by 排序列表    8
limit 偏移,条目数    9
*/

#子查询经典习题
#案例1 查询平均工资最低的部门信息
SELECT d.*
FROM departments d
JOIN (
    SELECT AVG(salary) ag ,e.department_id
    FROM employees e
    GROUP BY e.`department_id`
    ORDER BY a.ag
    LIMIT 0,1
) a
ON d.`department_id`=a.department_id
;
#平均工资最高的job信息
SELECT j.*
FROM jobs j,(
    SELECT AVG(salary) av,job_id
    FROM employees
    GROUP BY job_id
    ORDER BY av DESC
    LIMIT 1
) a
WHERE  j.`job_id`=a.job_id;

#查询平均工资高于公司平均工资的部门
SELECT a.department_id,a.av
FROM (
    SELECT AVG(salary) av,department_id
    FROM employees
    GROUP BY department_id
) a
WHERE a.av>(
    SELECT AVG(salary) FROM employees
);
#查询公司中所有manager的详细信息
SELECT b.*
FROM employees a
JOIN employees b
ON  a.`manager_id`=b.`employee_id`
GROUP BY b.`employee_id`;

SELECT *
FROM employees
WHERE employee_id = ANY(
    SELECT DISTINCT manager_id
    FROM employees
    GROUP BY manager_id
);
#各部门中,最高工资最低的那个部门 的最低工资为
SELECT MIN(salary)
FROM employees
WHERE department_id =(
    SELECT department_id
    FROM employees a
    GROUP BY department_id
    ORDER BY MAX(salary)
    LIMIT 1)
;
#查询平均工资最高的部门的manager信息
#where 标量子查询结果为null时,如何判断
SELECT *
FROM employees e
RIGHT JOIN (
    SELECT manager_id
    FROM employees
    GROUP BY department_id
    ORDER BY AVG(salary) DESC
    LIMIT 1
) a
ON IFNULL(e.manager_id,1) = IFNULL(a.manager_id,1) ;

SELECT
  *
FROM
  employees e
WHERE IFNULL (e.manager_id, 1) = IFNULL (
    (SELECT
      manager_id
    FROM
      employees
    GROUP BY department_id
    ORDER BY AVG (salary) DESC
    LIMIT 1),1
  );

#查询生日在1988-1-1后的学生姓名和专业名称
USE student;
SELECT  s.`studentname`,m.`majorname`
FROM student s
JOIN major m
ON s.`majorid`=m.`majorid`
WHERE DATEDIFF(s.`borndate`,'1988-1-1')>0;
#每个专业的男生女生人数
SELECT m.`majorname`, sex,COUNT(*)
FROM student s,major m
WHERE s.`majorid`=m.`majorid`
GROUP BY s.majorid , sex;
#变横行
SELECT majorid ,
(SELECT COUNT(*) FROM student WHERE sex="男" AND majorid=s.`majorid`),
(SELECT COUNT(*) FROM student WHERE sex="女" AND majorid=s.`majorid`)FROM student s
GROUP BY majorid;
#查询专业和张翠山一样的学生的最低分
SELECT MIN(score)
FROM student s
JOIN result r
ON s.`studentno`=r.`studentno`
WHERE majorid=(
    SELECT  majorid
    FROM student
    WHERE studentname = '张翠山'
);
#查询哪个专业没有学生,分别左连接,右连接
SELECT m.*
FROM major m
LEFT JOIN (
    SELECT COUNT(*) AS a, majorid
    FROM student
    GROUP BY majorid
) n
ON m.`majorid`=n.majorid
WHERE n.a IS NULL;

#没有成绩的学生人数
SELECT COUNT(*)
FROM result r
RIGHT JOIN student s
ON r.`studentno`=s.`studentno`
WHERE r.`id` IS NULL;

##进阶9 联合查询-----------------------------------------------
/* 语法:查询语句1
    union 【all】
    查询语句2
特点:
查询结果来自多个表,且各表之间无关键列索引
1、查询列数一致
2、每个查询语句的列的排列一致
3、union会去重,显示全部为union all
*/
SELECT c.id ,c.name FROM  china c
UNION ALL
SELECT s.id ,s.name FROM stuinfo s;

###DML语言 database manipulate language
#    插入insert
#    修改update
#    删除delete

#插入语句
#方式一:insert into 表名(列名) values(值,'值')

USE girls;
INSERT INTO beauty(id,NAME,sex,borndate,phone,photo,boyfriend_id)
VALUES(13,'安妮·海瑟薇','女','1980-1-1','188888888',NULL,NULL);

#变量nullable,用null作为该字段值,或者不写该字段
INSERT INTO beauty(id,NAME,sex,borndate,phone)
VALUES(14,'三上悠亚','女','1988-1-1','288888888');

#列的顺序可以调换
#字段个数和字段值必须一一对应
#可以省略列名,默认为所有字段,且按照表中顺序排列
INSERT INTO beauty
VALUES(15,'妲露拉·莱莉','女','1982-1-1','388888888',NULL,NULL);

#方式二:insert into 表名 set 字段名=值...
INSERT INTO beauty
SET id=16,NAME='刘涛',phone='12345678';

#两种方式 PK

#方式一可以插入多行
INSERT INTO beauty(id,NAME,sex,borndate,phone)
VALUES(17,'高圆圆','女','1988-10-10','488888888'),
(18,'白百何','女','1989-01-10','588888888');

INSERT INTO beauty #效率高**********
SELECT 17,'高圆圆','女','1988-10-10','488888888' UNION#*****
SELECT 18,'白百何','女','1989-01-10','588888888';

#方式一支持子查询#行子查询的结果为beauty的新值
INSERT INTO beauty(id,NAME,phone)
SELECT 19,'唐艺昕','688888888';

#修改单表记录
#语法 update 表名 set 字段名=值.... where 筛选条件
UPDATE beauty
SET NAME='唐嫣'
WHERE id=15;

#修改多表记录
#修改张无忌的女朋友电话为18866669999
UPDATE beauty b JOIN boys bo
ON b.`boyfriend_id`=bo.`id`
SET phone = '18866669999'
WHERE bo.`boyName`='张无忌';
#没有男朋友的女神的男朋友都为id=2
UPDATE beauty b LEFT JOIN boys bo
ON b.`boyfriend_id`=bo.`id`
SET b.`boyfriend_id`=2
WHERE bo.`id` IS NULL;

#删除语句
#方式一
#单表删除 ,LIMIT 2 b表示删除两条记录
DELETE FROM beauty WHERE id=19 LIMIT 2;

#多表删除
#删除黄晓明的信息以及他女朋友的信息
DELETE bo,b
FROM boys bo JOIN beauty b
ON b.`boyfriend_id`=bo.`id`
WHERE bo.`boyName`='黄晓明';

#方式二
TRUNCATE TABLE boys;

#两种方式PK 筛 率 断 返 滚
/*
1.delete可以加where,truncate不能
2.truncate效率高
3.对于自增列,delete删除后在插入,从断点处记录,
  truncate删除后从1 开始记录
4.truncate无返回值,delete显示删除了多少行(多少行受影响)
5.truncate删除不能回滚,delete删除可以回滚
*/
SELECT * FROM boys;

DELETE FROM boys;
TRUNCATE TABLE boys;
INSERT INTO boys(boyName,userCP)
VALUES('张无忌',100),
('鹿晗',800),
('段誉',300);


###DDL数据定义语言
#库的管理,表的管理
#库、表的创建create、修改alter,管理drop
#位置 programdata/mysql/data

##库的管理
#库的创建
CREATE DATABASE IF NOT EXISTS books;
#库的修改
RENAME DATABASE books TO  newbooks;#不稳定
#修改字符集
ALTER DATABASE books CHARACTER SET gbk;
#库的删除
DROP DATABASE IF EXISTS books;

##表的管理
#表的创建
#create table 表名(
#    列名 类型 【长度,约束】,
#    列名 类型 【长度,约束】
#    );
CREATE DATABASE IF NOT EXISTS books;

CREATE TABLE  IF NOT EXISTS book (
    id INT,
    bookname VARCHAR(20), #(20)为必须
    booauthorid INT,
    publishtime DATETIME
);
CREATE TABLE author(
    id INT,
    b_name VARCHAR(20),
    nation VARCHAR(20)
)
DESC author;
DESCRIBE author;

#表的修改
DESCRIBE book;
#修改列名
ALTER TABLE book CHANGE COLUMN publishtime pubdate DATETIME;
#修改类型或约束
ALTER TABLE book MODIFY COLUMN pubdate TIMESTAMP;
#添加新列
ALTER TABLE book ADD COLUMN annual DOUBLE ;
ALTER TABLE stuinfo ADD COLUMN sex CHAR AFTER id;
ALTER TABLE stuinfo ADD COLUMN fam_id INT FIRST;
#删除列
ALTER TABLE book DROP COLUMN annual;
#修改表名
ALTER TABLE author RENAME bookauthor;

##表的删除
DROP TABLE IF EXISTS bookauthor;
SHOW TABLES;

##表的复制
CREATE TABLE author(
    id INT,
    b_name VARCHAR(20),
    nation VARCHAR(20)
);
INSERT INTO author
VALUES
    (1,'村上春树','日本'),
    (2,'路遥','中国'),
    (3,'王小波','中国');

#仅复制表的结构,跨库:库名.表名
CREATE TABLE copy LIKE author;
#复制表的内容+结构
CREATE TABLE copy1
SELECT * FROM author;
#只复制部分数据
CREATE TABLE copy2
SELECT id ,nation
FROM author WHERE nation='中国';
#仅复制某些字段的结构
CREATE TABLE copy3
SELECT id ,nation
FROM author
WHERE 0; #where 1=2;

###常见数值类型
/*
数值型:整数、小数(定点数、浮点数)
字符型:较短文本char、varchar,
    较长文本text、blob(较长二进制)
日期型:
*/
##数值型:
/*整数:tinyint(3)、smallint(5)、mediumint(7)、int(10)、bigint
    int有符号为正负最大10位,27********,无符号为最大正10位,49********
特点:  1。默认为有符号(-),unsgined关键字表示无符号
    2。数字超过界值,警告,且用界值填充
    3。有默认长度,长度表示显示的最大宽度,位数不够用0填补,但必须搭配zerofill
    4。zerofill只能为无符号
*/
USE test;
DROP TABLE  tab_int;
CREATE TABLE tab_int(
    integer1 INT,
    integer2 INT UNSIGNED,
    integer3 INT(10) ZEROFILL
);

INSERT INTO tab_int VALUES (-1234567890,1234567890,123);
INSERT INTO tab_int VALUES (-1234567890,12345678901,123);
DESC tab_int;
SELECT * FROM tab_int;
/*小数:
浮点型float(M,D)、double(M,D)
定点型dec(M,D)/decimal(M,D)
通用的情况下,优先float>double>decimal
特点: 1。M表示整数+小数位数
    2.D表示小数位数
    3.如果数值位数超过范围,则插入临界值
    4.M D都可以省略,float和double随数据长度变化,但默认decimal(10,0)
    5.decimal精度高,用于如货币运算
*/
CREATE TABLE tab_dem(
    d1 FLOAT(5,2),
    d2 DOUBLE(5,3),
    d3 DECIMAL
);
DROP TABLE tab_dem;
INSERT INTO tab_dem(d1) VALUES(123.12);
INSERT INTO tab_dem(d2) VALUES(123.1);

SELECT * FROM tab_dem;
DESC tab_dem;

##文本类型
#较短文本 char varchar (二进制binary varbinary)
#较长文本 text blob(二进制)

#特点
/*
    写法        M                特点
char    char(M)        最大字符数,可以省略,默认1    固定长度
varchar varchar(M)    最大字符数,不接省略        可变长度

            空间的耗费    效率
            比较耗费    较高
            比较节省    较低


*/
#枚举 enum
CREATE TABLE tab_enum(
    e1 ENUM('a','b','c')
);
DESC tab_enum;
INSERT INTO tab_enum VALUES
    ('a'),    #不区分大小写
    ('c');  #不在列举范围,则插入值为空
SELECT * FROM tab_enum;

#集合 set
CREATE TABLE tab_set(
    s1 SET('a','b','c')
);
INSERT INTO tab_set VALUES
    ('a'),   #不区分大小写
    ('a,c'); #可加两个
SELECT * FROM tab_set;

##日期类型
/*分类:
date日期
time时间
year年
        字节    范围        时区
datetime    8    1000-9999    不受时区影响
timestamp    4    1970-2038    受影响

*/
#datetime和timestamp(受时区,语法模式、版本影响,但更能反映当前时区的真实时间)
CREATE TABLE tab_date(
    d1 DATETIME,
    d2 TIMESTAMP
);
INSERT INTO tab_date
VALUES    (NOW(),NOW());
SELECT * FROM tab_date;
#system时间为+8:00东八区
SHOW VARIABLES LIKE 'time_zone';

SET time_zone = '+9:00';
SELECT DATE(NOW());
##常见约束
/*
分类:六大约束
    1,not null 保证该字段不为空
        如ID、姓名
    2,default默认 保证该字段有默认值
        如
    3,primary key主键 用于保证该字段有唯一性,且非空
        比如学号、编号
    4,unique唯一键 用于保证该字段具有唯一性,可以为空
        比如座位号
    5,check【mysql不支持】
        比如0<年龄<99
    6,foreign key外键 用于限制两表关联列在主表中该列的值
        比如:employees.department_id in departments.department_id

在创建表和修改表时,添加约束

分类:    列级约束 :(除外键)六大常见约束都支持,但是check无效果、foreign key无效果
    表级约束 :(三键)除了非空、默认都支持

主键和唯一键的PK
        唯一性    是否允许为空    一个表有几个    是否允许组合
    主键    √    ×        最多一个    √,但是不推荐
      唯一键    √    √(只能有一个空)    可以有多个    √,但是不推荐

  组合的意思是,让ID和name的组合如:ID_name唯一且不为空

  外键:1,要求在从表(stinf)设置外键
    2,从表(stinf)的外键列要求和主表(major)的类型一致或兼容
    3,主表(major)的关联列必须是一个key (一般为主键)
    4,插入数据时,先插主表(major),后从表(stinf)
    5,删除数据时,先删从表(stinf),后主表(major)***级联删除和级联置空

*/

#列级约束

CREATE DATABASE constrain;
CREATE TABLE stinf(
    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,
    mname VARCHAR(20)
);
DESC stinf;
SHOW INDEX FROM stinf; #查看主键、外键、唯一键

#表级约束
DROP TABLE IF EXISTS stinf;
CREATE TABLE IF NOT EXISTS stinf(
    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 IN ('男','女')),#核查
    CONSTRAINT fk_stinf_major FOREIGN KEY(majorID)
                    REFERENCES major(id)#外键
    #constriant用来给键起名字
);
SHOW INDEX FROM stinf; #查看主键、外键、唯一键
DROP TABLE IF EXISTS stinf;

CREATE TABLE IF NOT EXISTS stinf(
    id INT,
    stuname VARCHAR(20),
    gender CHAR(1),
    seat INT,
    age INT,
    majorid INT,
     PRIMARY KEY(id),#主键
     UNIQUE(seat),#唯一键
     CHECK(gender IN ('男','女')),#核查
     FOREIGN KEY(majorID) REFERENCES major(id)#外键

);
SHOW INDEX FROM stinf; #查看主键、外键、唯一键
#通用
DROP TABLE IF EXISTS stinf;

CREATE TABLE IF NOT EXISTS stinf(
    id INT PRIMARY KEY, #主键
    stuname VARCHAR(20) NOT NULL,#非空
    gender CHAR(1) CHECK(gender='男' OR  gender='女'),#核查
    seat INT UNIQUE,#唯一键
    age INT DEFAULT 18,#默认
    majorid INT,
    CONSTRAINT fk_stinf_major FOREIGN KEY(majorid)
                REFERENCES major(id)#外键
);
SHOW INDEX FROM stinf; #查看主键、外键、唯一键

##修改约束
/*


*/
DROP TABLE IF EXISTS stinf;

CREATE TABLE IF NOT EXISTS stinf(
    id INT,
    stuname VARCHAR(20),
    gender CHAR(1) NOT NULL,
    seat INT,
    age INT,
    majorid INT
);
#列级约束修改
ALTER TABLE stinf MODIFY COLUMN id INT PRIMARY KEY ;
ALTER TABLE stinf MODIFY COLUMN age INT DEFAULT 18;
DESC stinf;
SHOW INDEX FROM stinf;
#表级约束修改
#主键也可以这样去添加,但是不能添加constraint 限制名
ALTER TABLE stinf ADD CONSTRAINT u_seat UNIQUE(seat);
ALTER TABLE stinf
  ADD CONSTRAINT fk_major_stinf FOREIGN KEY (majorid) REFERENCES major (id);
DESC stinf;
SHOW INDEX FROM stinf;

##修改表时删除约束
#1删非空约束
ALTER TABLE stinf MODIFY COLUMN gender CHAR(1);
#2删默认约束
ALTER TABLE stinf MODIFY COLUMN age INT;
#3删主键
ALTER TABLE stinf DROP PRIMARY KEY;
#4删唯一键
ALTER TABLE stinf DROP INDEX seat;
#5删除外键
ALTER TABLE stinf DROP FOREIGN KEY fk_major_stinf;
SHOW INDEX FROM FROM stinf;

##级联删除:关于从表有外键时,删除数据biubiu先删主表,级联可以先删从表
#级联删除
ALTER TABLE student ADD CONSTRAINT fk_stu_maj
    FOREIGN KEY(majorid) REFERENCES major(majorid) ON DELETE CASCADE;
DELETE FROM major WHERE majorid=3;
#级联置空
ALTER TABLE student DROP FOREIGN KEY fk_stu_maj;
##无法设置成功!!??
ALTER TABLE student ADD CONSTRAINT fk_stu_maj
    FOREIGN KEY(majorid) REFERENCES major(majorid) ON DELETE SET NULL;
DELETE FROM major WHERE majorid=2;



#标识列
/*
1 必须搭配  键(只限mysql)
2 至多一个标识列
3 标识列类型只能是数值
4 步长设置通过SET auto_increment_increment = 2;
5 标识列初始值可通过手动添加

*/
DROP TABLE tab_auto;
CREATE TABLE tab_auto(
    id INT PRIMARY KEY AUTO_INCREMENT,
    NAME VARCHAR(20)
);

INSERT INTO tab_auto(id,NAME) VALUES(NULL,'john');
INSERT INTO tab_auto(NAME) VALUES('john');
INSERT INTO tab_auto VALUES(NULL,'john');
INSERT INTO tab_auto VALUES(5,'lily'); #相当于设置了起始值
INSERT INTO tab_auto VALUES(NULL,'john');

TRUNCATE TABLE tab_auto;

SELECT * FROM tab_auto;
SHOW VARIABLES LIKE '%auto_increment%';
SET auto_increment_increment = 2; #设置步长
SET auto_increment_offset = 3; #mysql无作用
#修改表时 添加删标识列
DROP TABLE tab_auto;
CREATE TABLE tab_auto(
    id INT,
    NAME VARCHAR(20)
);
ALTER TABLE tab_auto MODIFY id INT PRIMARY KEY AUTO_INCREMENT;
ALTER TABLE tab_auto MODIFY id INT;

##事务
/*分类:
    隐式事务:insert update delete
    显式事务:set autocommit=0;
          strat transaction;
          select insert update delete;
          ***** 无create alter drop ******
          commit;    /    rollback;
ACID
1 原子性atomicity:一个事务不可再分割,要么都执行要么都不执行
2 一致性consistency:一个事务执行会使数据从一个状态切换到另一个状态
3 隔离性isolation:一个事务的执行不受其他事务的影响
3 持久性durability:一个事务的提交会永久改变数据库的数据

三种数据问题:
    1脏读:T1 T2 ,T2更新没提交,T1读取,读取了临时无效的信息,若T2回滚
    2不可重复度:T1读取,T2更新,T1再读取则信息变化
    3幻读:T1读取,T2更新某些行,T1再读取,出现多行数据

事务隔离级别:
            脏读    不可重复读    幻读
read uncommitted    √    √        √
read committed        ×    √        √
repeatable read        ×    ×        √
serializable        ×    ×        ×

mysql默认repeatable read
Oracle默认read committed
*/
#查看当前隔离级别
SELECT @@tx_isolation;
#设置隔离级别
SET SESSION TRANSACTION ISOLATION LEVEL READ UNCOMMITTED;
SET GLOBAL TRANSACTION ISOLATION LEVEL READ UNCOMMITTED;

SELECT @@tx_isolation;
#事务中delete 和truncate 的区别

SET autocommit=0;
START TRANSACTION;
DELETE FROM stuinfo;
ROLLBACK;
SELECT * FROM stuinfo;

SET autocommit=0;
START TRANSACTION;
TRUNCATE stuinfo;
ROLLBACK;
SELECT * FROM stuinfo;
COMMIT;

#演示savepoint的使用
INSERT INTO stuinfo VALUES(1,1,'s','yb');
INSERT INTO stuinfo VALUES(2,2,'x','lz');
SELECT * FROM stuinfo;
COMMIT;

SET autocommit=0;
START TRANSACTION;
DELETE FROM stuinfo WHERE id=1;
SAVEPOINT a;
DELETE FROM stuinfo WHERE id=2;
ROLLBACK TO a;
SELECT * FROM stuinfo;
COMMIT;
##视图
/*
含义:虚拟表,和普通表一样
mysql5.1出现的新特性,是通过表动态生成的数据

比如:一个班里面 分化出一个舞蹈班,有需要时,舞蹈班就可以直接出现

视图和表的PK:
1 创建语法不通 view  table
2 视图只占sql语句的字节,表占空间
3 视图一般不能增删改


*/

#查询各部门平均工资级别
USE myemployees;
CREATE VIEW myv1
AS
SELECT AVG(salary) ag,department_id
FROM employees
GROUP BY department_id;

SELECT v.ag ,j.`grade_level` FROM myv1 v
JOIN job_grades j
ON v.`ag` BETWEEN j.`lowest_sal` AND j.`highest_sal`;

#查询平均工资最低的部门信息
SELECT d.department_name, m.department_id, m.ag
FROM myv1 m JOIN departments d
ON d.`department_id`=m.`department_id`;

#视图的修改
CREATE OR REPLACE VIEW myv1
AS
SELECT AVG(salary) AS average_salary,department_id
FROM employees
GROUP BY department_id;

ALTER VIEW myv1
AS
SELECT AVG(salary) ag,department_id
FROM employees
GROUP BY department_id;

#视图的删除
CREATE  VIEW myv2
AS
SELECT department_id
FROM employees
GROUP BY department_id;

DROP VIEW myv1, myv2;
#视图的查看
DESC myv2;
SHOW CREATE VIEW myv2;
SHOW CREATE VIEW myv2\G; #在 DOS中使用

#通过视图增删改原始表
/*
在视图包含以下情况不能insert update delete包含以下sql语句:
1 分组函数、instinct、group by 、having 、union、union all
2 常量视图
3 select包含子查询
4 join
5 from一个不能更新的视图
6 where子句的子查询引用了from子句的表

*/
##变量
/*
系统变量:全局变量、会话变量
自定义变量:用户变量、局部变量
*/

#一、系统变量、会话变量(一个查询编辑器一个会话)
/*
变量由系统提供,不是用户定义,属于服务器层面

注意:如果是全局变量,则需要加global,如果是会话级别,则需要加session,默认session

使用语法:
1,查看所有变量:
show global|session variables;

2,查看满足条件的部分系统变量
show global|session variable like '%char%';

3,查看某个指定变量的值
select @@global|session.变量名;
select @@global.flush;

4,为某个变量赋值(跨连接有效,不能跨重启)
set @@global|session 变量名=值;
set   global|session 变量名=值;

*/
#自定义变量
/*
声明、赋值、使用(查看、比较、运算)

        作用域        定义和使用的位置         语法
用户变量    当前会话    会话的任何地方            必须加@,不限制类型
局部变量    begin end中    只能在begin end中的第一句话    不加@,限定类型
*/
#1,用户变量 :  作用域:当前会话

#1 声明并初始化
SET @用户变量名=;
SET @用户变量名:=;
SELECT  @用户变量名:=;

#2 赋值(更新用户变量的值)
#方式一
SET @用户变量名=;
SET @用户变量名:=;
SELECT  @用户变量名:=;

#方式二
SELECT 字段 INTO @用户变量名
FROM 表名;

#使用
SELECT @用户变量名;

#案例
SET @count:=1;
SELECT COUNT(*) INTO @count FROM employees;
SELECT @count;

#2,局部变量
/*
作用域:只在定义它的begin 和 end 中有效

*/

#声明 ---------必须放在begin后面第一句----------------------
DECLARE 变量名 类型;
DECLARE 变量名 类型 【DEFAULT 值】;

#赋值
    #方式一
    SET 用户变量名=;
    SET 用户变量名:=;
    SELECT  @用户变量名:=;

    #方式二
    SELECT 字段 INTO 用户变量名 FROM 表名;

#使用
SELECT 局部变量;

##存储过程和函数
/*
好处:提高代码的重用性、简化操作
*/

#存储过程
/*
定义:一组预先编译好的SQL语句集合


一、创建语法
create procedure 存储过程名(参数列表)
begin
    存储过程体(一组合法的SQL语句)
end

注意:
1,参数列表包含三部分:
参数模式  参数名  参数类型
in         id       int

参数模式:
in :改参数可以作为输入,也就是该参数需要调用方 传入值
out  : 该参数可以作为输出,也就是该参数可以作为返回值
inout : 该参数既可以作为输出,又可以作为输入,即既需要传入值,也能返回值

2,如果存储过程体只有一句话, begin 和 end 可以省略
 存储过程体每条sql均需要结尾加;
 存储过程的结尾可以使用 delimiter 重新设置
 *********delimiter在新连接中要去dos重新设置*************
 语法: delimiter 结束标记
 案例: delimiter $

 二、调用语法
 Call 存储过程名(实参列表);
 */
 ##空参列表
 #案例:给girls.admin添加5条数据

DELIMITER $
CREATE PROCEDURE myp1()
BEGIN
    INSERT INTO admin(username,PASSWORD)
    VALUES('yb1',123),('yb2',123),('yb3',123),('yb4',123),('yb5',23);
END $

CALL myp1()$
SELECT * FROM admin$

##in列表
#案例1:创建存储过程实现:根据女神名查询对应的男生信息

CREATE PROCEDURE myp2(IN beautyname VARCHAR(20))
BEGIN
    SELECT bo.*
    FROM boys bo
    LEFT JOIN beauty b ON b.boyfriend_id=bo.id
    WHERE b.name=beautyname ;
END $

CALL myp2('热巴')$

#出现 Incorrect string value: '\xC8\xC8\xB0\xCD' for column,表示字符集不匹配
SET NAMES gbk$ #(sqlyog默认utf8, dos默认gbk)

#案例2:创建存储过程实现,用户是否登录成功

CREATE PROCEDURE myp3(IN username VARCHAR(20),IN PASSWORD VARCHAR(20))
BEGIN
    DECLARE result INT DEFAULT 0; #声明
    SELECT COUNT(*) INTO result   #赋值
    FROM admin ad
    WHERE ad.username=username AND ad.password = PASSWORD;

    SELECT IF(result=0,'失败','成功') "结果";  #使用

END $

##out列表
#案例:创建存储过程实现:根据女神名返回对应的男生名
CREATE PROCEDURE myp4(IN beautyname VARCHAR(20),OUT boyname VARCHAR(20))
BEGIN
    SELECT bo.boyname INTO boyname
    FROM boys bo  JOIN beauty b
    ON bo.id = b.boyfriend_id
    WHERE b.name=beautyname;
END $

CALL myp4('柳岩',@bname)$
SELECT @bname$

#案例:创建存储过程实现:根据女神名返回对应的男生名和魅力值
CREATE PROCEDURE myp5(IN beautyname VARCHAR(20),OUT boyname VARCHAR(20),
            OUT userCP VARCHAR(20))
BEGIN
    SELECT bo.boyname ,bo.usercp INTO boyname ,usercp  #赋值
    FROM boys bo  JOIN beauty b
    ON bo.id = b.boyfriend_id
    WHERE b.name=beautyname;
END $
/*
set @name='a'$
set @charm='b'$                           省略了声明
*/
CALL myp5('柳岩',@name,@charm)$                   #相当于省去了声明
SELECT @name,@charm$                       #调用

##inout列表
#案例:返回a,b值的二倍
CREATE PROCEDURE myp6(INOUT a INT,INOUT b INT)
BEGIN
    SET a=a*2;  #赋值
    SET b=b*2;
END $
SET @aa=1$          #声明
SET @bb=2$
CALL myp6(@aa,@bb)$
SELECT @aa,@bb$     #调用

##课后案例
#存储过程实现传入用户名密码,插入admin
CREATE PROCEDURE p_insert(IN username VARCHAR(20), IN PASSWORD VARCHAR(20))
BEGIN
    INSERT INTO admin(username,PASSWORD) VALUES(username,PASSWORD);
END $
CALL p_insert('yb','122103')$
SELECT * FROM admin$
#传入女神标号,返回姓名电话
CREATE PROCEDURE p_inf(IN id INT,OUT NAME VARCHAR(50), OUT phone VARCHAR(11))
BEGIN
    SELECT b.name,b.phone INTO NAME,phone  #注意要赋值
    FROM beauty b
    WHERE b.id=id;
END $
CALL p_inf(2,@bname,@bphone)$   #省去声明
SELECT @bname ,@bphone$        #调用

#传入两个女生生日日期,然后返回大小
CREATE PROCEDURE myp7(IN date1 DATE,IN date2 DATE,OUT result INT)
BEGIN
    SELECT DATEDIFF(date1,date2) INTO result;
END $
CALL myp7('1994-09-10',DATE(NOW()),@result)$
SELECT @result$

#传入一个日期类型的日期,输出为**年**月**日格式
CREATE PROCEDURE myp8(IN nor_date DATETIME,OUT for_date VARCHAR(20))
BEGIN
    SELECT DATE_FORMAT(nor_date,'%Y年%m月%d日') INTO for_date;
END $
CALL myp8(DATE(NOW()),@fd)$
SELECT @fd$

#输入 小昭,返回 小昭 and 张无忌
CREATE PROCEDURE myp9(IN beautyname VARCHAR(20),OUT information VARCHAR(50))
BEGIN
    SELECT CONCAT(beautyname,'and',IFNULL(bo.boyname,'null'))  INTO information
    FROM boys bo ,beauty b
    WHERE b.boyfriend_id=bo.id
    AND b.name=beautyname;
END $
CALL myp9('柳岩',@inf)$
SELECT @inf$

#传入条目数和起始索引,查询beauty表记录,
CREATE PROCEDURE myp10(IN offsett INT,IN increment INT)
BEGIN
    SELECT * FROM beauty
    LIMIT offsett,increment;
END$

CALL myp10(3,5)$#从第3条记录开始,显示5条


#删除储存过程
DROP PROCEDURE myp7; #只能一个一个的删
#查看储存过程
SHOW CREATE PROCEDURE myp6\G;#在dos执行


##函数
/*
定义:一组预先编译好的SQL语句集合

区别:存储过程:可以有0个返回,也可以有多个返回,适合做批量插入、更新
      函数:有且仅有1个返回值,适合做处理数据后返回一个结果
*/
/*
一、创建语法:
create function 函数名(参数列表) returns 返回类型
begin
    函数体;
    return ;
end $

注意:1参数列表 包含  (无参数模式) 参数名  参数类型
      2函数体肯定有return语句,如果没有会报错
      3函数体中仅有一句话,则可以省略begin和end
      4使用delimiter语句设置结束标记

二、调用语法

select 函数名(参数列表)
*/

#--------------------------函数案例演示------------------------
#1 无参有返回  案例 :返回公司的员工个数
USE myemployees;
CREATE FUNCTION myf1() RETURNS INT
BEGIN
    DECLARE c INT ; #声明
    SELECT COUNT(*) INTO c #赋值
    FROM employees;
    RETURN c; #调用
END $
SELECT myf1()$

#2有参有返回
#案例:根据员工名返回工资
CREATE FUNCTION myf2( ename VARCHAR(20)) RETURNS INT
BEGIN
    DECLARE s INT;
    SELECT salary INTO s
    FROM employees e
    WHERE e.last_name=ename;
    RETURN s;
END $
SELECT myf2('chen')$

#根据部门名返回该部门平均工资
CREATE FUNCTION myf3( dname VARCHAR(20)) RETURNS INT
BEGIN
    SET @s=0;
    SELECT AVG(e.salary) INTO @s
    FROM employees e JOIN departments d
    ON e.department_id = d.department_id
    WHERE d.department_name = dname;
    RETURN @s;
END $
SELECT myf3('IT')$

#案例 :传入两个float ,返回二者之和
CREATE FUNCTION mysum(a FLOAT ,b FLOAT) RETURNS FLOAT
BEGIN
    DECLARE absum FLOAT;
    SELECT a+b INTO absum;
    RETURN absum;
END $
#查看、删除函数
SHOW CREATE FUNCTION myf3\G; #dos
DROP FUNCTION myf3;

##流程控制 ——----------------------------------------------
/*
顺序、分支、循环
*/
#一、 分支
#if(条件,ture,false)
#case
/*
1、等值判断

    case 字段
    when 值1  then 返回值1
    when 值2  then 返回值2
    ...
    else 返回值3
    end

2、 条件判断
    case
    when 条件1  then  返回值1
    when 条件2  then  返回值2
    ...
    else 返回值3
    end
-----可以和select 、update 搭配,case返回的是 返回值1、返回值2...,位置不限------------


3、case可以单独使用,只能放在begin end中
    case 字段
    when 判断条件1  then 语句1;
    when 判断条件2  then 语句2;
    ...
    else 语句3;
    end case;
*/

#案例
CREATE PROCEDURE grade_level(IN grade INT)
BEGIN
    CASE
    WHEN  90< grade AND grade <= 100 THEN SELECT 'A' ;
    WHEN  80< grade AND grade <=90   THEN SELECT 'B' ;
    WHEN  70< grade AND grade <=80   THEN SELECT 'C' ;
    ELSE  SELECT 'D' ;
    END CASE;
END $
CALL grade_level(98)$

#if 多重分支,只能用在begin end中
/*
   if      条件1  then 语句1;
   elseif  条件2  then 语句2;
   ...
   ELSE 语句n;
   end if;
*/
#传入成绩,显示等级
CREATE FUNCTION grade_level( grade INT) RETURNS CHAR
BEGIN
    IF grade>90 AND grade <= 100 THEN RETURN 'A';
    ELSEIF grade >80 THEN RETURN 'B';
    ELSE RETURN 'c';
    END IF;
END$

##循环结构
/*
分类:while 、 loop 、 repeat

循环控制 :

iterate ,结束本次循环,继续进行下一次
leave  ,跳出,结束当前循环
出现循环控制时,必须给循环结构添加名称
*/

#1,while
/*
【标签】while 循环条件 do
    循环体
end while 【标签】;
*/

#2,loop
/*
【标签】loop
    循环体
end loop 【标签】;

可以模拟简单的死循环

*/

#3,repeat
/*
【标签】repreat
    循环体
until 结束循环体的条件
end repeat 【标签】;
*/

/*
loop一般实现简单死循环
while先判断后执行
repeat先执行后判断,无条件至少执行一次
*/
#案例,根据次数插入到admin表中多条记录
CREATE PROCEDURE p_while1(IN insertcount INT)
BEGIN
    DECLARE i INT DEFAULT 1;
    WHILE i<insertcount DO
        INSERT INTO admin(`username`,`password`)
            VALUES(CONCAT('rose',i),'123');
        SET i=i+1;
    END WHILE ;
END$
CALL p_while1(50)$

#案例,根据次数插入到admin表中多条记录,到20次就停止
CREATE PROCEDURE p_while2(IN insertcount INT)
BEGIN
    SET @i=1;
    a:WHILE @i<insertcount DO
        INSERT INTO admin(`username`,`password`)
            VALUES(CONCAT('xiaohu',@i),'0000');
        IF @i>=20  THEN LEAVE a;
        END IF ;
        SET @i=@i+1;
    END WHILE a;
END$
#案例,根据次数插入到admin表中多条记录,结尾为偶数

CREATE PROCEDURE p_while3(IN insertcount INT)
BEGIN
    SET @i=0;
    a:WHILE @i<insertcount DO
        INSERT INTO admin(`username`,`password`)
            VALUES(CONCAT('xiaohu',@i),'0000');
        SET @i=@i+2;
    END WHILE a;
END$

CREATE PROCEDURE p_while4(IN insertcount INT)
BEGIN
    SET @i=1;
    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('ts',@i),'6666');
    END WHILE a ;
END $

####高级sql
/*GA generally available;

##字符集
delimiter ;
show variables like '%char%';

#插件式的存储引擎,将查询处理和其他的系统任务以及数据的存储提取相分离

mysql 分四层
    1连接层:与其他语言的链接,如Perl、Python
    2服务层:mysql查询,内部优化
    3引擎层:可拔插的引擎,innoDB、myISAM
    4存储层:硬件

#查看引擎
show engines;
show variables like '%engine%';

#sql性能下降的原因
    1查询语句写的烂
    2索引失效(单值、复合)
    3关联查询join太多
    4服务器调优及各个参数设置(缓冲、线程数)


*/
##索引 :是帮助提高mysql高效获取数据的【【 数据结构】】
 /*
*********排好序的快速查找数据结构*************

数据本身之外,数据库还维护着一个满足特定查找算法的【数据结构】
这些数据结构以某种方式指向数据,这样就可以在这些数据的基础上实现
高级查找算法,这种数据结构就是索引。

索引往往以索引文件的形式存储在磁盘上

索引往往指的是B数(多路搜索树)结构组织的索引

聚集索引、全文索引、复合索引、前缀索引、唯一索引默认都是使用B+树索引,统称索引。

出了B+树类型,还有哈希索引(hash index)

索引的优劣
优势:    提高数据检索的效率,降低数据库IO的成本(input and output)
    降低数据排序成本,降低了CPU的消耗
劣势:    提高查询速度的同时,降低了更新表的速度(增删改时,索引也会发生变化)
    数据量较大,需要花时间研究建立最优秀的索引

索引分类:
单值索引:一个索引只包含单个列,一个表可以有多个单列索引(银行查卡号,ID卡等)
      一个表最好不超5个单列索引
唯一索引:索引列的值必须唯一,但允许有空值
复合索引:一个索引包含多个列
全文索引:


##索引的结构:类似于二叉树一样
树的宽度又数据量决定,查询的速度由树的高度决定。
判断次数(IO次数)==树的高度

##什么情况适合建索引
1主键自动建立唯一索引
2频繁作为查询条件的字段
3与其他表关联字段,如外键 应该建立索引
4频繁更新的字段不适合建索引
5where中用不到的字段不建索引
6尽量组合索引
7查询中排序的字段(若排序的字段多个,复合索引按照排序中的多个字段顺序建立)
8统计或分组字段(group by和索引有关)
##什么情况不适合建索引
1表字段太少(3百万以下)
2经常增删改的表
3数据列包含太多重复数据(国籍)


##性能查询
用法 :explain sql语句;
作用 : 1表的读取顺序            id
    2数据读取操作的操作类型        select_type
    3哪些索引可以使用
    4是哪索引被实际使用
    5表之间的引用
    6每张表有多少行被优化器查询

    id、select_type、table、type、possible_key、key_len、ref、rows、extra

id :id相同时,table从上往下执行;存在子查询时,id不同,id值越大,越先执行。
    表子查询的select_type值为derived;
select_type:SIMPLE \ PRIMARY \ SUBQUERY \ DERIVED \ UNION \ UNION RESULT
    SIMPLE : 不包含子查询和union
    PRIMARY:主查询,最外层 ,最后执行
    SUBQUERY:子查询
    DERIVED:表子查询
    UNION:union后面的表的s_t为union
    UNION RESULT:从union表获取结果的select
table:    显示这一张行数据是关于哪张表的
type: All \ index \ range \ ref \ eq_ref \ const,system \ Null
    显示查询使用了何种类型,由好到坏依次为:
    system>const>eq_ref>ref>range>index>All****************
    -----------------------------------------------------------------
    system:系统表,只有一行数据,const的特例,忽视掉
    const:表示通过索引一次就找到了,用于比较primary和unique索引
        因为只匹配一行数据,如where查主键的一个值
    eq_ref:唯一性索引扫描,对于每一个索引键,表中只有一条记录与之匹配
        explain select * from e ,d where e.id=d.id;
        #e.id的每个值在d.id中只出现一次
???ref:非唯一性索引扫描,返回匹配某个单独值的所有行(符合条件的多行)
        explain select * from employees where job_id='AD_VP';
    range:只检索给定范围的行使用一个索引来选择行,可以列显示使用哪个索引
        between and >  <  in
        explain select * from employees where `employee_id` between 100 and 105;
    index:full index scan.与all的区别在于index只遍历索引数
        explain select `employee_id` from employees;
    full:遍历全表

********一般要求出现All以后的百万级查询,必须优化。一般到range,更好为ref

possible_keys:显示可能应用在这张表的索引,一个或者多个(但不一定被查询实际使用)
key:实际使用的索引 【查询中若使用了覆盖索引,则该索引仅出现在key列表中】
    【覆盖索引:用c1、c2建索引,查询为select c1,c2 from ..;】
key_len:索引中使用的字节数,可通过该值计算  查询中使用的索引的长度。在不损失精度
     的情况下,【长度越短越好】。该值为索引字段的最大可能长度,【并非实际使用长度】
???     即key_len是根据表计算而得,不是通过表内检索出来的
ref:显示索引的哪一列被使用了,如果可能的话,【是一个常数】
rows:根据 表统计信息 及索引选用情况,大致估算出找到所需记录要读取的行数,越小越好
extract:包含不合适在其他列中显示但十分重要的额外信息;
       #using filesort:没有用索引进行排序,使用了一个外部索引【九死一生】
     假设假设索引为ind_t_a_c_b。
     select * from table t where t.a='*' order by t.c;则为using filesort
     select * from table t where t.a='*' order by t.b;则不为using filesort
     select * from table t where t.a='*' order by t.b,t.c;也不为using filesort
       #using temporary 使用了临时表保存中间结果,MYsql对查询结果排序时使用临时表
        常见于order by 和 group by【十死无生】
       #using index 查询中使用了覆盖索引,【效率不错】
      同时出现了using where 表明索引被用来执行索引键值的查找(where 后面的列被用于建索引)
          没出现using where表明索引中的列只用来select,没进行where
       #using where 使用了where
       #using join buffer 使用了连接缓存,配置文件里的using buffer调大
       #impossible  where :where子句的值总是false,不能用来获取任何元组
       #selec table optimized away:在没有group by子句的情况下,基于索引优化min/max
       #distinct:优化distinct操作,在找到第一匹配的元组后即停止找同样的动作


 */
EXPLAIN
SELECT id FROM(
SELECT a.id FROM admin a
 UNION
 SELECT b.id FROM beauty b) c ;
 EXPLAIN
 SELECT a.id FROM admin a
 UNION
 SELECT b.id FROM beauty b;
 EXPLAIN SELECT * FROM admin ORDER BY PASSWORD;

/*
索引优化

*/
##案例1 单表索引优化
#category为1,comments>1,views最多的article_id
USE test;
CREATE TABLE IF NOT EXISTS article(
    id INT(10)         UNSIGNED   NOT NULL    PRIMARY KEY    AUTO_INCREMENT,
    author_id INT(10)       UNSIGNED   NOT NULL,
    categoryid 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`,`categoryid`,`views`,`comments`,`title`,`content`)
VALUES
    (1,1,1,1,'1','1'),
    (2,2,2,2,'2','2'),
    (3,3,3,3,'3','3'),
    (4,4,4,4,'4','4');
#情况一:无索引。!type为all,extra提示using filesort
EXPLAIN  SELECT * FROM article WHERE categoryid =1 AND comments >1
                   ORDER BY views DESC LIMIT 1;
#情况二:按序建索引ccv.用到了索引,type为range,但是仍使用了文件内排序
#原因为comments为范围查询,sql无法利用索引再对后面的views进行检索。
#即range类型查询字段后面的索引无效
CREATE INDEX inx_article_ccv ON article(categoryid,comments,views);
EXPLAIN  SELECT * FROM article WHERE categoryid =1 AND comments >1
                   ORDER BY views DESC LIMIT 1;
#情况三:符合索引ca_v,type为ref,无文件内排序
DROP INDEX inx_article_ccv ON article;
CREATE INDEX inx_article_cv ON article(categoryid,views);
EXPLAIN  SELECT * FROM article WHERE categoryid =1 AND comments >1
                   ORDER BY views DESC LIMIT 1;
##案例2:两表索引优化,左拼给右表加索引。
USE myemployees;
EXPLAIN SELECT * FROM employees e LEFT JOIN departments d
ON e.`department_id`=d.`department_id`;
SHOW INDEX FROM `departments`;
##案例3:三表连接,用小表驱动大表,索引建立在left 或 right 后面的


##索引失效的原因:
/*
1全职匹配我的最爱

2最佳左前缀法则********
    复合索引要遵守该法则,查询应该从索引的最左前列开始,并且不跳过索引的列
    create index stu_1_2_3 on student(`studentno`,`studentname`,`loginpwd`);
    explain select * from student where studentno='S001' and studentname = '张三封' and `loginpwd`='8888';
    explain select * from student where studentno='S001' and loginpwd = '8888';
    explain select * from student where  loginpwd = '8888';##第一个字段不能丢失

3不在索引列上做任何操作(计算、函数、(自动或者手动)的类型转换),导致索引失效,全表扫描

4储存引擎不能使用索引中范围条件右边的列
create index table.a_b_c on table(a,b,c)
select * from table where a='' and b>X and c=''; #用不到c,但是
select * from table where a='' and b like 'xx%' and c='';#能用到c

5尽量使用覆盖索引

6mysql在使用非等于时(> < !=),无法使用索引,全表扫描

7is null ,is not null 也无法使用索引

8like以通配符开始,mysql无法使用索引,全表扫描,
    比如select * ...like '%xx'不推荐,而like 'xx%'则使用了索引,type为range
    若使用like '%xx%',需要使用覆盖索引,
    即索引包含name时,select name ... like '%xx%';
    #select使用*或者非索引列字段也不行

    like 'kk%',虽然也是range,但是与>x 不同,
    like后面的等值条件还能用(显示在len上), >x后面的则通通失效

9字符串不加单引号,mysql无法使用索引,全表扫描

10少用or,用or连接时索引会失效

总结:

列只和建索引的书写顺序有关:比如index 123,where 1='' and 2='' and 3=''
                    ===where 2='' and 3='' and 1=''
                where 1='' and 2> and 3=
                ==where 1='' and 3= and 2>
where有order,group时  :where 1= and 3= order by 2
            ==where 1= and order by 2
            ≈where 1= order by 2,3
            但是 where 1= order by 3,2 (出现内排序,group出现temporary)
            但是where 1= and 2= order by 3,2无内排序,因为2定值了

            但是 where 1= and 2= order by 3
            !==where 1= order by 3(出现内排序using filesort)

范围查询与order by结合
index 12
where 1> order by 1 优
where 1> order by 1,2 优
where 1= order by 2 优
where 1> order by 2  内排序 (理解索引的内涵)
where 1> order by 2,1 内排序 (排好序  的快速查找数据结构)
order by a acs,b desc,内排序(order by 默认升序,同升同降无内排序)

group by :同order by,但能用where就不用having

提高order by 的方法:
1 使用order by大忌使用select * ,应该只查询需要的字段
  1.1当查询的字段的大小总和<max_length_for_sort_data而且排序字段不是
    text/BLOB时,会使用改进算法--单路排序,否则使用对路排序,速度慢
  1.2单路多路都可能超过sort_buffer的容量(单路可能性更大),超出后会创建temporary,导致慢速

2尝试提高sort_buffer_size
3尝试提高max_length_for_sort_data,但是设置太高,容易超过sort_buffer_size



口诀:
全职匹配我最爱,最左点缀要遵守
带头大哥不能死,中间兄弟不能断
索引列上少计算,范围之后全失效
like百分写最右,覆盖索引不写星
不等空值还有or,索引失效要少用
var引号不可丢 ,SQL高级也不难
*/

/*查询截取分析

1至少跑一天,观察,看看生产的慢sql情况

2开启慢查询日志,设置阈值:比如超过5秒的就是慢sql,并将其抓取出来

3explain+慢sql分析

4show profile

5运维经理或DBA进行数据库服务器的参数调优

总结:     1慢查询的开启并捕获
    2explain+慢sql分析
    3show profile查询sql在MySQL服务器里面的执行细节和生命周期情况
    4sql数据服务器的参数调优

理解小表驱动大表:

A表数量>>B表
当:【select * from a where a.id in (select id from b)】as WAYIN
    的执行顺序为:
    1 select id from b
    2 select * from a where a.id=b.id
此时的执行效率 in> exists

A表数量<<B表
当:【select * from a where exists (select 1 from b where a.id =b.id)】as WAYEX
    的执行顺序为
    1 select * from a
    2 select id from a.id=b.id
此时的执行效率 exists>in

【exists语法为:
exists子查询中会忽略的select后面的查询列表,所以可以写1 ,'x'等常量值

    1 先查出主查询的全部数据(小表),
    2 然后根据子查询(大表)得到的布尔向量决定小表数据的去留】

***总结:小表驱动大表时,即A表<<B表,使用WAYEX。

*/

/*慢查询日志
show variables like '%slow_query%';

set global slow_query_log =1 ;#开启慢查询日志

show variables like '%long_query_time%';#默认时间>10s为慢sql

set global long_query_time=3;#设置阈值为3,查看该值需在新会话查看,非新查询编辑器
set global slow_query_log_file = 'slow.log';#设置慢查日志的文件位置

select sleep(4); #若执行超过阈值的sql会在慢查询中显示

show global status like '%show_queries%';#显示当前系统中较慢的sql 条数

*/

/*show profile
show variables like '%profiling%';

set profiling = on;

show profiles;

show profile cpu, block io for query 17;#一条sql内部执行的完整生命周期

#如果status出现以下条目,降低速度
1 coverting HEAP to MyISAM :查询结果太大,内存不够,往磁盘上搬了
2 creating tpm table :创建临时表,[拷贝数据到临时表][用完再删除]
3 cooying to tmp table on disk :把内存中临时表复制到磁盘。危险!!
4 locked
*/

/*全局查询日志:#不要在生产环境开启该功能

set global general_log ;
set global log_output='TABLE';
select * from  my.general_log;

*/

/*数据库锁理论
锁是计算机协调多个进程或线程并发访问某一资源的机制

在数据库锁中,除传统的计算资源(CPU,RAM,IO)的征用以外,数据也是一种供许多用户共享的资源。
如何保证数据并发访问的一致性、有效性是所有数据库必须解决的一个问题,锁冲突也是影响数据库
并发访问性能的一个重要因素。从这个角度来说,锁对数据库而言显得尤其重要,也更加复杂。

表锁分类:
#从对数据操作的类型:
        1读锁(共享锁):针对同一份数据,多个读操作可以同时进行,不会互相影响
            (会话1给表1上读锁,能读表1,不能改表1,不能读表2)
            (会话2         能读表1,改表1阻塞,能读表2)
        2写锁(排它锁):当前写锁没有完成之前,它会阻断其他写锁和读锁
            (会话1给表1上写锁,能读表1, 能改表1, 不能读表2)
            (会话2能读表2,读表1发生阻塞,改表1更慢)
        总结:读锁阻塞改表,写锁阻塞读写
show open tables; #显示没上锁的表
lock table 表1 read, 表2 write; #表1读锁,表2写锁
unlock tables; #解锁所有的表
show status like 'table%';

行锁:只在事务中,对行增删改时,导致某一行锁定,另一会话阻塞增删改
行锁变表锁:varchar类型的 值为数字的 字段,没加引号,导致该列都被行锁,变成表锁

set autocommit=0;
select * from table where id=x for update;#强制锁定一行
commit;#直到会话1结束,会话2才能读、写
*/
  • 1
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值