SELECT {* | [distinct] <字段列名>}
FROM <表 1>, <表 2>… WHERE <表达式>
GROUP BY <group by definition>
HAVING <表达式>
ORDER BY <字段列名> [{ ASC | DESC }]
LIMIT[<偏移量>,] <行数> -- 从0开始
(8) SELECT (9) DISTINCT (11) <TOP_specification> <select_list>
(1) FROM <left_table>
(3) <join_type> JOIN <right_table>
(2) ON <join_condition>
(4) WHERE <where_condition>
(5) GROUP BY <group_by_list>
(6) WITH {CUBE | ROLLUP}
(7) HAVING <having_condition>
(10) ORDER BY <order_by_list>
-- 找出部门员工总数大于等于5,且平均薪水大于$2000的员工数据,显示部门代码、部门员工数、部门平均薪水数据
select DEPARTMENT_ID, COUNT(*) count, AVG(SALARY) avg
FROM employees
GROUP BY DEPARTMENT_ID
HAVING count >= 5 and avg > 2000;
with rollup 可以为 GROUP BY 运行结果的每一个分组返回一个统计行,并且为所有分组返回一个总的统计行。
加上 ROLLUP 子句后,会在每个分组后面加上一行统计值,其中统计行高位字段显示为 NULL
test表:

select age, COUNT(*)
FROM test
GROUP BY age
with rollup
运行结果:

select IF(grouping(age)=1, '=total=', age) age, IF(grouping(nam)=1, '=total=', nam) name,
grouping(age), grouping(nam), COUNT(*)
FROM test
GROUP BY age, nam
with rollup
运行结果:

在 GROUPING() 函数包含多个参数时,按照以下方式来返回结果:
GROUPING(r1,r2) 等价于 GROUPING(r2) + GROUPING(r1)
GROUPING(r1,r2,r3,…) 等价于 GROUPING(r3) + GROUPING(r2)d
以此类推
test表

test1表

外连接包括左连接、右连接、全连接
左连接
select * from test LEFT JOIN test1 ON test.age = test1.agee;
运行结果:

SELECT * FROM test LEFT JOIN test1 on test.id = test1.id
UNION -- union 会删除重复行,union all 不去重
SELECT * FROM test RIGHT JOIN test1 on test.id = test1.id
内连接分为等值连接和不等连接:
-- 等值连接示例
select * from test inner JOIN test1 ON test.age = test1.agee; -- inner join 可以省略inner
-- 或者
select * FROM test, test1 where test.age = test1.agee;
运行结果:

select * from table1 join table2 using(id);
-- 等价于
select * from table1 join table2 on table1.id = table2.id;
select * from test natural join test1
on条件是在生成临时表时使用的条件。
where条件是在临时表生成好后,再对临时表进行过滤的条件。
-- 找出属于部门代号10或20且薪水大于该部门平均薪水的员工
SELECT EMPLOYEE_ID, DEPARTMENT_ID, SALARY
FROM employees e1
where DEPARTMENT_ID in (10, 20)
and SALARY >
(
SELECT AVG(SALARY) avg
FROM employees e2
WHERE e1.DEPARTMENT_ID = e2.DEPARTMENT_ID
GROUP BY DEPARTMENT_ID
);
-- 使用子查询于Select子句,找出员工数据及其主管名字(主管id和员工id都在employees表)
SELECT EMPLOYEE_ID,
(
SELECT CONCAT(FIRST_NAME,LAST_NAME) FROM employees e2
WHERE e1.MANAGER_ID = e2.EMPLOYEE_ID
) manager_name
FROM employees e1;
-- 找出薪资低于部门代码为10中最低薪员工的员工数据
select EMPLOYEE_ID, SALARY
from employees
WHERE SALARY < ALL -- 低于最低的,即比所有人都低
(
SELECT SALARY
FROM employees
WHERE DEPARTMENT_ID = 10
);
-- 由employees表格中,找出为主管的数据
SELECT * FROM employees m
WHERE EXISTS -- 存在一个员工的managerID是该员工的employeeID,则为主管
(
SELECT * FROM employees
WHERE m.EMPLOYEE_ID = employees.MANAGER_ID
);
对于exists,子查询结果集有记录为true,无记录为false。
distinct 依赖的字段全部包含索引时,mysql直接通过操作索引对满足条件的数据进行分组。
distinct 依赖的字段未全部包含索引时,mysql在临时表中对该部分数据进行分组,在临时表中进行分组的过程中不会对数据进行排序。
select * from table where name like '张%'
在oracle中,为满足select * from table的语法规范而引入的虚拟表,用来表示select 1 from dual,但是mysql可以直接select 1。
窗口函数(开窗函数),也叫OLAP函数,(Online Analytical Processing)实时分析处理函数。
适用于对分组统计结果中的每一条记录进行计算的场景,具体有排名问题和 TopN问题。
窗口,即运算范围,具体指数据表中参与运算的那些行,也是窗口函数的作用范围。
分组操作会把分组的结果聚合成一条记录,而窗口函数是将分组的结果置于每一条数据记录中。
聚合函数可以用于窗口函数中。
窗口函数和聚合函数的区别:
窗口函数可以分为静态窗口函数和动态窗口函数
窗口函数基本语法:
-- 窗口函数一般放在 select 的子句中
func_name(<parameter>) OVER
(
[partition by <part_by_condition>]
[order by <order_by_list> asc|desc]
)
func_name(<parameter>) OVER win_name
...
window win_name as
(
[partition by <part_by_condition>]
[order by <order_by_list> asc|desc]
[rows 行范围关键字| between 行范围关键字 and 行范围关键字 ] -- 不用between and 则取当前行和指定行之间
)
-- over 关键字用于指定函数的窗口范围
-- over后内容为空,则窗口会包含满足WHERE条件的所有记录,然后窗口函数基于这些记录进行计算。
-- partition by + 字段 根据此字段将数据集分组, 然后窗口函数在每个分组中分别执行。
-- order by + 字段 每个窗口的数据依据此字段进行排序,使窗口函数按照排序后的顺序进行编号。
常用行范围关键字:
实例:
SELECT *,SUM(sales) over(ORDER BY `month`) as 累计利润
from chh_baozipu where product='猪肉大葱包子';
SELECT *,SUM(sales) OVER win as '近三个月利润相加'
FROM chh_baozipu
WINDOW win as (PARTITION BY product ORDER BY `month` ROWS 2 PRECEDING);
SELECT *,SUM(sales) OVER win as '前一个月到下一个月利润相加'
FROM chh_baozipu
WINDOW win as (PARTITION BY product ORDER BY `month` ROWS BETWEEN 1 PRECEDING AND 1 FOLLOWING);
select age,
ROW_NUMBER() over win as _row_number,
RANK() over win as _rank,
DENSE_RANK() over win as _dense_rank,
NTILE(3) over win as _ntile
FROM test
window win as (ORDER BY age);
执行结果:

