跳到主要内容

SQL基础知识

SQL 结构化查询语言

分类

  • DDL 数据定义语言
  • DML 数据管理语言
  • DCL 数据控制语言
  • DQL 数据查询语言

DDL

  • create
  • alter
  • drop
  • rename
  • truncate

DDL 没有事务性,不能回滚。所以在使用 truncate 的时候需要注意。

DML

  • insert
  • delete
  • update
  • select

使用频率较高,需要熟练掌握。

DCL

  • commit
  • rollback
  • savepoint
  • grant
  • revoke

需要理解透彻事务。 transaction

DQL

select <字段名>
from <表名>
where <条件>

SQL 语句的规范

  1. use interview;
  2. 字句分行写,提高可读性。
  3. 每条命令以 ; 结尾。
  4. 关键字大写,表名,字段名小写。

SELECT

select 1;
select 1 + 1
from dual;
select *
from jobs;

别名

select job_id id
from jobs;
select job_id as id
from jobs;
select job_id "job id"
from jobs; -- 推荐
select job_id 'job id'
from jobs; -- 不推荐

distinct 去除重复行

如果要使用 distinct 的时候要放在 select 后面。

SELECT distinct studentid
from student_courses;

空值参与运算,结果也为空

null, 0 , '', 'null' 这四个值不同。

使用 IFNULL 函数来判断。

null 参与计算时结果为 null


select ifnull(age_, 0)
from students;

-- 无法获取数据,因为 null 参与计算的结果为 null。 如果要判断值是否为 null ,需要使用 xxx is null。或者使用 xx <=> null.
select *
from students
where name = null;

着重号

用于转义关键字。


select *
from `order`;

显示表结构

describe students;
desc students;

过滤数据

select *
from students
where id = 1;

-- 会把 Persident 的记录查询出来。
-- 正常情况下是不应该查询出来的,但是 MySQL 比较特殊。
select *
from jobs
where job_title = 'president';

运算符 +

在 SQL 中, + 是没有连接的作用,仅表示加法运算。此时,字符串会隐式转换为数值,如果转换失败,当作 0。

select 100 + '1'
from dual; -- 101
select 100 + 'a'
from dual; -- 100

比较运算符

  • =
  • <=> 安全等于
  • <>
  • !=
  • <
  • <=
  • >
  • >=
-- true 返回 1 ,false 返回 0 
select 1 = 2,
1 > 2,
1 >= 2,
1 != 2,
1 <> 2,
1 < 2,
1 <= 2,
'a' = 'a',
1 = null, -- null
null = null, -- null
null <=> null -- 1
from dual;
  • is null
  • is not null
  • least
  • greatest
  • between ... and ...
  • isnull
  • in
  • not in
  • like
  • regexp
  • rlike
select least(1, 2, 3)
from dual; -- 1 计算最小值
select greatest(1, 2, 3)
from dual; -- 3 计算最大值

逻辑运算符

and 优先级高于 or

  • or
  • ||
  • and
  • &&
  • not
  • !
  • xor

排序 order by

select *
from students
order by id desc; -- 降序
select *
from students
order by id asc; -- 升序

分页 limit

-- 显示第 5,6 两条数据
select *
from students limit 4,2;
-- limit N offset M -- N 从 0 开始
-- MySQL 8.0 新特性
select *
from students limit 2
offset 4;

-- 显示前 5 条数据
select *
from students limit 5;
where ... order by ... limit ...

笛卡尔积

select *
from tablea,
tableb;
select *
from tablea
cross join tableb;

多表查询

角度一

  • 等值连接
  • 非等值连接
-- 非等值连接
select emp.employee_id, emp.last_name, jg.grade_level
from employees emp,
job_grades jg
where emp.salary between jg.lowest_sal and jg.highest_sal;

角度二

  • 自连接
  • 非自连接
-- 自连接
select emp.employee_id, emp.last_name, mgr.employee_id, mgr.last_name
from employees emp,
employees mgr
where emp.manager_id = mgr.employee_id;

