DML
数据操作语言。 用于添加、删除、更新和查询数据库记录,并检查数据完整性
关键字包括 CREATE 、 DROP 、 ALTER 等
命令行登录mysql
//登录mysql -hlocalhost -P3306 -u user -ppassword//连接本机可以省略部分mysql -u user -ppassword//打开指定库use 库名//查看表结构desc 表名;
DML操作
//查询常量值select 100;select 'string'; //字符串形式//查询表达式selct 12*12;//查询函数select version();//起别名select version() as 版本; //使用asselect version() 版本; //使用空格select version() as "banben"; //名字是字符串 建议双引号//concat用于拼接字符串//如果拼接的字段有为null值 拼接后输出的结果也是nullselect CONCAT(last_name,first_name) 姓名 from employees;
DISTINCT关键字
select DISTINCT department_id from employees;
注:DISTINCT需要放到所有列名的前面,其实是对后面所有列名的组合进行去重
条件表达式筛选
//条件表达式筛选 <>表示不等于> < = != <> >= <=// <=> 安全等于符 可以判断null值 也可以判断普通数值SELECT last_name,commission_pct from employeeswhere commission_pct <=> null//逻辑表达式筛选and or not#查询部门编号不在90到110之间或者工资高于15000的员工信息SELECT * from employeeswhere not(department_id between 90 and 100) or salary>15000注:OR可以和AND一起使用,AND的优先级高于OR//模糊查询like in (is null) (is not null) (between and)注:IN列表中存在NULL,则结果为NULLlike如果给定的值或者匹配条件为NULL,则返回结果为NULL#查询员工名中包含字符ee的员工信息 likeSELECT * from employees where last_name like "%ee%";#查询员工名中第二字符是ee的员工信息SELECT * from employees where last_name like "_ee%";#查询员工名中第二字符是 _ 的员工信息SELECT * from employees where last_name like "_\_%";SELECT * from employees where last_name like "_$_%" ESCAPE '$';#查询部门编号在90到110员工信息 between andSELECT * from employees WHERE department_id BETWEEN 90 and 100#查询员工的工种编号是it_port,ad_vp,ad_pres中的一个的员工名和工种编号 INSELECT last_name,job_id from employeeswhere job_id in("it_port","ad_vp","ad_pres");#查询没有奖金的员工名和奖金率 is null判断null值SELECT last_name,commission_pct from employeeswhere commission_pct is null // = 不能用来判断null值
通配符
- % 表示任意多个字符
- _ 表示任意单个字符
- \或ESCAPE 表示转义字符
排序
# order by [asc 升序|desc 降序]//查询员工信息,按工资从高到底排序 ORDER BYSELECT * from employees ORDER BY salary desc; //支持按别名排序//查询员工信息,先按工资降序 再按id降序SELECT * from employeesORDER BY salary desc,employee_id desc; //支持多个字段排序
分页查询
// limit offest,size (offest 要显示条目的起始索引;size 要显示的条目个数)//查询有奖金的员工信息 并且工资较高的前10名按降序显示select * from employeeswhere commission_pct is not NULLORDER BY salary DESCLIMIT 10,15MySQL 8.0中可以使用"LIMIT 3 OFFSET 3" 表示取从第三条记录开始后的三条记录#实际运用 分页显示公式要显示的页数 page,每页显示的条目数 sizelimit (page-1)*size,size;
注:LIMIT 子句必须放在整个SELECT语句的最后
多表查询
当查询的字段来源于多个表时,就会用到多表查询
//连接分类://内连接:等值连接、非等值连接、自连接 (交集)//外连接:左外连接、右外连接、全外连接 (交集+自己有的部分)//交叉连接 (笛卡尔乘积)
#等值连接 取多个表之间交集的部分//查询员工的名字和部门名字select last_name,department_name from employees e,departments dwhere e.department_id = d.department_id注:如果为表起了别名,则查询的字段就不能使用原来的表名去限定//查询有奖金的每个部门的部门名和部门的领导编号和该部门的最低工资select department_name,d.manager_id,MIN(salary)from departments d,employees ewhere d.department_id=e.department_id and commission_pct is not nullGROUP BY d.department_id,d.manager_id注:查询两个列 不能确定两个列是否一一对应时 要把两个列字段列入分组中#非等值连接//查询员工的工资和工资级别select salary,grade_levelfrom employees e,job_grades jwhere e.salary BETWEEN lowest_sal AND highest_salORDER BY salary desc#自连接 表内自连//查询员工名和上级的名字select e.last_name,m.last_namefrom employees e,employees mwhere e.manager_id=m.employee_id
内连接
sql99语法:select 查询列表from 表1 别名 [连接类型]join 表2 别名on 连接条件 //多表连接多个join 和onwhere 筛选条件#内连接#1 等值连接//查询员工名、部门名select last_name,department_namefrom employees e INNER JOIN departments don e.department_id = d.department_id//查询部门个数>3的城市名和部门个数select city,COUNT(*)from locations l INNER JOIN departments don l.location_id = d.location_idgroup by cityHAVING COUNT(*)>3#2 自连接//查询员工名和上级的名字select e.last_name,m.last_namefrom employees e JOIN employees mon e.employee_id = m.manager_id
外连接
#外连接 用于一个表中有 另一个表没有的记录//外连接的查询结果为主表中所有的记录 内连接结果+主表有从表没有的记录//左外连接左边的是主表 右外连接右边的是主表//全外连接=内连接结果+表1中有表2中没有的+表2中有表1中没有的MySQL不支持FULL JOIN,但是可以用 LEFT JOIN UNION RIGHT join代替//查询哪个部门没有员工select d.*,e.employee_idfrom departments dLEFT OUTER JOIN employees e //把主表中有 从表中没有的数据列出了on d.department_id=e.department_idwhere e.employee_id is null //在结果集上筛选
注:左外连接会以连接条件会基准,先把符合连接条件的数据筛选出来,再讲左表(基准表)的所有数据显示出来,如果左表有的数据,右表不存在满足连接条件的数据,则左表的数据依然展示,而右表的显示为null
注:超过三个表禁止 join。需要 join 的字段,数据类型保持绝对一致;多表关联查询时,保证被关联的字段需要有索引
交叉连接
#笛卡尔乘积select b.*,bo.*from beauty bCROSS JOIN boys bo
联合查询
要查询的结果来源于多个表时,且多个表之间没有直接的连接关系,但查询的信息一致时
注:要求多条查询语句的查询列数是一致的;
union会自动去重,如果不想去重,就添加关键字 union ALL; UNION ALL语句时所需要的资源比UNION语句少,如果表内不存在重复数据尽量用union all
select * from employees where email like"%a%"unionselect * from employees where department_id>90
子查询
一个查询语句嵌套在另一个查询语句内部的查询
按结果集的行列数不同
- 标量子查询:结果集只有一行一列(搭配> < <= >= = <>)
- 列子查询:结果集只有一行多列(搭配 in any/some all)
- 行子查询:结果集有一行多列或多行多列
- 表子查询:结果集一般为多行多列
#标量子查询//查询工资大于abel的员工select * from employeeswhere salary>(select salaryfrom employeeswhere last_name="Abel" //此处select返回的是一行一列);#列子查询 一行多列//返回location_id是1400或1700的部门中所有员工姓名select last_namefrom employeeswhere department_id in(select DISTINCT department_id from departments where location_id in(1400,1700))#行子查询//查询员工编号最小并且工资最高的员工消息select * from employeeswhere (employee_id,salary)=(select MIN(employee_id),MAX(salary) from employees) //行子查询#from后面跟子查询//查询平均工资最低的部门select MIN(avg_s)from (select department_id,AVG(salary) avg_sfrom employeesGROUP BY department_id //将子查询充当一张表来用) ag_dep; //必须起别名 不然找不到表
相关子查询
#exists 相关子查询 查询需要的东西涉及到了主查询//查询有员工的部门名select department_namefrom departments dwhere EXISTS(select * from employees ewhere d.department_id=e.department_id)
单行函数
对外暴露方法名。隐藏细节,实现代码复用性
数学函数
//round 四舍五入select ROUND(1.567);select ROUND(1.567,2); //小数点后保留两位//ceil 向上取整select CEIL(1.5);//floor 向下取整select FLOOR(1.5);//truncate 截断select TRUNCATE(1.333,1); //小数点后一位以后的数字不管//mod 取余select MOD(10,3);
字符函数
//length 获取参数值的字节个数select length("abc");//concat 拼接字符串select CONCAT(first_name,"_",last_name) 姓名 from employees;//UPPER,LOWER实现大小写转换select UPPER("name");select LOWER("NAME");//substr 截取函数select substr("abcde",1,3) letter;#姓名首字母大写其余小写,且之间用'_'连接select CONCAT(UPPER(SUBSTR(last_name,1,1)),"_", //函数间可以嵌套使用LOWER(SUBSTR(last_name,2))) from employees;//instr 返回子串第一次出现的索引 找不到就返回0select INSTR("abcd","cd");//trim 去除左右空格 或指定的字符select TRIM(" abc ");select TRIM('M' from "MMMabcMMM");//lpad 用指定的字符实现左填充到指定长度select LPAD("abc",5,"*");select RPAD(LPAD("abc",5,"*"),8,"*"); //rpad 右填充//replace 替换指定的字符select REPLACE("abcdefg","abc","efg")
日期函数
//返回当前日期和时间select NOW();//返回当前日期select CURDATE();//返回当前时间select CURTIME();//获取指定字段的年、月、日、分、秒select YEAR(NOW());select YEAR(hiredate) from employees; //函数内可以是字段//str_to_date 将字符通过指定的格式转换成日期select STR_TO_DATE(NOW(),"%Y-%m-%d");//date_foramat 将日期转换成字符select DATE_FORMAT(NOW(),"%y年%m月%d日")//datediff 返回两个日期之间相差的天数select DATEDIFF(NOW(),"1999-1-1");
流程控制函数
IF(value,value1,value2): 如果value的值为TRUE,返回value1, 否则返回value2
IFNULL(args1,args2):如果args1不是空返回args1,否则返回返回args2
NULLIF(args1,args2):如果args1=args2返回null,否则返回返回args1
//查看奖金率select last_name,commission_pct,IF((commission_pct IS NULL),"无奖金","有奖金") 备注from employees ORDER BY commission_pct DESC//IFNULL(expr1,expr2)判断字段是否为nullexpr1是判断的字段,expr2是如果判断成立 要给判断字段赋予的值select IFNULL(commission_pct,0) 奖金率 from employees
CASE-WHEN
//case后面跟参数select salary,department_id,CASE department_id //类似于switch 适合做等值判断WHEN 30 THEN salary*1.1WHEN 40 THEN salary*1.2WHEN 50 THEN salary*1.5ELSE salaryEND 新工资 from employees;//case后面不跟参数select salary,CASE //类似于if else-if elseWHEN salary>20000 THEN 'A'WHEN salary>15000 THEN 'B'WHEN salary>10000 THEN 'C'ELSE 'D'END 工资级别 from employees;
聚合函数
用作统计,搭配group by使用
//总和select SUM(salary) from employees//平均select AVG(salary) from employees//最小select MIN(salary) from employees//最大select MAX(salary) from employees//保留小数点后两位的操作CAST(AVG(salary) as DECIMAL(10,2))
count函数
- COUNT(*)返回表中记录总数,适用于任意数据类型
- COUNT(expr) 返回expr不为空的记录总数
注:count(*)会统计值为 NULL 的行,而 count(列名)不会统计此列为 NULL 值的行
//总数 count(1) 是计算一共有多少符合条件的行select COUNT(salary) from employees注:以上分组函数都忽略了null值
count(*),count(1),count(列名)区别
对于MyISAM引擎的表是没有区别的。这种引擎内部有一计数器在维护着行数
Innodb引擎的表用count(*),count(1)直接读行数,复杂度是O(n),因为innodb要去数一遍。但好于具体的count(列名)
分组查询
当执行group by时,查询语句中最好不要出现不相关的查询字段;select的字段不属于group by的字段的话,sql语句就会报错(一般配合聚合函数使用)
过滤分组:HAVING子句,不能单独使用,必须要跟 GROUP BY 一起使用( 不能在 WHERE 子句中使用聚合函数)
//查询每个部门的平均工资select department_id,AVG(salary) from employeesGROUP BY department_id;//查询有奖金的每个领导手下员工的最高工资select MAX(salary),manager_id from employeeswhere commission_pct IS NOT null //where 用于分组前筛选GROUP BY manager_id 原始表中要有分组筛选条件的字段//查询哪个部门的员工数>2select COUNT(*) 人数,department_id from employeesgroup by department_idHAVING COUNT(*)>2 //HAVING 用于分组后的筛选 通常用在聚合函数上//查询每个部门每个工种的员工的平均工资select AVG(salary),manager_id,job_idfrom employeesGROUP BY manager_id,job_id //按多个字段分组
注:GROUP BY后可使用 WITH ROLLUP 关键字,在所有查询出的分组记录之后增加一条记录,该记录计算查询出的所有记录的总和(即统计记录数量 )
当使用ROLLUP时,不能同时使用ORDER BY子句进行结果排序,即ROLLUP和ORDER BY是互相排斥的
WHERE和HAVING的对比
- WHERE 可以直接使用表中的字段作为筛选条件,但不能使用分组中的计算函数作为筛选条件; HAVING 必须要与 GROUP BY 配合使用,可以把分组计算的函数和分组字段作为筛选条件
- WHERE 在 GROUP BY 之前执行,HAVING 在 GROUP BY 之 后
- 如果需要通过连接从关联表中获取需要的数据,WHERE 是先筛选后连接,而 HAVING 是先连接后筛选
- WHERE 比 HAVING 更高效。因为 WHERE 可以先筛选,用一 个筛选后的较小数据集和关联表进行连接,这样占用的资源比较少,执行效率也比较高。HAVING 则需要先把结果集准备好,也就是用未被筛选的数据集进行关联,然后对这个大的数据集进行筛选,这样占用的资源就比较多,执行效率也较低
SELECT 的执行流程
FROM ->ON-> WHERE -> GROUP BY -> HAVING -> SELECT 的字段 -> DISTINCT -> ORDER BY -> LIMIT
(select之前相当于对数据表进行行筛选,select字段是对列进行筛选)
在 SELECT 语句执行这些步骤的时候,每个步骤都会产生一个 虚拟表 ,然后将这个虚拟表传入下一个 骤中作为输入。这些步骤隐含在 SQL 的执行过程中,是不可见的
插入语句
#为表的指定字段插入数据insert into 表名(列名,列名..) values(值1...)或insert into 表名 set 列名=值,列名=值...//批量插入多条数据insert into 表名(列名,列名..) values (值1...),(值2...),(值3...);//支持子查询insert into 表名(列名,列名...)select 值1,值2,值3... from 表名;
更新语句
//单表修改 *update 表名set 列=值,列=值...where 筛选条件;//多表修改update 表1 别名inner|left|right join 表2 别名on 连接条件set 列=值,列=值...
删除语句
delete from 表名 where 筛选条件; *truncate table 表名; //删除整个表
注:假如删除的表中有自增长列,使用delete删除后,再插入数据,自增长列的值从断点开始。(如:删除一行id=1的记录,再插入语句时,插入语句的id从2开始自增)
使用truncate删除,自增长列的值从1开始;
delete删除后有一个受影响行数的返回值,truncate没有;
#delete删除可以回滚,truncate删除不能回滚
TRUNCATE TABLE 比 DELETE 速度快,且使用的系统和事务日志资源少,但 TRUNCATE 无事务且不触发 TRIGGER,有可能造成事故
