在数据库领域,DDL、DML 和 DQL 是结构化查询语言(SQL)的三个核心组成部分,它们各自负责不同的数据库操作。
简单来说,它们分别扮演着"建筑师"、"管理员"和"分析师"的角色。
🏗️ DDL (数据定义语言)
DDL (Data Definition Language) 负责定义和管理数据库的结构,例如创建、修改或删除数据库、数据表、索引等对象。它不操作表中的具体数据,而是操作存放数据的"容器"。
- 核心作用:定义或修改数据库对象的"骨架"。
- 常用命令 :
CREATE:创建数据库或表。ALTER:修改表结构,如添加或删除字段。DROP:删除数据库或表。TRUNCATE:清空表中的所有数据,但保留表结构。
- 特点 :DDL 语句执行后通常会立即生效并自动提交,操作不可回滚。
✏️ DML (数据操纵语言)
DML (Data Manipulation Language) 负责对数据库表中的数据进行增、删、改等操作。它操作的是"容器"里的具体内容。
- 核心作用:对表中的具体记录进行增删改。
- 常用命令 :
INSERT:向表中插入新数据。UPDATE:修改表中已有的数据。DELETE:从表中删除数据。
- 特点 :DML 操作通常在一个事务中进行,可以提交(
COMMIT)使更改永久生效,也可以回滚(ROLLBACK)来撤销更改。
🔍 DQL (数据查询语言)
DQL (Data Query Language) 专门用于从数据库中查询和检索数据。它是日常工作中使用频率最高的一类语句。
- 核心作用:从数据库中读取数据,不做任何修改。
- 核心命令 :
SELECT:这是 DQL 中唯一的核心命令,配合FROM、WHERE、GROUP BY、ORDER BY等子句,可以实现各种复杂的查询。
- 特点:只读不写,不会对数据库中的数据和结构造成任何改变。
📌 核心区别速览
为了更清晰地理解,可以参考下表:
| 分类 | 全称 | 核心作用 | 常用命令 |
|---|---|---|---|
| DDL | 数据定义语言 | 定义/修改数据库结构 | CREATE, ALTER, DROP |
| DML | 数据操纵语言 | 增/删/改表中的数据 | INSERT, UPDATE, DELETE |
| DQL | 数据查询语言 | 查询表中的数据 | SELECT |
💡 补充说明
关于 SELECT 命令的归属,存在不同观点。一些资料将其归为 DML 的一部分,因为它也操作数据(读取)。但在现代数据库教学和实践中,为了强调其"只读"的特性并与增删改操作区分开,更普遍的做法是将其单独划分为 DQL。
显示库
bash
show databases; --显示库
use mysql; --选择库
show tables; 显示表
desc user; 显示表结构
select * from user; // 查询表
常见数据库表
sql
## 创建数据库表
CREATE TABLE t_student(
sno int(6), -- 6显示长度
sname varchar(5), -- 5个字符
sex char(1),
age INT(3),
enterdate DATE,
classname varchar(16),
email varchar(15)
);
-- 查看表的结构
desc t_student;
-- 查看表中的数据
select * from t_student;
-- 查看建表语句
show create table t_student;
添加数据
sql
-- 查看表记录
select * from t_student;
-- 在t_student 中插入数据
insert into t_student values (1,'张三','男',18,'2026-4-1','安全1班','158@163.com');
insert into t_student values (1234567,'张三','男',18,'2026-4-1','安全1班','158@163.com');
insert into t_student values (2,'张三','男',18,'2026.5.1','安全1班','158@163.com');
insert into t_student values (5,'张三','男',18,now(),'安全1班','158@163.com');
insert into t_student (sno,sname,enterdate) values (6,'李四','2026-5-8');
修改删除数据
sql
-- 修改数据
update t_student set sex = '女';
update t_student set sex = '男' where sno = 2;
UPDATE t_student SET age = 21 where sno = 6;
-- 删除数据
delete from t_student where sno = 5;
DDL 和 DML的补充
sql
-- 创建一张表,快速添加,数据和结构都和t_student 一样
create table t_student2
as
select * from t_student;
select * from t_student2;
-- 创建一张表,快速添加,结构一样,数据没有
create table t_student3
as
select * from t_student where 1=2;
select * from t_student3;
-- 创建一张表,快速添加,部分数据和结构都和
create table t_student4
as
select sno,sname,age from t_student where sno = 2;
select * from t_student4;
-- 删除和清空数据
delete from t_student; -- 只是删除数据,没有重新建表
truncate TABLE t_student; -- 保留表结构,删除所有数据,重新建表
DQL
最简单的SQL语句
sql
-- 对emp 表查询
select * FROM emp; -- * 代表所有数据
-- 显示部分列
select empno, ename, sal from emp;
-- 显示部分行
select * from emp where sal > 2000;
-- 显示部分行和部分列
select empno, ename, sal from emp WHERE sal > 2000;
-- 起别名
select empno 员工编号, ename 姓名, sal 工资 from emp;
-- 运算符
select empno, ename, sal, sal+1000 as '涨薪后', deptno from emp where sal < 2500;
select empno,ename,sal,comm,sal+comm from emp;
-- 去重操作
select job FROM emp;
select DISTINCT job from emp;
select distinct job,deptno from emp; -- 对后面的所有列组合去重,而不是单独的某一列去重
-- 排序
select * from emp order by sal; -- 默认升序
select * from emp order by sal asc; -- asc 升序
select * from emp order by sal desc; -- desc 降序
select * from emp order by sal asc, deptno desc; -- 组合排序
where 子句
sql
-- 查看emp
select * from emp;
--
select * from emp WHERE deptno = 10;
select * from emp WHERE deptno => 10;
select * from emp WHERE deptno != 10;
select * from emp where job = 'CLERK';
select * from emp where job = 'clerk'; -- 默认情况下不区分大小写
select * from emp where binary job = 'clerk'; -- binary 区分大小写
select * from emp where hiredate < '1981-12-25' ORDER BY hiredate;
-- and
select * from emp where sal > 1500 and sal < 3000;
select * from emp where sal > 1500 && sal < 3000;
select * from emp where sal > 1500 and sal < 3000 order by sal;
select * from emp where sal BETWEEN 1500 and 3000;
-- OR
SELECT * FROM emp WHERE deptno = 10 OR deptno = 20;
SELECT * FROM emp WHERE deptno IN (10,30);
-- 模糊查询 % 代表任意多个字符
SELECT * FROM emp WHERE ename LIKE '%A%';
-- _代表一个字符
SELECT * FROM emp WHERE ename LIKE '___A%';
-- null 判断
SELECT * FROM emp WHERE comm is NULL;
SELECT * FROM emp WHERE comm is NOT NULL;
-- 小括号的使用
SELECT * FROM emp WHERE job = 'SALESMAN' or job = 'clerk' and sal >= 1500;
SELECT * FROM emp WHERE (job = 'SALESMAN' or job = 'clerk') and sal >= 1500;
函数的分类
sql
- 函数举例:
select empno,ename,lower(ename),upper(ename),sal from emp;
-- 函数的功能:封装了特定的一些功能,我们直接拿过来使用,可以实现对应的功能
-- 函数作用:为了提高select的能力
-- 注意:函数没有改变数据自身的值,而是在真实数据的上面进行加工处理,展示新的结果而已。
select max(sal),min(sal),count(sal),sum(sal),avg(sal) from emp;
-- 函数的分类:
-- lower(ename),upper(ename) :改变每一条结果,每一条数据对应一条结果 -- 单行函数
-- max(sal),min(sal),count(sal),sum(sal),avg(sal):多条数据,最终展示一个结果 -- 多行函数
单行函数
sql
-- 单行函数包含:
-- 1.字符串函数
select ename,length(ename),substring(ename,2,3) from emp;
-- substring字符串截取,2:从字符下标为2开始,3:截取长度3 (下标从1开始)
-- 2.数值函数
select abs(-5),ceil(5.3),floor(5.9),round(3.14) from dual; -- dual实际就是一个伪表
select abs(-5) 绝对值,ceil(5.3) 向上取整,floor(5.9) 向下取整,round(3.14) 四舍五入; -- 如果没有where条件的话,from dual可以省略不写
select ceil(sal) from emp;
select 10/3,10%3,mod(10,3) ;
-- 3.日期与时间函数
select * from emp;
select curdate(),curtime() ; -- curdate()年月日 curtime()时分秒
select now(),sysdate(),sleep(3),now(),sysdate() from dual; -- now(),sysdate() 年月日时分秒
insert into emp values (9999,'lili','SALASMAN',7698,now(),1000,null,30);
-- now()可以表示年月日时分秒,但是插入数据的时候还是要参照表的结构的
desc emp;
-- 4 流程函数
-- if相关
select empno,ename,sal,IF(sal >=2500,'高薪','底薪') as '薪资等级' from emp;
select empno,ename,sal,comm,sal+ifnull(comm,0) from emp; -- 如果comm 是null取0,单分支
select NULLIF(1,1), NULLIF(2,1) FROM DUAL; -- 如果v1 == v2,返回null,否则返回v1
-- case 相关
select empno,ename,job,
-- 等值判断
case job
when 'CLERK' then '店员'
when 'SALESMAN' then '销售'
when 'MANAGER' then '经理'
else '其他'
end as '职位',
sal from emp;
-- 区间判断
select empno,ename,sal,
case
when sal <= 1000 then 'A'
when sal <= 2000 then 'B'
when sal <= 3000 then 'C'
else 'D'
end as '薪资等级',
deptno from emp;
-- 6 其他函数
select database(), user(), version() from dual;
-- 多行函数
select max(sal),min(sal),count(sal),sum(sal),avg(sal) from emp;
-- 多行函数会自动忽略null值
select max(comm),min(comm),count(comm),sum(comm),avg(comm) from emp;
-- count 统计记录数 方式1
select * from emp;
select count(ename) from emp;
select count(*) from emp;
-- 方式2
select 1 from emp;
select count(1) from emp;
分组查询group by
sql
-- 多行函数
select max(sal),min(sal),count(sal),sum(sal),avg(sal) from emp;
-- 多行函数会自动忽略null值
select max(comm),min(comm),count(comm),sum(comm),avg(comm) from emp;
-- count 统计记录数 方式1
select * from emp;
select count(ename) from emp;
select count(*) from emp;
-- 方式2
select 1 from emp;
select count(1) from emp;
分组
sql
-- 统计个部分的平均工资
select deptno, avg(sal) from emp GROUP BY deptno;
select deptno, avg(sal) from emp GROUP BY deptno ORDER BY avg(sal);
-- 统计各个岗位的平均工资
select job , avg(sal) from emp group by job;
select job , lower(job), avg(sal) from emp group by job;
having
sql
-- 统计个部分的平均工资 只显示2000 以上的
select deptno,avg(sal) from emp group by deptno having avg(sal) > 2000;
select deptno,avg(sal) as 平均工资 from emp group by deptno having 平均工资 > 2000;
select deptno,avg(sal) as 平均工资 from emp group by deptno having 平均工资 > 2000 order by 平均工资 DESC;
-- 统计各个岗位的平均工资,除了manager
SELECT job , avg(sal) FROM emp where job != 'MANAGER' GROUP BY job;
select job , avg(sal) from emp GROUP BY job having job != 'MANAFER' ORDER BY avg(sal);
-- where是在分组前过滤,having是在分组后过滤
单表查询总结
sql
-- 单表查询练习
-- 1. 列出工资最小值小于2000的职位
select job , min(sal) from emp GROUP BY job having min(sal) < 2000;
-- 2 列出平均工资大于1200元的部门和工作搭配组合
select deptno,job,avg(sal)
from emp
group by deptno,job
having avg(sal) > 1200
order by deptno;
-- 统计人数小于4的部门的平均工资
select deptno , count(1), avg(sal)
from emp
group by deptno
having count(sal) < 4;
-- 统计部分最高工资,排除最高工资小于3000的部分
select deptno , max(sal)
from emp
group by deptno
having max(sal) < 3000;
多表查询
sql
-- cross join 交叉查询
select *
from emp
cross join dept;
-- 自然链接 natural join
-- 优点自动匹配同名的列,同名列只展示一次
select *
from emp
natural join dept;
select empno,ename,sal,dname,loc
from emp
natural join dept;
-- 缺点: 查询字段的时候没有指定字段所属的数据库表,效率低
-- 解决指定表明
select emp.empno,emp.ename,emp.sal,dept.dname,dept.loc
from emp
natural join dept;
-- 表名太长
select e.empno,e.ename,e.sal,d.dname,d.loc
from emp e
natural join dept d;
-- 缺点 natural join 自动匹配所有的同名列,我们希望匹配指定的同名列
-- 内连接 inner 解决 using 子句
select e.empno,e.ename,e.sal,d.dname,d.loc
from emp e
inner join dept d -- inner 可以不写
using(deptno);
-- using 缺点关联的字段必须同名
-- 解决 : 内连接 - on 子句
select *
from emp e
inner join dept d
on(e.DEPTNO = d.DEPTNO);
select *
from emp e
inner join dept d
on(e.DEPTNO = d.DEPTNO)
WHERE sal > 3000;
-- 条件:
-- 刷选条件:where HAVING
-- 链接条件:on,using,natural;
-- SQL 99 语法;刷新条件和链接条件是分开的
外连接
sql
-- inner ON 子句: 显示的是所有匹配的信息
select *
from emp e
inner join dept d
on(e.deptno = d.deptno);
select * from dept;
select * from emp;
-- 问题;40号部门没有员工,没有显示
-- 外连接 除了匹配的数据,还显示不匹配的数据
-- 左外连接 left outer join 左面表的信息
select *
from emp e
left outer join dept d
on(e.deptno = d.deptno);
-- 右外链接
select *
from emp e
right outer join dept d
on(e.deptno = d.deptno);
-- 全连接 full mysql不知道,oracle 支持
select *
from emp e
full outer join dept d
on(e.deptno = d.deptno);
-- 解决MySQL不支持 full的问题
select *
from emp e
left outer join dept d
on(e.deptno = d.deptno)
union -- 并集 去重
select *
from emp e
right outer join dept d
on(e.deptno = d.deptno);
select *
from emp e
left outer join dept d
on(e.deptno = d.deptno)
union all -- 并集 不去重
select *
from emp e
right outer join dept d
on(e.deptno = d.deptno);
-- mysql 支持并集,不支持交集,差集(oracle支持)