角度三

  • 内连接 [inner] join ... on ....
  • 外连接
    • 左外连接 left [outer] join ... on ...
    • 右外连接 right [outer] join ... on ...
    • 满外连接 MySQL 不支持 full [outer] join ... on ... 的写法,可以使用 union all 实现满外连接
-- SQL99
-- 内连接
select emp.employee_id, emp.last_name, mgr.employee_id, mgr.last_name
from employees emp
inner join
employees mgr
on emp.manager_id = mgr.employee_id;
-- 左外连接
select emp.employee_id, emp.last_name, dept.department_id, dept.department_name
from employees emp
left join departments dept
on emp.department_id = dept.department_id;
-- 右外连接
select emp.employee_id, emp.last_name, dept.department_id, dept.department_name
from employees emp
right join departments dept
on emp.department_id = dept.department_id;
-- 满外连接, MySQL 不支持 full outer join 的写法
#
select emp.employee_id,
emp.last_name,
dept.department_id,
dept.department_name
#
from employees emp
full join departments dept #
on emp.department_id = dept.department_id;
-- union all 实现满外连接
select emp.employee_id, emp.last_name, dept.department_id, dept.department_name
from employees emp
left join departments dept
on emp.department_id = dept.department_id
union all
select emp.employee_id, emp.last_name, dept.department_id, dept.department_name
from employees emp
right join departments dept
on emp.department_id = dept.department_id and emp.employee_id is null;

union 、 union all 的使用

union 操作符返回两个查询结果集的并集,去除重复记录。
union all 操作符返回两个查询结果集的并集;不会去除重复记录。

能用 union all 就尽量使用 union allunion 效率比较低。

自然连接 natural join

自动查询两张表中所有相同的字段,然后进行等值连接。

-- nature join
select emp.employee_id, emp.last_name, dept.department_id, dept.department_name
from employees emp
join departments dept
on emp.department_id = dept.department_id and emp.manager_id = dept.manager_id;

select emp.employee_id, emp.last_name, dept.department_id, dept.department_name
from employees emp
natural join departments dept;

using

简化等值连接

-- using
select emp.employee_id, emp.last_name, dept.department_id, dept.department_name
from employees emp
join departments dept
on emp.department_id = dept.department_id;

select emp.employee_id, emp.last_name, dept.department_id, dept.department_name
from employees emp
join departments dept
using (department_id);

系统内置函数

数值函数

  • abs 绝对值
  • sign 取符号
  • pi
  • ceil 天花板
  • ceiling
  • floor 地板
  • least
  • greatest
  • mod
  • rand
  • rand(x)
  • round(x)
  • round(x,y)
  • truncate(x,y) 数字x,截断为y位小数
  • sqrt(x) 平方根

角度与弧度互换

  • radians(x) 角度->弧度
  • degrees(x) 弧度->角度

三角函数

  • sin
  • asin
  • cos
  • acos
  • tan
  • atan
  • atan2
  • cot

指数,对数

  • pow(x,y),power(x,y)
  • exp(x)
  • ln(x),log(x)
  • log10(x)
  • log2(x)

进制转换

  • bin(x) 2 进制
  • hex(x) 16 进制
  • oct(x) 8 进制
  • conv(x,f1,f2)

字符串函数