SELECT last_name, job_id, salary,
CASE job_id
WHEN 'IT_PROG' THEN 1.10*salary
WHEN 'ST_CLERK' THEN 1.15*salary
WHEN 'SA_REP' THEN 1.20*salary
ELSE salary
END
as "REVISED_SALARY"
FROM employees;
-- insert
INSERT INTO 表名 (列名1,列名2,...) VALUES(值1,值2,...)
INSERT INTO 表名 (select * from table1)
-- update
UPDATE 表名 SET 列 = 新值,列 = 新值,... WHERE 筛选条件
-- 多字段update
UPDATE t_emp e,t_dept d -- UPDATE 表1,表2 SET 字段1=值1,字段2=值2,...... WHERE 连接条件
SET e.deptno=d.deptno,e.job="ANALYST",d.loc="GUANGZHOU"
WHERE e.ename="ALLEN" AND d.dname="RESEARCH";
-- 工作职称为'CLARK'的员工,薪水修正为所有员工的平均薪水
UPDATE employees set SALARY =
(
SELECT * FROM -- 加一个外层查询,目的是屏蔽employees表
(
SELECT AVG(SALARY) FROM employees
) a
)
WHERE JOB_ID in
(
SELECT JOB_ID FROM jobs
WHERE JOB_TITLE like '%clerk%'
);
-- delete
DELETE FROM 表名 WHERE 筛选条件
-- 创建表
create table emp
(
empno DECIMAL, -- empno DECIMAL [primary key], 主键约束
ename VARCHAR(120),
hiredate datetime DEFAULT NOW() COMMENT '入职日期'
-- [, primary key(empno)] 主键约束
);
-- 复制表
CREATE TABLE emp_temp LIKE emp; -- 参照emp表结构创建一个表
insert into emp_temp SELECT * FROM emp; -- 数据拷贝
-- 删除表
drop table emp;
-- 创建视图
CREATE VIEW emp_v1 as SELECT * FROM employees;
-- 删除视图
drop view emp_v1;
-- 添加字段
alter table emp_temp
add COLUMN gender VARCHAR(2);
-- 修改字段
alter table emp_temp
MODIFY column gender VARCHAR(10) DEFAULT NULL COMMENT '性别';
ALTER TABLE stu_info_copy
CHANGE old_name new_name varchar(50) DEFAULT '' COMMENT '用户名'; -- 可改列名,可删除自增长
-- 删除字段
alter table emp_temp
DROP COLUMN gender;
-- 表重命名
alter table emp_temp rename emp_temp2;
rename table emp_temp to emp_temp2;
-- 添加主键约束
alter table emp_temp2 add PRIMARY KEY emp_pk(empno);
alter table emp_temp2 add PRIMARY KEY (empno); -- 主键约束名称可有可无
-- 删除主键约束
alter table emp_temp2 drop PRIMARY key;
-- 添加索引
ALTER TABLE table_name ADD INDEX index_name (Att_name_1);
CREATE INDEX index_name ON table_name (Att_name);
-- 删除索引
alter table table_name drop index index_name;
DROP INDEX index_name ON table_name
-- 创建用户
create user '用户名'@'IP地址' identified WITH mysql_native_password by '密码';
flush privileges;
-- 删除用户
# 一个用户可能会有多个IP地址
drop user '用户名'@'IP地址';
-- 授权
grant 权限1, 权限2, 权限3,… ,权限n on 数据库名.表名 to 用户名@地址 [WITH GRANT OPTION];
-- 把mysql数据库的user表的(查询,插入,更新,删除)的权限都给alian,并且是任意ip地址都可以操作
grant SELECT, INSERT, UPDATE, DELETE on mysql.user to 'alian'@'%';
flush privileges;
-- 授予数据库db1的所有权限给指定账户
GRANT ALL ON db1.* TO 'user1'@'localhost';
-- 授予角色给指定的账户
GRANT 'role1', 'role2' TO 'user1'@'localhost', 'user2'@'localhost';
FLUSH PRIVILEGES;
-- 查看权限
show grants for 'alian'@'%';
-- 收回权限
revoke 权限1, 权限2…权限n on 数据库名.表名 from 用户名@地址;
-- 回收用户的更新和删除mysql(默认的库)数据库的权限
revoke update,delete on mysql.user from 'alian'@'%';
-- 事务提交与回滚
BEGIN TRANSACTION # 事务开始
SQL1
SQL2
COMMIT # 事务提交
BEGIN TRANSACTION # 事务开始
SQL1
SQL2
ROLLBACK # 事务回滚
-- 保存节点
savepoint 结点名; # 设置保存点,并和rollback结合使用,实现回滚到指定保存点
SQL1
SQL2
rollback to 结点名; # 回滚到指定点
| 函数 | 结果 | 描述 |
|---|---|---|
| lower(‘SQL Course’) | sql course | 小写表示 |
| upper(‘SQL Course’) | SQL COURSE | 大写表示 |
| concat(‘Hello’, ‘World’) | HelloWorld | 连接字符串 |
| substr(‘helloworld’, 2, 5) | ellow | 获取子串 |
| left(‘helloworld’, 2) | he | 从左开始获取子串 |
| right(‘helloworld’, 2) | ld | 从右开始获取子串 |
| length(‘helloworld’) | 10 | 长度 |
| instr(‘helloworld’, ‘world’) | 6 | 获取字串首次出现位置,找不到返回0 |
| locate(‘AB’, ‘12ABAB’) | 3 | 获取字串首次出现位置,找不到返回0 |
| locate(‘AB’, ‘12AB3AB’, 5) | 6 | 从第5个开始找,获取字串首次出现位置,找不到返回0 |
| lpad(salary, 10, ‘*’) | *****24111 | 左补齐 |
| rpad(salary, 10, ‘*’) | 24231***** | 右补齐 |
| trim(‘h’ from ‘helloworld’) | elloworld | 剪切字符串 |
| replace(‘abcd’, ‘b’, ‘m’) | amcd | 替换字符 |
| 函数 | 结果 | 描述 |
|---|---|---|
| round(45.926, 2) | 45.93 | 四舍五入 |
| truncate(45.926, 2) | 45.92 | 低位截断 |
| mod(16, 3) | 1 | 取余 |
| ceil(1.2) | 2 | 向上取整 |
| floor(1.7) | 1 | 向下取整 |
| sign(4) | 1 | 符号函数 |
| 函数 | 结果 | 描述 |
|---|---|---|
| now() | 2022-07-22 17:11:24 | 获取当前日期和时间 |
| SYSDATE() | 2022-07-22 17:11:24 | 获取当前日期和时间 |
| CURDATE() | 2022-07-22 | 获取当前日期 |
| CURTIME() | 17:11:24 | 获取当前时间 |
| datediff(date_end,date_start) | 获取两个日期之间的天数 | |
| timestampdiff(unit,begin,end) | 获取两个日期之间的时间 | |
| str_to_date(‘9-13-1999’, ‘%m-%d-%Y’) | 1999-09-13 | 字符串转日期类型 |
| date_format(‘2018/6/6’, ‘%Y年%m月%d日’) | 2018年06月06日 | 日期格式化 |
| date_add(date_start,INTERVAL 15 year) | 在一个日期上增加一段时间 | |
| date_sub(date_start,INTERVAL 15 year) | 在一个日期上减去一段时间 | |
| last_day(date_1) | 获取该月最后一天的日期 | |
| day(now()) | 获取日期的天 | |
| month(now()) | 获取日期的月 |
SELECT str_to_date(DATE_FORMAT(NOW(),'%Y-%m-%d'),'%Y-%m-%d %H:%i:%s');
select DATE_SUB(DATE_ADD(str_to_date(DATE_FORMAT(NOW(),'%Y-%m-%d'),'%Y-%m-%d %H:%i:%s'),INTERVAL 1 DAY),INTERVAL 1 SECOND);
count(*)查询的是用户表中的所有记录,不论字段中是否为null,都计算在内;
count(列名)查询的是用户表中所有不为null的记录,不包括字段中为Null的数据;
count(一个数)查询的是用户表中所有记录,不论字段中是否为null,都计算在内;
count(DISTINCT c_id)
| 函数 | 结果 | 描述 |
|---|---|---|
| ifnull(NULL,‘IFNULL function’) | IFNULL function | 第一个参数为null则返回第二个参数,否则返回第一个参数 |
| coalesce(value, …) | 返回第一个不为NULL的参数,如果所有参数都为NULL,此函数返回NULL | |
| if(expr1,expr2,expr3) | 如果 expr1 是TRUE (expr1 <> 0 and expr1 <> NULL),则 IF()的返回值为expr2; 否则返回值则为 expr3。 |
| MyISAM | InnoDB | |
|---|---|---|
| 使用场景 | 表中绝大多数都只是读查询 | 既有读也有写 |
| 事务 | 不支持 | 每一条SQL语言都默认封装成事务,自动提交,这样会影响速度,所以最好把多条SQL语言放在begin和commit之间,组成一个事务 |
| 外键 | 不支持,一个包含外键的InnoDB表转为MYISAM表会失败 | 支持 |
| 索引 | MyISAM是非聚集索引,使用B+Tree作为索引结构,索引和数据文件是分离的,索引保存的是数据文件的指针。 | InnoDB是聚集索引,也是使用B+Tree作为索引结构,B+树主键索引的叶子节点就是数据文件,数据文件是和主键索引绑是在一起的。 |
| 辅助索引 | 辅助索引和主键索引是独立的。MyISAM的B+树主键索引和辅助索引的叶子节点都是数据文件的地址指针。 | 辅助索引的叶子节点是主键的值,辅助索引需要两次查询,先查询到主键,然后再通过主键查询到数据。因此,主键不应该过大,因为主键太大,其他索引也都会很大。 |
| 锁 | 支持表级锁 | 支持表级锁和行(默认)级锁。InnoDB的行锁是实现在索引上的,而不是锁在物理行记录上。即如果访问没有命中索引,则无法使用行锁,将退化为表锁。 |
| 存储文件 | 表定义文件 frm,数据文件 myd,索引文件 myi | 表定义文件 frm,数据文件 ibd |
有主键则定义主键索引为聚集索引;没有主键则选第一个不允许为NULL的唯一索引;还没有就使用innodb的内置rowid为聚集索引。
SQL解析器主要包含词法分析和语法分析,其中,词法分析主要负责识别和检查关键字,而语法分析会根据mysql的语法规则对SQL进行校验,找出SQL的语法问题。
mysql索引数据结构有hash和b+tree,hash由数组和链表组成。对MyISAM存储引擎,其叶子节点 data 域存放的是数据的物理地址,即索引结构和真正的数据结构其实是分开存储的。对InnoDB存储引擎,数据是存储在主键索引里面的。
select * from table order by grade asc, id desc;
grant select on database_B.table_C to 'user_A'@'%'
1)首先客户端发送请求到服务端,建立连接,并进行权限验证。
2)服务端先看下查询缓存是否命中,命中就直接返回,否则继续往下执行。
3)接着来到解析器,进行语法分析,一些系统关键字校验,校验语法是否合规。
4)然后优化器进行SQL优化,比如怎么选择索引之类,然后生成执行计划。
5)最后执行引擎调用存储引擎API查询数据,返回结果。
1、先在where解析这一步把当前的查询语句中的查询条件分解成每一个独立的条件单元
2、mysql会自动将sql拆分重组
3、然后where条件会在B-tree index这部分进行索引匹配,如果命中索引,就会定位到指定的table records位置。如果没有命中,则只能采用全部扫描的方式
4、根据当前查询字段返回对应的数据值
答:在SQL解析阶段。
1.PPT中涉及的命令,每个都需要去测试一遍(可以命令与可视化工具结合着看一下)
3.仔细阅读和理解(出现的各种索引的用法详解?特性是什么?各个索引的对比[出一个对比表])
4.Explain执行计划了解
5.MySQL Slow Log 分析工具了解
6.建表、索引的原则、编写高效SQL的注意点