zoukankan      html  css  js  c++  java
  • mysql

    #开启关闭服务
    #管理员启动cmd,,net start/stop mysql,,
    #登录数据库,,mysql 【-h localhost -P 端口号】 -u 用户名 -p,,
    #退出数据库,,exit,, / #ctrl+c
    #查看版本号,,mysql -V,,
    ##常见语句
    #查看当前所有数据库
    SHOW DATABASES;
    #打开指定的库,,use 库名;
    #查看某库所有的表,,show tables; // show tables from 库名;
    /*创建表,, create table(
    字段名称 字段类型,
    字段名称 字段类型
    );*/
    #查看表结构,,desc 表名;
    #修改字符集 set names gbk;
    #查看表内容,,select * from 表名;
    #插入内容,,insert into 表名 (id , name) values (1,‘yangbo’);
    #更新修改内容,,update 表名 set name='yb' where id=1;
    #删除内容,,delete from 表名 where id=1;
    ##不区分大小写
    ##单行注释 #注释 -- 注释
    ##多行注释 /* 注释 */
    ###data query language/ data manipulation lan/
    ### data define lan/transcation control lan/
    ###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 AS 名 FROM 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 myv2G; #在 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: 'xC8xC8xB0xCD' 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 myp6G;#在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 myf3G; #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才能读、写
    */
    DELIMITER ;

  • 相关阅读:
    java语法基础
    HashMap中的put()和get()的实现原理
    理解:o(1), o(n), o(logn), o(nlogn) 时间复杂度
    mongodb去重分页查询支持排序
    elk日志分析系统搭建(window )亲自搭建
    IDEA更改主题插件——Material Theme UI
    css实现图片的瀑布流且右上角有计数
    C# string "yyMMdd" 转DataTime
    Vue.js系列(一):Vue项目创建详解
    VS2017常用快捷键
  • 原文地址:https://www.cnblogs.com/xl-892694298/p/14938870.html
Copyright © 2011-2022 走看看