MySQL 中下表是从 1 开始的

  • ascii(s)
  • char_length(s)
  • length(s)
  • concat(s1,s2,...)
  • concat_ws(x,s1,s2,...)
  • insert(str,idx,len,replacestr)
  • replace(str,a,b)
  • upper(s) ucase(s)
  • lower(s) lcase(s)
  • left(str,n)
  • right(str,n)
  • lpad(str,len,pad) 左对齐
  • rpad(str,len,pad) 右对齐
  • ltrim(s)
  • rtrim(s)
  • trim(s) 去掉首尾空格
  • trim(s1 from s2)
  • trim(trailing s1 from s2)
  • repeat(str,n) 重复 n 次
  • space(n) 返回 n 个空格
  • strcmp(s1,s2) 比较 s1 s2 第 ascii 码
  • substr(s,index,len) 从字符 index ,截取长度 len 的字符串
  • locate(substr,str) 字符串出现的位置
  • elt(m,s1,s2...) 返回指定标的字符串
  • field(s,s1,s2...) 第一次出现的位置
  • find_in_set(s1,s2) s1 在 s2 中的位置
  • reverse(s)
  • nullif(value1,value2) 比较两个字符串,相等返回null,否则返回 value1
select ascii('a'),        -- 第一个字符的 ascii 码
char_length('abc'),-- 字节个数
length('qwer'),
length('我爱学习'),
concat('hello', ' ', 'MySQL', '8.0'),
concat_ws(' ', 'a', 'b', 'c'),
insert
(
'wangzhy'
,
2
,
3
,
'123'
)
,
-- 从第二个字符开始,替换三个字符
replace
(
'wangzhy'
,
'zhy'
,
'zhiyuan'
)
,
upper
(
'wangzhy'
)
,
ucase
(
'wangzhy'
)
,
lower
(
'WANG'
)
,
lcase
(
'WANG'
)
,
left
(
'wangzhy'
,
4
)
,
right
(
'wangzhy'
,
3
)
,
lpad
(
'aa'
,
7
,
'22'
)
,
-- 左对齐
rpad
(
'qq'
,
10
,
'*'
)
,
-- 右对齐
ltrim
(
' a b c '
)
,
rtrim
(
' a c '
)
,
trim
(
' a '
)
,
repeat
(
'a'
,
20
)
,
space
(
20
)
,
strcmp
(
'a'
,
'1'
)
,
substr
(
'wangzhy'
,
1
,
2
)
,
locate
(
'a'
,
'wangzhy'
)
,
elt
(
'2'
,
'b'
,
'c'
,
'a'
)
,
field
(
'a'
,
'b'
,
'a'
)
,
find_in_set
(
'a'
,
'wangzhy'
)
,
reverse
(
'wangzhy'
)
,
nullif
(
'a'
,
'a'
)
,
nullif
(
'b'
,
'a'
)
,
nullif
(
'a'
,
'b'
)
from dual;

时间、日期函数

  • curdate() 年月日
  • curtime() 时分秒
  • sysdate() 年月日时分秒
  • now() 年月日时分秒
  • utc_date()
  • utc_time()
select
curdate(),
current_date,
curtime(),
current_time(),
now(),
utc_date(),
utc_time()
from dual;
  • unix_timestamp()
  • unix_timestamp(date)
  • from_unixtime(timestamp)
select unix_timestamp(),
unix_timestamp(now()),
unix_timestamp('2023-09-04 21:35:11'),
from_unixtime(1693834511)
from dual;
  • year(date)/month(date)/day(date)

  • hour(time)/minute(time)/second(time)

  • monthname(date)

  • dayname

  • weekname

  • quarter

  • wwek(date) weelofday(date)

  • dayofyear

  • dayofmonth

  • dayofweek

  • extract(type from date)

  • time_to_sec(time)

  • sec_to_time(sec)

  • date_add(datetime,interval expr type) interval 是关键字,不省略。

  • adddate(datetime,interval expr type)

  • date_sub(datetime,interval expr type)

  • subdate(datetime,interval expr type)

select date_add(now(), interval 1 year )
from dual
  • addtime(time1,time2) time1+time2

  • subtime(time1,time2) time1-time2

  • datediff 时间间隔天数

  • timediff

  • from_days

  • to_days

  • last_day

  • makedate

  • maketime

  • period_add

  • date_format(date,fnt)

  • time_format(time,fnt)

  • get_format(date_type,format_type)

  • str_to_date(str,fmt)

  • %Y 年 2023

  • %y 年 23

  • %M 月份 January

  • %m 月份 01

  • %b 月份缩写 Jan,Feb

  • %c 月份,数字 1,2,3

  • %D 英文后缀,月中的天数 1st,2nd,3rd

  • %d 两位数,月中天数 01,02

  • %e 数字,月中天数 1,2

  • %H 两位数,小时,24 小时制, 01,02...

  • %h 两位数,小时,12 小时制 01,02...

  • %k 数字,小时,24 小时制 1,2...

  • %l 数字,小时,12 小时制 1,2...

  • %i 两位数,分钟 00,01,02...

  • %S 两位数,秒

  • %s 两位数,秒

  • %W 一周的星期名称 Sunday

  • %a 一周的星期名称缩写 Sun,Mon,Tues...

  • %w 数字,在一周的天数,0,1,2...

  • %j 三位数,在年中的天数 001,002...

  • %U

  • $u

  • %T 24 小时制

  • %r 12 小时制

  • %p AP/PM

select date_format(now(), '%Y-%m-%d %H:%i:%s')
from dual;

流程控制函数

  • if
  • ifnull
  • case when exp1 then val1 when exp2 then val2 else val3 end
  • case exp when constant1 then val1 when constant2 then val2 else value3 end

信息函数

  • version()
  • connection_id()
  • database()
  • schema()
  • user()
  • current_user()
  • charset('')
  • collation('')

聚合函数

  • avg

  • sum

  • max

  • min

  • count count(*), count(1)

  • group by

  • having

select 的底层执行原理

SQL 的编写顺序

select...
from...
join...on 表的连接条件
where...不包括聚合函数的条件
group by...
having...包含聚合函数的条件
order by...
limit...

SQL 的执行顺序

from ...
join ... on ....
where ...
group by ....
having ....

select ....

order by ....
limit ...

from tab1 , tab2 -> on -> left/right join -> where -> group by -> having -> select -> distinct -> order by -> limit

根据 SQL 的执行顺序,可以得到

  1. 别名只能在 order by 使用,不允许在 where 子句中使用。
  2. 不包含聚合的函数应该写在 where 子句中,包含聚合函数的条件只能写在 having 子句中。

子查询

  1. 子查询在主查询之前一次执行完成
  2. 子查询的结果被主查询使用

注意

  • 子查询要包含在括号内
  • 将子查询放在比较条件的右侧
  • 单行操作符对应单行子查询(>,=,>=,<,<=,!=),多行操作符对应多行子查询(in,all,any,some)

子查询类别

  • 单行子查询

  • 多行子查询

  • 相关子查询

    • 查询工资大于本部门平均工资的员工信息
  • 不相子查询

    • 查询工资大于本公司平均工资的员工信息

多行子查询

in

在使用 not in 的时候需要注意,子查询的结果中,不允许包含 null,不然结果会有问题。

any

-- 返回其他 job_id 中比 job_id 为 'IT_PROG' 部门任一工资低的员工的员工号、姓名、JOB_ID、salary
select *
from employees
where job_id != 'IT_PROG'
and salary < any (select salary from employees where job_id = 'IT_PROG');

all

-- 返回其他 job_id 中比 job_id 为 'IT_PROG' 部门所有工资低的员工的员工号、姓名、JOB_ID、salary
select *
from employees
where job_id != 'IT_PROG'
and salary < all (select salary from employees where job_id = 'IT_PROG');

all 的使用方法

-- 查询平均工资最低的部门ID
-- 自己写的,这个有问题,最小值可能有多条数据
select department_id
from employees
group by department_id
order by avg(salary) asc limit 1;
-- 方式一 (三层子查询)
select department_id
from employees
group by department_id
having avg(salary) =
(select min(avg_sal) from (select avg(salary) avg_sal from employees group by department_id) t);
-- 方式二()
select department_id
from employees
group by department_id
having avg(salary) <= ALL (select avg(salary) avg_sal
from employees
group by department_id);

相关子查询

-- 查询大于本部门平均工资的员工
select *
from employees e1
where salary > (select avg(e2.salary) from employees e2 where e2.department_id = e1.department_id);

select *
from employees e1
left join (select department_id, avg(salary) avg_sal from employees group by department_id) e2
on e1.department_id = e2.department_id
where e1.salary > e2.avg_sal;

order by 中使用子查询

-- 查询员工的 id,salary 根据 department_name 排序
select e.employee_id, e.salary, d.department_name
from employees e
left join departments d on e.department_id = d.department_id
order by d.department_name;

select employee_id, salary
from employees
order by (select department_name from departments where employees.department_id = departments.department_id);

exists、not exists

-- 查询管理者的信息
select employee_id, job_id, department_id
from employees e1
where exists(select 1 from employees e2 where e2.manager_id = e1.employee_id);

-- 没有员工的部门
select department_id, department_name
from departments
where not exists(select 1 from employees where employees.department_id = departments.department_id);

存储过程

  1. 一般用于更新操作。
  2. 不可用户查询语句。
  3. 可以有 0 个或多个返回值。
  4. create procedure
  5. 功能更灵活,更强大。

存储函数

  1. 一般用于查询操作。
  2. 可以放在查询语句中使用。
  3. 只有一个返回值。
  4. create function

存储过程和存储函数的优缺点

优点

  1. 一次编译,多次使用,提高了 sql 的执行效率。
  2. 可以减少开发工作量。
  3. 安全性强。
  4. 可以减少网络传输量。
  5. 良好的封装性。

缺点

  1. 可移植性差。
  2. 调试困难。
  3. 存储过程的版本难管理。
  4. 不适合高并发场景。

小结

可以不用,但是要会。

变量

系统变量

@@ 标识

global variables

show
global variables;

session variables

show
session variables;
-- 1. 系统变量  
-- 1.1 全局 global variablesshow global variables ;
-- 1.2 回话 session variablesshow session variables ;
-- 查询 session system variablesshow variables ;

show
global variables like 'admin%';
show
variables like 'character%';
show
global variables like 'character%';

select @@global.character_set_client;
select @@session.character_set_client;

-- Variable 'max_connections' is a GLOBAL variable
-- select @@session.max_connections;
select @@global.max_connections; -- 1000
select @@session.pseudo_thread_id;
-- session id

-- session 变量存在返回 session 变量的值,不存在就返回 global 变量的值。
select @@character_set_client;
select @@max_connections; -- 1000
select @@pseudo_thread_id;
-- session id

-- 修改系统变量的值
-- 1. 修改 my.cnf/my.ini 文件,需要重启 MySQL 服务。
-- 2. 通过 set 命令修改系统变量。(重启 MySQL 服务之后,就会失效。)
-- 2.1 set @@global.变量名=值
-- 2.2 set global 变量名=值
-- 2.3 set @session.变量名=值
-- 2.4 set session 变量名=值

用户变量

@ 标识

  • 会话用户变量
  • 局部变量
    1. 只在 begin .... end 中有效,即只在存储过程和存储函数中使用。
    2. 声明在 begin 代码块中第一行,通过 declare 声明。
    3. declare 变量名 类型 [default 值] 如果没有声明默认值,则初始值为 null
-- 1. set @用户变量=值  
set
@emp_id=1
-- 2. set @用户变量:=值
set @dept_id:=100
-- 3. select @用户变量 := 表达式 [from 等子句]
select @count:= count(1)
from employees;
select @count;
-- 4. select 表达式 into @用户变量 [from 等子句]
select avg(salary)
into @avg_sal
from employees;
select @avg_sal;
delimiter
$
create procedure test_var()
begin
declare
a int default 0;
declare
b int;
declare
emp_name varchar(25);
set
a = 1;
set
b:=101;
select last_name
into @emp_name
from employees
where employee_id = 101;

select a, b, emp_name;
end $
delimiter ;

call test_var();