MySQL学习笔记

数据库的好处

实现数据持久化;
可储存大量数据;
方便检索;
保证数据的一致性、完整性;
安全,可共享;
通过组合分析,获取新数据

数据库的相关概念

  • DB(database)数据库:存储数据的仓库,保存一系列有组织的数据
  • DBMS(Database Management System)数据库管理系统/数据库管理软件,用于管理DB中的数据
    MySQL:甲骨文
    Oracle:甲骨文,服务价格昂贵
    DB2: IBM,应用方向窄,兼容性不高
    SqlServer:微软 兼容性低,只支持Windows系统
  • SQL(Structure Query Language)结构化查询语言:用于和DBMS通信的语言

数据库存储数据的特点

  1. 将数据放到表中,表再放到库中
  2. 一个数据库中可以有多个表,每张表都有自己的唯一标识名
  3. 表具有一些特性,这些特性定义了数据在表中如何储存
  4. 表由列组成,也称字段
  5. 表中的数据按行储存

MySQL软件

  • MySQL隶属于MySQLAB公司,总部位于瑞典,08年被sun收购,09年oracle收购sun
  • 优点:开放源代码,成本低,体积小,执行快,兼容性好,容易安装和使用
  • DBMS分为两类:
  1. 基于共享文件系统的DBMS(Access微软)-不需要安装
  2. 基于客户机-服务器(C/S,client/server)的DBMS(MySQL、Oracle、SqlServer),一般安装数据库指的是安装数据库的服务端

MySQL服务的启动和停止

  • 方式一:图形化
    右击-计算机管理-服务-MySQL服务
  • 方式二:通过管理员身份运行dos
    net start 服务名
    net stop 服务名

MySQL服务的登录和退出

  • 方式一:通过dos命令
    mysql -h主机名 -P端口号 -u用户名 -p秘密
    注意:
    如果是本机,则-h主机名可以省略
    如果端口号是3306,则-P端口号可以省略
  • 方式二:通过图形化界面客户端
    通过sqlyog,直接输入用户名、密码等连接进去即可

常见SQL命令演示

commands end with ; or \g

显示当前连接下所有数据库

show database;

打开/使用指定库

use 库名;

显示当前库中所有表

show tables;

显示指定库中所有表

show tables from 库名;

查询test数据库里面的表
当前的数据库还是在mysql,因为没有用use test

显示指定表中所有列

show columns from 表名;
  • 查询当前所在数据库
select database();
  • 创建表
create table stuinfo(
stuid int,   
tsunami varchar(20), 
gender char,      
borndate datetime);

int代表数值数据类型
varchar代表字符串数据类型
char代表代表单个字符
datetime代表日期和时间数据类型,最后一行数据不用逗号分隔
一般不存储年龄而是存储生日,年龄会变生日不会变

  • 描述表的内容 desc+表名
desc stuinfo;

MySQL学习笔记_第1张图片

  • 查询表中的数据
select * from stuinfo;
  • 插入数据
insert into stuinfo values(1,'张无忌','男','1998-3-3');
  • 更改数据
update stuinfo set borndate='1980-1-1' where stuid=1;
  • 删除数据
delete from stuinfo where stuid=1;
  • 更改表,修改表的结构
alter table stuinfo add column email varchar(20);
  • 删除表
drop table stuinfo;
  • 不区别大小写
  • 可以加注释
    单行注释用#或者–空格
    多行注释/* */

SQL语言介绍

语法要求

  1. SQL语句可以单行或多行书写,以分号结尾
  2. 可以用空格和缩进来增强语句的可读性
  3. 关键字不区别大小写,建议用大些

分类

  • DDL(Data Definition Language):数据定义语言,用来定义数据库对象:库、表、列等 create/drop/alter
  • DML(Data Manipulation Language):数据操作语言,用来定义数据库记录(数据)
    增删改 insert/update/delete
  • DCL(Data Control Language):数据库控制语言,用来定义访问权限和安全级别
  • DQL(Data Query Language):数据查询语言,用来查询记录(数据)select

图形化界面客户端的安装和使用

  • 选中语句,F9执行
  • 调整字体,工具-首选项/control+滚动轴

基础查询

语法

select 查询列表 from 表名;

特点

  1. 查询的结果集是一个虚拟表 (如何保存)
  2. select后面跟的查询列表,可以由多个部分组成,可以是单个字段、多个字段、常量、表达式、函数以及以上组合,用逗号隔开
  3. 执行顺序
    select first_name from employees;
    1⃣️from子句
    2⃣️select子句

查询常量

不涉及到表中的字段可以不写来自于某个表

select 100;

查询表达式

select 100%3;

查询单个字段

select last_name from employees;

如果表名是关键字,需要加着重号``

查询多个字段

select last_name,email,employee_id from employees;

查询所有字段

select * from employees;

F12:快捷键对齐格式
MySQL学习笔记_第2张图片

查询函数(调用函数,获取返回值)

select database(); 查询当前所在的数据库
select version(); 查询当前数据库服务器的版本
select user();

引申1:起别名

方式一:使用AS关键字
select user() as 用户名;
select user() as “用户名”;
select user() as ‘用户名’;
以上三种形式都️,什么时候需要加引号,例如:
select last_name as 姓 名 from employees; ❌
姓名之间有空格,系统无法识别

select last_name as "姓 名" from empolyees;☑️

方式二:使用空格
select user() 空格 用户名;
select user() 空格 “用户名”;
select user() 空格 ‘用户名’;
select last_name 空格 “姓 名” from employees;

拼接函数concat

需求:查询first_name和last_name拼接成的全名,最终起别名为:姓 名。

  • 方案1:使用+ ❌
    select first_name+last_name as “姓 名”
    from employees;

JAVA中 + 的作用:

  1. 加法运算 +加号两边都是数值
  2. 拼接符 至少有一个操作数为字符串

引申2: mysql中 + 的作用:只能做加法运算

  1. 如果两个操作数都是数值型,则直接做加法运算
  2. 如果其中一个操作数为非数值型,系统将强制转换成数值型,如果转换失败,则当做0处理
    ‘123’+4➡️127
    ‘abc’+4➡️4
    ‘张无忌’ + 100 ➡️ 100
  3. 如果其中一个操作数为null ,则结果直接为NULL
    null+null➡️null
    null+100➡️null
  • 方案2:使用concat拼接函数
SELECT CONCAT(first_name,last_name) AS "姓 名"
FROM employees;

引申3:去重distinct的使用

需求:查询员工涉及到的部门编号有哪些

select distinct departement_id from employees;

查看表的结构(字段、类型、约束)

desc employees;
show columns from employees;

案例讲解

  • 显示出表employees的全部列,各列之间用逗号连接,列名显示成OUT_PUT

  • 错误答案❌
    SELECT CONCAT (employee_id,’,’,first_name,’,’,last_name,’,’,salary,’,’,commission_pct) AS “OUT_PUT”
    FROM employees;

  • 结果有很多NULL值
    原因:commission_pct包含NULL值
    拼接的时候要注意,如果其中有一个参数为NULL,那结果也为NULL。所以拼接时,NULL值不能直接用于拼接。
    我们希望如果它为NULL时,不拼接它,当做一个空字符;不为NULL时,该是啥就拼接啥。这里需要借助一个函数IFNULL函数。

IFNULL函数(表达式1,表达式2)

- 表达式1:可能为NULL的字段或表达式
- 表达式2:如果表达式1为NULL,则最终结果显示的值
- 功能:如果表达式1为NULL,则显示表达式2;否则显示表达式1

SELECT commission_pct,IFNULL(commission_pct,‘空’)
FROM employees;
MySQL学习笔记_第3张图片

  • 正确答案
select concat(employee_id,',',first_name,',',last_name,',',salary,',',IFNULL(commission_pct,'') AS "OUT_PUT"
FROM employees;

引申4:补充函数

select version();
select database();
select user();
select ifnull(字段名,表达式)
select concat(字符1,字符2,字符3);拼接函数
select length(字符/字段);获取字节长度

条件查询

语法

select 查询列表
from 表名
where 筛选条件;

执行顺序

1⃣️from子句
2⃣️where子句
3⃣️select子句

特点

  1. 按关系表达式筛选
    关系运算符:>, <, >=, <=,=,<>不等于
    补充:也可以使用!=,但不建议
  2. 按逻辑表达式筛选
    逻辑运算符:与或非 and or not
    补充:也可以使用&& || ! ,但不建议
  3. 模糊查询
    like,in,between and,is null

按关系表达式筛选

  • 案例1:查询部门编号不是100的员工信息
    员工信息没有具体指明,所以用select *
SELECT * 
FROM employees
WHERE department_id <> 100;

按逻辑表达式筛选(and,or,not)

  • 案例1:查询部门编号不是50-100之间的员工姓名、部门编号、邮箱
  • 方式一:
SELECT last_name,department_id,email
FROM employees
WHERE department_id<50 OR department_id>100;
  • 方式二,语意性更强
SELECT last_name,department_id,email
FROM employees
WHERE NOT (department_id>=50 AND department_id<=100);
  • 案例2:查询奖金率>0.03或者员工编号在60-110之间的员工信息
SELECT *
FROM employees
WHERE commission_pct>0.03 OR (employee_id>=60 AND employee_id<=110);

模糊查询

LIKE关键字

NOT LIKE

  • 功能:一般和通配符搭配使用,对字符型数据进行部分匹配查询

  • 常见的通配符:
    _ 代表任意单个字符
    % 代表任意多个字符,支持0-多个

  • 案例1:查询姓名中包含字符a的员工信息
    注意⚠️:字符数据要用单引号
    注意⚠️:包含 a 代表前后可能都有其他字符

SELECT *
FROM employees
WHERE last_name LIKE '%a%';
  • 案例2:查询姓名中包含最后一个字符为e的员工信息
SELECT *
FROM employees
WHERE last_name LIKE '%e';
  • 案例3:查询姓名中包含第三个字符为x的员工信息
SELECT *
FROM employees
WHERE last_name LIKE '__x%';
  • 案例4:查询姓名中包含第二个字符为_的员工信息
SELECT *
FROM employees
WHERE last_name LIKE '_$_%' ESCAPE '$';

转义字符可以自行设定,ESCAFE ' ′ 代 表 设 定 '代表设定 为转义字符

IN关键字

  • 功能:判断某字段的值是否在指定的列表
    a IN(常量值1,常量值2,常量值3,…)
    a NOT IN(常量值1,常量值2,常量值3,…)

  • 案例1:查询部门编号是30/50/90的员工名、部门编号
    方式一:

SELECT last_name,department_id
FROM employees
WHERE department_id IN(30,50,90);

方式二:逻辑表达式

SELECT last_name,department_id
FROM employees
WHERE department_id=30OR department_id=50OR department_id=90;
  • 案例2:查询工种编号不是SH_CLERK或者IT_PROG的员工信息
    注意⚠️:数值型的常量值不用单引号,非数值型的常量值都加单引号
    方式1:
SELECT *
FROM employees
WHERE job_id NOT IN('SH_CLERK','IT_PROG');

方式2:

SELECT *
FROM employees
WHERE NOT(job_id='SH_CLERK'
OR job_id='IT_PROG');

BETWEEN AND关键字

  • 功能:判断某个字段的值是在指定的区间
    between and / not between and
  • 案例1:查询部门编号是30-90之间的部门编号、员工姓名
    方式1:
SELECT department_id,last_name
FROM employees
WHERE department_id BETWEEN 30 AND 90;

方式2:

SELECT department_id,last_name
FROM employees
WHERE department_id>=30 AND department_id<=90;

注意⚠️:between and包含两个临界值;且顺序不能颠倒

  • 案例2:查询年薪不是100000-200000之间的员工姓名、工资、年薪
SELECT last_name,salary,salary*12*(1+IFNULL(commission_pct,0)) 年薪
FROM employees
WHERE salary*12*(1+IFNULL(commission_pct,0)) NOT BETWEEN 100000 AND 200000

IS NULL/IS NOT NULL

  • 案例1:查询没有奖金的员工信息
SELECT *
FROM employees
WHERE commission_pct IS NULL;
  • 案例2:查询有奖金的员工信息
SELECT *
FROM employees
WHERE commission_pct IS NOT NULL;

对比=和IS

= 只能判断普通内容
IS 只能判断NULL值

<=>安全等于,既能判断普通内容,又能判断NULL值,阅读性差

案例讲解

  1. 选择姓名中有字母a和e的员工姓名
  • 方式1:
SELECT last_name
FROM employees
WHERE last_name LIKE '%a%' AND last_name LIKE '%e%';
  • 方式2:
SELECT last_name
FROM employees
WHERE last_name LIKE '%a%e%' OR last_name LIKE '%e%a%';

排序查询

语法

SELECT 查询列表
FROM 表名
【WHERE 筛选条件】可省略
ORDER BY 排序列表(可以多个)

执行顺序

  1. from子句
  2. where子句
  3. select子句
  4. order by子句

特点

  1. 排序列表可以是单个字段、多个字段、表达式、函数、别名、列数以及以上的组合
  2. 升序,通过ASC,默认行为;降序,通过DESC

按单个字段排序

案例1: 将员工编号>120的员工信息进行工资的升序

SELECT *
FROM employees
WHERE employee_id>120
ORDER BY salary ASC;

案例2: 将员工编号>120的员工信息进行工资的降序

SELECT *
FROM employees
WHERE employee_id>120
ORDER BY salary DESC;

按表达式排序

  • 案例1: 对有奖金的员工,按年薪降序
SELECT *,salary*12*(1+IFFULL(commission_pct,0)) 年薪
FROM employees
WHERE commission_pct IS NOT NULL
ORDER BY salary*12*(1+IFFULL(commission_pct,0)) DESC;

按别名排序

  • 案例1: 对有奖金的员工,按年薪降序
SELECT *,salary*12*(1+commission_pct) 年薪
FROM employees
WHERE commission_pct IS NOT NULL
ORDER BY 年薪 DESC;

注意⚠️:WHERE条件筛选为什么不能用别名?
因为执行顺序是FROM,WHERE,SELECT,别名是在SELECT子句中起的,在执行WHERE子句时还不知道别名。

按函数的结果顺序

  • 案例1: 按姓名的字数长度进行升序
SELECT last_name
FROM employees
ORDER BY LENGTH(last_name);

按多个字段排序

  • 案例1: 查询员工的姓名、工资、部门编号,先按工资升序,再按部门编号降序
SELECT last_name,salary,department_id
FROM employees
ORDER BY salary ASC,department_id DESC;

补充选学:按列数排序

-案例:按第二列排序(first_name)

SELECT * FROM employees
ORDER BY 2 DESC;

语意性较差

SELECT * FROM employees
ORDER BY first_name DESC;

案例讲解

  • 查询邮箱中包含e的员工信息,并先按邮箱的字节数降序,再按部门号升序排序
SELECT *
FROM employees
WHERE email LIKE '%e%'
ORDER BY LENGTH(email) DESC,department_id ASC;

常见函数-单行函数

  • 说明:SQL中的函数分为单行函数和分组函数
  • 调用语法一致:SELECT 函数名(实参列表);
  • 函数:为了解决某个问题,将编写的一系列的命令集合封装在一起,对外仅仅暴露方法名,供外部调用
  1. 自定义方法(函数)
  2. 调用方法(函数)
    叫什么:函数名
    干什么:函数功能
  • 常见函数
    字符函数:专门用于处理字符型数据的函数
    数学函数:专门用于处理数值型数据的函数
    日期函数:专门用于处理日期型数据的函数
    流程控制函数

字符函数

1. CONCAT(str1,str2)拼接字符

SELECT CONCAT('hello,',first_name,last_name) 备注
FROM employees;

2. LENGTH(str)获取字节长度

SELECT LENGTH('hello,郭襄')

结果不是8,而是12,一个汉字识别成3个字节

3. CHAR_LENGTH(str)获取字符长度/字符个数

一个汉字或者一个字母都是一个字符

SELECT CHAR_LENGTH('hello,郭襄')

结果是8

4. SUBSTR(字符串,从哪里开始,截取长度)

substr(str,pos):截取从pos开始的所有字符
substr(str,pos,len):截取len个从pos开始的字符
可以缩写为SUBSTR
susbtr(str完整的字符串,起始索引,截取的字符长度/个数)
**susbtr(str,起始索引)不写截取字符长度,默认把后面都截取出来
注意⚠️:起始索引从1开始

  • 截取张三丰
SELECT SUBSTR('张三丰爱上了郭襄'13)
  • 截取郭襄
SELECT SUBSTR('张三丰爱上了郭襄'72)

或写成

SELECT SUBSTR('张三丰爱上了郭襄'7)

5. INSTR(str,substr)获取字符第一次出现的索引

  • INSTR(str,substr):获取substr在str中第一次出现的索引
SELECT INSTR('三打白骨精aaa白骨精bb白骨精','白骨精')

结果为3

6. TRIM(str)去前后指定的字符,默认是去空格

SELECT TRIM('  虚  竹       ') AS a;

结果为虚 竹

SELECT TRIM('x' FROM 'xxxxxx虚xxx竹xxxxxxxxxx') AS a;

结果为虚xxx竹

7. LPAD(str,len,substr)/RPAD 左填充/右填充

  • 用a在木婉清的左边填充为一共十个字符
  • 10代表要求的字符长度
SELECT LPAD('木婉清',10,'a');

结果为:
在这里插入图片描述

8. UPPER(str)/LOWER(str) 变大写/变小写

  • 案例:查询员工表的姓名,要求格式:姓首字符大写,其他字符小写;名所有字符大写,且姓和名之间用_分割,最后起别名“OUTPUT”
SELECT UPPER(SUBSTR(first_name,1,1)) FROM employees;
SELECT LOWER(SUBSTR(first_name,2)) FROM employees;
SELECT UPPER(last_name) FROM employees;

SELECT CONCAT(UPPER(SUBSTR(first_name,1,1)),LOWER(SUBSTR(first_name,2)),'_',UPPER(last_name)) "OUTPUT"
FROM employees;

9. STRCMP(str1,str2) 比较两个字符大小

  • 前者大于后者,结果返回1
  • 前者小于后者,结果返回-1
  • 两者相等,结果返回0
SELECT STRCMP('abc','aac');

按字典顺序,第一个字符a相等,比较第二个字符b>a,所以结果返回1

10. LEFT(str,len)/RIGHT截取子串

  • LEFT(str,len):从左边截取指定len个数的字符
  • RIGHT(str,len):从右边截取指定len个数的字符
SELECT LEFT('鸠摩智',1);

结果为 鸠

SELECT RIGHT('鸠摩智',1);

结果为 智

SELECT RIGHT('鸠摩智',2);

结果为 摩智

数学函数

1. ABS(x)绝对值

SELECT ABS(-2.4);

结果返回2.4

2. CEIL(x) 向上取整,返回>=该参数的最小整数

SELECT CEIL(1.09);

结果返回2

SELECT CEIL(0.09);

结果返回1

SELECT CEIL(-1.09);

结果返回-1

3. FLOOR(x) 向下取整,返回<=该参数的最大整数

SELECT FLOOR(1.09);

结果返回1

SELECT FLOOR(0.09);

结果返回0

SELECT FLOOR(-1.09);

结果返回-2

4. ROUND(x,d)四舍五入,保留小数点后d位

  • 取整
SELECT ROUND(1.8712345);

结果返回2

  • 保留小数点后两位
SELECT ROUND(1.8712345,2);

结果返回1.87

5. TURNCATE(x,d)截断,保留小数点后d位

  • 从小数点后第几位开始截断
SELECT TURNCATE(1.8712345,0);

结果返回1

SELECT TURNCATE(1.8712345,1);

结果返回1.8

6. MOD(x,y)取余

SELECT MOD(-10,3);
SELECT -10%3

两者结果返回一样
a%b=a-a/bb, 绝对值取余,符号和a一致
-10%3=-10-(-10)/3
3=-1

日期函数

1. NOW获取当前日期和时间

SELECT NOW();

2. CURDATE只获取当前日期

SELECT CURDATE();

3. CURTIME只获取当前时间

SELECT CURTIME();

4. DATEDIFF(date1,date2)获取两个日期之差(天数)

SELECT DATEDIFF('1998-7-16','2019-7-13');

结果为-7667,前面减后面

5. DATE_FORMAT(date,格式)更换日期格式

SELECT DATE_FORMAT('1998-7-16','%Y年%m月%d日 %H小时%i分钟%s秒') 出生日期;
SELECT DATE_FORMAT(hiredate,'%Y年%m月%d日 %H小时%i分钟%s秒') 入职日期
FROM employees;

6. STR_TO_DATE(str,格式) 按指定格式解析字符串为日期

SELECT * FROM employees
WHERE hiredate<STR_TO_DATE('3/15 1998','%m/%d %Y');

分别提取日期的年月日小时分钟秒

date(str)将日期格式转化为年月日
year(date)
month(date)
day(date)
hour(date)
minute(date)
second(date)

流程控制函数

1. IF函数

  • IF(表达式1,表达式2,表达式3)
  • 如果表达式1成立,结果返回表达式2;
  • 如果表达式1不成立,结果返回表达式3
SELECT IF(100>9,'好','坏');

结果返回 好

  • 需求:如果有奖金,则显示最终奖金;如果没有,则显示0
SELECT IF(commission_pct IS NULL,0,salary*12*commission_pct) 奖金
FROM employees;

2. CASE函数

  • 情况1:类似于switch语句,可以实现等值判断
    CASE (表达式)
    WHEN 值1 THEN 结果1
    WHEN 值2 THEN 结果2

    ELSE 结果n (ESLE可省略)
    END

  • 案例:部门编号是30,工资显示为2倍;
    部门编号是50,工资显示为3倍;
    部门编号是60,工资显示为4倍;
    否则不变
    显示部门编号,新工资,旧工资

SELECT department_id,salary,
CASE department_id 
WHEN 30 THEN salary*2 
WHEN 50 THEN salary*3
WHEN 60 THEN salary*4
ELSE salary
END newsalary
FROM employees;
  • 情况2:类似于多重IF语句,实现区间判断
    CASE
    WHEN 条件1 THEN 结果1
    WHEN 条件2 THEN 结果2

    ELSE 结果n
    END

  • 案例:
    如果工资>20000,显示级别A
    如果工资>15000,显示级别B
    如果工资>10000,显示级别C
    否则,显示D
    显示工资和级别

SELECT salary,
CASE
WHEN salary>20000 THEN 'A'
WHEN salary>15000 THEN 'B'
WHEN salary>10000 THEN 'C'
ELSE 'D'
END grade
FROM employees;

案例讲解

  1. 显示系统时间(注:日期+时间)
SELECT NOW();
  1. 查询员工号,姓名,工资以及工资提高百分之20%后的结果(new salary)
SELECT employee_id,last_name,salary,salary*1.2 "new salary"
FROM employees;
  1. 将员工的姓名按首字母排序,并写出姓名的长度(length)
SELECT LENGTH(last_name) 长度
FROM employees
ORDER BY SUBSTR(last_name,1,1) ASC;
  1. 做一个查询,产生下面的结果(拼接)
    earns monthly but wants
    Dream Salary
    King earns 24000 monthly but wants 72000
SELECT CONCAT(last_name,'earns',salary,'monthly but wants',salary*3) "Dream Salary"
FROM employees;
  1. 使用CASE-WHEN,按照下面的条件:
    Job grade
    AD_PRES A
    ST_MAN B
    IT_PROG C
    SA_REP D
    ST CLERK E
    产生下面的结果
    last_name job_id grade
    king AD_PRES A
SELECT last_name,job_id,
CASE job_id
WHEN 'AD_PRES' THEN 'A'
WHEN 'ST_MAN' THEN 'B'
WHEN 'IT_PROG' THEN 'C'
WHEN 'SA_REP' THEN 'D'
WHEN 'ST CLERK' THEN 'E'
END Grade
FROM employees;

注意⚠️:除了数值型,其他都要用引号
没有ELSE,可以不用写

常见函数-分组函数/聚合函数

说明

分组函数往往用于实现将一组数据进行统计计算,最终得到一个值,又称为聚合函数或统计函数

分组函数清单

SUM(字段名):求和
AVG(字段名):求平均数
MAX(字段名):求最大值
MIN(字段名):求最小值
COUNT(字段名):计算非空字段值的个数

特点

  1. 实参的字段类型,SUM和AVG只支持数值型,其他三个可以支持任意类型
  2. 这五个函数都忽略NULL值
  3. COUNT可以支持以下参数:
    COUNT(字段):查询该字段非空值的个数
    COUNT(*):查询结果集的行数
    COUNT(1):查询结果集的行数
  4. 分组函数可以和DISTINCT搭配使用,实现去重的统计
    SELECT COUNT(DISTINCT 字段) FROM 表;
  • 案例1: 查询员工信息表中,所有员工的工资和、工资平均值、最低工资、最高工资、有工资的个数
SELECT SUM(salary),AVG(salary),MIN(salary),MAX(salary),COUNT(salary)
FROM employees; 
  • 案例2:
    查询员工表中记录数;
SELECT COUNT(employee_id) FROM employees;

查询员工表中有佣金的人数;

SELECT COUNT(salary) FROM employees;

查询员工表中月薪大于2500的人数;

SELECT COUNT(salary)
FROM employees
WHERE salary>2500;

查询有领导的人数;

SELECT COUNT(manager_id) FROM employees;

COUNT补充:

  1. 统计结果集的行数,推荐使用count(*)
SELECT COUNT(*FROM employees;
SELECT COUNT(*FROM employees WHERE department_id=30;
SELECT COUNT(1FROM employees;
SELECT COUNT(1FROM employees WHERE department_id=30;

返回结果和前面一样,相当于增加一列常量列,值都为1,最后计算有多少个1

  1. 搭配distinct实现去重的统计
  • 查询有员工的部门个数;
SELECT COUNT(DISTINCT department_id) FROM employees;
  • 查询每个部门的总工资
SELECT SUM(salary),department_id
FROM employees
GROUP BY department_id;

分组查询

当需要分组查询时需要使用GROUP BY子句

语法

SELECT 查询列表(一般为分组函数,被分组的字段)
FROM 表名
WHERE 筛选条件
GROUP BY 分组列表
HAVING 分组后筛选
ORDER BY 排序列表;

  • 执行顺序:
  1. FROM子句
  2. WHERE子句
  3. GROUP BY子句
  4. HAVING子句
  5. SELECT子句
  6. ORDER BY子句

特点

  1. 查询列表往往是分组函数和被分组的字段
  2. 分组列表可以是单个字段或多个字段
  3. 分组查询中的筛选分为两类
筛选的基表 使用的关键词 位置
分组前筛选 原始表FROM后的表 WHERE GROUP BY 前
分组后筛选 分组后的结果集 HAVING GROUP BY后

WHERE—GROUP BY—HAVING
分组函数做条件只可能放在HAVING后面

1. 简单的分组

案例1:查询每个工种(被分组字段)的员工平均工资

SELECT AVG(salary),job_id
FROM employees
GROUP BY job_id;

案例2:每个领导的手下人数

SELECT COUNT(*),manager_id
FROM employees
WHERE manager_id IS NOT NULL
GROUP BY manager_id;

2.可以实现分组前的筛选WHERE

案例1:查询邮箱中包含a字符的每个部门的最高工资

SELECT MAX(salary) 最高工资,department_id
FROM employees
WHERE email LIKE '%a%'
GROUP BY department_id;

案例2:查询每个领导手下有奖金的员工的平均工资

SELECT AVG(salary) 平均工资,manager_id
FROM employees
WHERE commission_pct IS NOT NULL
GROUP BY manager_id;

3. 可以实现分组后的筛选HAVING

案例1:查询哪个部门的员工个数>5

  • 分析1: 查询每个部门的员工个数
SELECT COUNT(*) 员工个数,departmenet_id
FROM employees
GROUP BY department_id;
  • 分析2: 在刚才的结果基础上,筛选哪个部门的员工个数>5
SELECT COUNT(*) 员工个数,departmenet_id
FROM employees
WHERE COUNT(*)>5
GROUP BY department_id;

系统报错,因为执行顺序是FROM子句,WHERE子句,SELECT子句,WHERE子句无法从FROM employees中筛选出COUNT(*)列,应该在GROUP BY分组之后可以筛选。WHERE不支持后面加分组函数做条件。

SELECT COUNT(*) 员工个数,departmenet_id
FROM employees
GROUP BY department_id
HAVING COUNT(*)>5;

HAVING支持后面加分组函数做条件,支持分组后做筛选

案例2:每个工种有奖金的员工的最高工资>12000的工种编号和最高工资

SELECT job_id,MAX(salary)
FROM employees
WHERE commission_pct IS NOT NULL
GROUP BY job_id
HAVING MAX(salary)>12000;

案例3:领导编号>102的每个领导手下的最低工资大于5000的领导编号和最低工资

SELECT manager_id,MIN(salary) 最低工资
FROM employees
WHERE manager_id>102
GROUP BY manager_id
HAVING MIN(salary)>5000;

4. 可以实现排序

  • 案例:查询每个工种有奖金的员工的最高工资>6000的工种编号和最高工资,按最高工资升序
    分析1: 查询每个工种有奖金的员工的最高工资
SELECT MAX(salary) 最高工资,job_id
FROM employees
WHERE commission_pct IS NOT NULL
GROUP BY job_id;

分析2:从前面的结果中筛选最高工资>6000

SELECT MAX(salary) 最高工资,job_id
FROM employees
WHERE commission_pct IS NOT NULL
GROUP BY job_id
HAVING MAX(salary)>6000;

分析3: 按最高工资升序排序

SELECT job_id,MAX(salary) 最高工资
FROM employees
WHERE commission_pct IS NOT NULL
GROUP BY job_id
HAVING MAX(salary)>6000
ORDER BY 最高工资 ASC;

5. 按多个字段分组

  • 案例:查询每个工种每个部门的最低工资,并按最低工资降序
    提示:工种和部门一样才是一组
SELECT MIN(salary) 最低工资,job_id,department_id
FROM employees
GROUP BY job_id,department_id
ORDER BY MIN(salary) DESC;

连接查询

含义

含义:又称多表查询,当查询的字段来自于多个表时,就会用到连接查询

笛卡尔乘积

笛卡尔乘积:表1有m行,表2有n行,结果=m*n行
发生原因:没有有效的连接条件
解决办法:添加有效的连接条件

分类

  • 按年代分类:
    SQL92标准:MySQL中仅支持内连接;
    SQL99标准(推荐):MySQL中支持内连接+外连接(左外和右外)+交叉连接
  • 按功能分类
  1. 内连接:等值连接,非等值连接,自连接
  2. 外连接:左外连接,右外连接,全外连接
  3. 交叉连接

内连接(SQL92语法)

  • 语法:
    SELECT 查询列表
    FROM 表1 别名,表2 别名,…
    WHERE 连接条件
    AND 筛选条件

    GROUP BY分组列表
    HAVING 分组后筛选
    ORDER BY 排序列表
  • 执行顺序:
  1. FROM子句
  2. WHERE子句
  3. AND子句
  4. GROUP BY子句
  5. HAVING子句
  6. SELECT子句
  7. ORDER BY子句

等值连接

语法

SELECT 查询列表(的 后面是什么就写什么)
FROM 表名1 别名1,表名2 别名2,…
WHERE 等值连接的连接条件

特点

  1. 为了解决多表中的字段名重名问题,往往为表起别名,提高语意性
    注意:如果给表起了别名,则查询的字段就不能使用原来的表面去限定。
  2. 多表的顺序无要求
  3. 多表等值连接的结果为多表的交集部分
  4. n表连接,至少需要n-1个连接条件
  5. 可以搭配前面介绍的所有子句使用,比如排序、分组、筛选

①简单的两表连接

案例:查询员工名和部门名

SELECT last_name,department_name
FROM employees e,departments d
WHERE e.deparmtent_id=d.department_id;

当表起了别名后,不能再用原表明限定字段,应该用别名来限定
employees.last_name ❌
e.last_name ☑️

②添加筛选条件

案例1:查询部门编号>100的部门名和所在的城市名

SELECT department_name,city
FROM departments d,locations l
WHERE d.location_id=l.location_id
AND d.department_id>100;

案例2:查询有奖金的员工名、部门名

SELECT last_name,department_name
FROM employees e,departments d
WHERE e.department_id=d.department_id
AND e.commission_pct IS NOT NULL;

案例3:查询城市名中第二个字符为o的部门名和城市名

SELECT department_name,city
FROM departments d,locations l
WHERE d.location_id=l.location_id
AND city LIKE '_o%';

③添加分组+筛选

案例1:查询每个城市的部门个数

SELECT COUNT(*) 部门个数,city
FROM locations l,departments d
WHERE l.location_id=d.location_id
GROUP BY city;

案例2:查询有奖金的每个部门的部门名、部门的领导编号和该部门的最低工资

SELECT department_name,d.manager_id,MIN(salary)
FROM departments d,employees e
WHERE d.department_id=e.department_id
AND e.commission_pct IS NOT NULL
GROUP BY department_name,d.manager_id;

④添加分组+筛选+排序

案例1:查询部门中员工个数>10的部门名,并按部门名降序

SELECT COUNT(*) 员工个数,department_name
FROM employees e,departments d
WHERE e.department_id=d.department_id
GROUP BY department_name
HAVING COUNT(*)>10
ORDER BY department_name DESC;

案例2:查询每个工种的员工个数和工种名,并按员工个数降序

SELECT COUNT(*) 员工个数,job_title
FROM employees e,jobs j
WHERE e.job_id=j.job_id
GROUP BY job_title
ORDER BY COUNT(*) DESC;

⑤三表连接

案例:查询员工名、部门名和所在的城市名

SELECT last_name,department_name,city
FROM employees e,departments d,locations l
WHERE e.department_if=d.department_id
AND d.location_id=l.location_id;

非等值连接

案例1: 查询员工的工资和工资级别

SELECT salary,grade_level
FROM employees e,job_grades g
WHERE salary BETWEEN g.lowest_sal AND g. highest_sal;

自连接

案例:查询员工编号、员工名以及上级的名称

SELECT e.employee_id,e.last_name,m.manager_id,m.last_name
FROM employees e,employees m
WHERE e.manager_id=m.employee_id;

内连接(SQL99语法)

  • 语法:
    SELECT 查询列表
    FROM 表名1 列名
    INNER JOIN 表名2 别名 ON 连接条件 (INNER可省略)
    INNER JOIN 表名3 别名 ON 连接条件
    WHERE 筛选条件
    GROUP BY 分组列表
    HAVING 分组后筛选
    ORDER BY 排序列表;

  • SQL92和SQL99的区别:
    SQL99使用JOIN关键字代替SQL92中的逗号连接各表,并且将连接条件ON和筛选条件WHERE进行了分离,阅读性更强

等值连接

1. 简单连接

案例:查询员工名和部门名

SELECT last_name,department_name
FROM employees e
INNER JOIN departments d
ON e.department_id=d.e.department_id;

2. 添加筛选条件

案例:查询部门编号>100的部门名和所在的城市名

SELECT department_name,city
FROM departments d
INNER JOIN locations l
ON d.location_id=l.location_id
WHERE d.department_id>100;

3. 添加分组+筛选

案例:查询每个城市的部门个数

SELECT COUNT(*) 部门个数,l.city
FROM departments d
INNER JOIN locations l
ON d.location_id=l.location_id
GROUP BY l.city;

select子句用city取代l.city可以吗???

4. 添加分组+筛选+排序

案例:查询部门员工个数>10的部门名,并按员工个数降序

SELECT COUNT(*) 员工个数,d.department_name
FROM employees e
INNER JOIN departments d
ON e.department_id=d.department_id
GROUP BY d.department_id
HAVING 员工个数>10
ORDER BY 员工个数 DESC;

HAVING支持别名

非等值连接

案例:查询部门编号在10-90之间的每个工资级别的员工个数

SELECT COUNT(*) 员工个数,grade
FROM employees e
INNER JOIN sal_grade g
ON e.salary BETWEEN g.min_salary AND g.max_salary
WHERE e.department_id BETWEEN 10 AND 90
GROUP BY g.grade;

自连接

案例:查询员工名和对应的领导名

SELECT e.last_name,m.last_name
FROM employees e
INNER JOIN employees m
ON e.manager_id=m.manager_id;

测试题讲解

案例:将当前日期显示成xxxx年xx月xx日

SELECT date_format(now(),'%Y年%m月%d日');

案例:
已知学员信息表stuinfo: stuId, stuName, gender, majorId
已知专业表major: id, majorName
已知成绩表result: id, majorId, stuId, score

  • 查询所有男生的姓名、专业名和成绩,使用SQL92和SQL99两种语法方式实现
SELECT s.stuname,m.majorname,r.score
FROM stuinfo s,major m,result r
WHERE s.majorid=m.id AND r.stuid=s.stuid
AND s.gender='男';
SELECT s.stuName,m.majorName,r.score
FROM stuinfo s
INNER JOIN major m ON s.majorid=m.id
INNER JOIN result r ON s.stuid=r.stuid
WHERE s.gender='男';
  • 查询每个性别的每个专业的平均成绩,并按平均成绩降序
SELECT AVG(score) 平均成绩,s.majorid,s.gender
FROM stuinfo s
INNER JOIN result r
ON s.majorid=r.majorid
GROUP BY s.gender,s.majorid    (用r.majorid也可)
ORDER BY 平均成绩 DESC;

外连接(SQL99语法)

说明

查询结果为主表中的所有记录,如果从表有匹配项,则显示匹配项;如果从表没有匹配项,则显示为NULL值

应用场景:一般用于查询主表中有但从表没有的记录

特点

  1. 外连接分主从表,两表的顺序不能任意调换
  2. 左外连接,LEFT JOIN左边为主表;右外连接,RIGHT JOIN右边为主表;FULL JOIN全外连接(MySQL不支持)
  3. 外连接的查询结果=内连接的查询结果+主表有但从表没有的记录
  4. 一般来讲,外连接往往用于查询主表有但从表没有的记录

语法

SELECT 查询列表
FROM 表1 别名
LEFT/RIGHT/FULL OUTER JOIN 表2 别名 (OUTER可省略)
ON 连接条件
WHERE 筛选条件
GROUP BY
HAVING
ORDER
LIMIT;

案例讲解

  • 案例:查询哪个部门没有员工,并显示其部门编号和部门名
SELECT d.department_id,d.department_name
FROM departments d
LEFT JOIN employees e ON d.department_id=e.department_id
WHERE e.employee_id IS NULL;

筛选条件用从表主键列,因为主键列没有NULL值,如果为NULL值,一定是因为没有匹配的原因。如果用其他列,可能存在某些本身就为NULL值。

MySQL学习笔记_第4张图片
MySQL学习笔记_第5张图片

  • 查询哪个城市没有部门
SELECT l.city
FROM departments d
RIGHT JOIN loctations l ON l.location_id=d.location_id
WHERE d.department_id IS NULL;
  • 查询部门名为SAL或IT的员工信息
SELECT d.*,e.*
FROM departments d
LEFT JOIN employees e ON d.department_id=e.department_id
WHERE d.department_name='SAL' OR d.department_name='IT';

子查询

说明

当一个查询语句中又嵌套了另一个完整的SELECT语句,则被嵌套的select语句,称为子查询或内查询,外面的select语句称为主查询或外查询

子查询不一定必须出现在select语句内部,只是出现在select语句内部的时候较多

分类

按子查询出现的位置进行分类:

  1. select后面,要求:子查询结果为单行单列(标量子查询)
  2. from后面,相当于一张表,表的结构几行几列都行。要求:子查询结果可以为多行多列
  3. where或having后面(最常见),要求子查询结果必须为单列,可以是单行单列(单行子查询),也可以是多行单列(多行子查询)
  4. exists后面,要求:几行几列都行(相关子查询)

特点

  1. 子查询放在WHERE后面筛选条件中,要求必须放在条件的右侧
  2. 子查询一般放在小括号中
  3. 子查询的执行优先于主查询
  4. 单行子查询:结果集为单行单列,一般搭配关系运算符使用(> < >= <= = <>)
  5. 多行子查询:结果集为多行单列,一般搭配多行操作符使用(in,any/some, all)

单行子查询

  • 案例:查询和Zlotkey相同部门的员工姓名和工资

第一步:查询Zlotkey的部门编号

SELECT department_id
FROM employees
WHERE last_name='Zlotkey';

第二步:查询department_id=1.题的员工姓名和工资

SELECT last_name,salary
FROM employees
WHERE department_id=(
      SELECT department_id
      FROM employees
      WHERE last_name='Zlotkey');
  • 案例:查询工资比公司平均工资高的员工的员工号,姓名和工资

第一步:查询平均工资

SELECT AVG(salary)
FROM employees;

第二步:查询salary>第一步结果的信息

SELECT employee_id,last_name,salary
FROM employees
WHERE salary>(SELECT AVG(salary) FROM employees);
  • 案例:查询最低工资大于50号部门最低工资的部门id和其最低工资

第一步:查询50号部门的最低工资

SELECT MIN(salary)
FROM employees
WHERE department_id=50;

第二部:查询最低工资>第一步结果的部门ID和最低工资

SELECT department_id,MIN(salary)
FROM employees
GROUP BY department_id
HAVING MIN(salary)>(
       SELECT MIN(salary)
       FROM employees
       WHERE department_id=50
);
  • 案例:查询工资最高的员工的姓名,要求first_name和last_name显示为一列,列名为姓.名

1⃣️查询最高工资

SELECT MAX(salary)
FROM employees;

2⃣️查询结果1⃣️的员工

SELECT CONCAT(first_name,last_name) "姓.名"
FROM employees
WHERE salary=(
      SELECT MAX(salary)
      FROM employees
);

多行子查询

  • in:判断某字段是否在指定列表内

  • any/some:判断某字段的值是否满足其中任意一个
    x>any(10,30,50)等价于x>min(10)
    x=any(10,30,50)等价于x in(10,30,50)

  • all:判断某字段的值是否满足里面所有的
    x>all(10,30,50)等价于x>max(10,30,50)
    all和any语意性较差,一般都可以被代替

  • 案例:返回location_id是1400或1700的部门中的所有员工姓名
    第一步:查询location_id是1400或1700的部门

SELECT department_id
FROM departments
WHERE location_id IN(1400,1700);

结果是多行单列

第二步:查询department_id=第一步的姓名

SELECT last_name
FROM employees
WHERE department_id IN(
      SELECT DISTINCT department_id
      FROM departments
      WHERE location_id IN(1400,1700)
      );
  • 案例:返回其它部门中比job_id为‘IT_PROG’部门任一工资低的员工的员工号、姓名、job_id 以及salary
    第一步:查询job_id为‘IT_PROG’部门的工资
SELECT DISTINCT salary
FROM employees
WHERE job_id='IT_PROG';

第二步:查询其他部门工资<任意一个第一步的结果

SELECT employee_id,last_name,job_id,salary
FROM employees
WHERE salary<ANY(
      SELECT DISTINCT salary
      FROM employees
      WHERE job_id='IT_PROG'
);

等价于

SELECT employee_id,last_name,job_id,salary
FROM employees
WHERE salary<(
      SELECT MAX(salary)
      FROM employees
      WHERE job_id='IT_PROG'
);
  • 案例:返回其它部门中比job_id为‘IT_PROG’部门所有工资都低的员工的员工号、姓名、job_id 以及salary

第一步:查询job_id为‘IT_PROG’部门的工资

SELECT DISTINCT salary
FROM employees
WHERE job_id='IT_PROG';

第二步:查询其他部门工资<所有第一步的结果

SELECT employee_id,last_name,job_id,salary
FROM employees
WHERE salary<ALL(
      SELECT DISTINCT salary
      FROM employees
      WHERE job_id='IT_PROG'
);

等价于

SELECT employee_id,last_name,job_id,salary
FROM employees
WHERE salary<(
      SELECT MIN(salary)
      FROM employees
      WHERE job_id='IT_PROG'
);

放在其他位置的子查询

放在SELECT后面(较少使用,往往可以被代替)

  • 案例:查询部门编号是50的员工个数
SELECT
(
SELECT COUNT(*)
FROM employees
WHERE department_id=50
) 个数;

放在FROM后面(表子查询)

  • 案例:查询每个部门的平均工资的工资级别

1⃣️查询每个部门的平均工资

SELECT AVG(salary),department_id
FROM employees
GROUP BY department_id;

2⃣️将1⃣️和sal_grade两表连接查询

SELECT dep_ag.department_id,ep_ag.ag,g.grade
FROM sal_grade g
JOIN(
     SELECT AVG(salary) ag,department_id
     FROM employees
     GROUP BY department_id
) dep_ag ON dep_ag.ag BETWEEN g.min_salary AND g.max_salary;

放在EXISTS后面

  • 案例1: 查询有无名字叫“张三丰”的员工信息
SELECT *
FROM employees
WHERE last_name='张三丰';

没有显示结果,表示没有名字叫张三丰的员工信息

SELECT EXISTS(
       SELECT *
       FROM employees
       WHERE last_name='张三丰'
) 有无张三丰;

结果显示为0,0代表没有(FALSE),1代表有(TRUE)

案例讲解

  1. 查询各部门中工资比本部门平均工资高的员工的员工号、姓名和工资

1⃣️查询各部门平均工资

SELECT department_id,AVG(salary) ag
FROM employees
GROUP BY department_id;

2⃣️将1⃣️结果和employees表连接查询

SELECT employee_id,last_name,salary
FROM employees e
JOIN(
     SELECT department_id,AVG(salary) ag
     FROM employees
     GROUP BY department_id
) dep_ag ON e.department_id=dep_ag.department_id
WHERE e.salary>dep_ag.ag;
  1. 查询和姓名中包含字母u的员工在相同部门的员工的员工号和姓名

1⃣️查询姓名中包含字母u的员工所在部门

SELECT DISTINCT department_id
FROM employees
WHERE last_name LIKE '%u%'

2⃣️查询结果1⃣️部门中的员工的员工号和姓名

SELECT employee_id,last_name
FROM employees
WHERE department_id IN(
      SELECT department_id
      FROM employees
      WHERE last_name LIKE '%u%'
);
  1. 查询管理者是King的员工姓名和工资

1⃣️查询管理者是King的员工编号

SELECT employee_id
FROM employees
WHERE last_name='King';

2⃣️查询管理者编号为1⃣️的员工的姓名和工资

SELECT last_name,salary
FROM employees
WHERE manager_id IN(
      SELECT employee_id
      FROM employees
      WHERE last_name='King'
);
  1. 查询平均工资最低的部门信息和该部门的平均工资

1⃣️查询各部门的平均工资中最低的

SELECT department_id,AVG(salary) ag
FROM employees
GROUP BY department_id
ORDER BY ag ASC
LIMIT 1;

2⃣️连接结果1⃣️和departments表

SELECT d.*,dep_ag.ag
FROM departments d
JOIN (
       SELECT department_id,AVG(salary) ag
      FROM employees
      GROUP BY department_id
      ORDER BY ag ASC
      LIMIT 1
) dep_ag ON d.department_id=dep_ag.department_id;
  1. 各个部门中,最高工资中最低的那个部门的最低工资是多少
    1⃣️查询各部门的最高工资中最低的
SELECT department_id
FROM employees
GROUP BY department_id
ORDER BY MAX(salary) ASC
LIMIT 1;

2⃣️查询部门便是是1⃣️的部门的最低工资

SELECT MIN(salary),department_id
FROM employees
WHERE department_id=(
      SELECT department_id
      FROM employees
      GROUP BY department_id
      ORDER BY MAX(salary) ASC
      LIMIT 1
);

分页查询

应用场景:当页面上的数据一页显示不全,则需要分页显示
分页查询的sql命令请求数据库服务器—>服务器响应查询到的多条数据—>前台页面

语法和执行顺序

SELECT 查询列表
FROM 表1 别名
JOIN 表2 别名
ON 连接条件
WHERE 筛选条件
GROUP BY 分组
HAVING 分组后筛选
ORDER BY 排序列表
LIMIT 起始索引,显示的条目数

执行顺序:

  1. FROM子句
  2. JOIN子句
  3. ON子句
  4. WHERE子句
  5. GROUP BY子句
  6. HAVING子句
  7. SELECT子句
  8. ORDER BY子句
  9. LIMIT子句

特点

  1. 起始条目索引如果不写,默认是0
  • 案例:查询员工信息表的前5条
SELECT * FROM emploees LIMIT 0,5;

完全等价于

SELECT * FROM emploees LIMIT 5;
  1. LIMIT后面支持两个参数
    参数1: 显示的起始条目索引
    参数2: 条目数
  • 案例:查询有奖金的,且工资较高的第11名到第20名
SELECT * FROM employees
WHERE commission_pct IS NOT NULL
ORDER BY salary DESC
LIMIT 10,10;
  1. 公式
    假如要显示的页数是page,每页显示的条目数为size
SELECT *
FROM employees
LIMIT (page-1)*size,size;

page size=10
1 LIMIT 0,10
2 LIMIT 10,10
3 LIMIT 20,10
4 LIMIT 30,10

联合查询

说明:当查询结果来自于多张表,但多张表之间没有关联,这个时候往往使用联合查询,也称UNION查询

语法

完整的SELECT语句1 UNION
完整的SELECT语句2 UNION
完整的SELECT语句3 UNION

完整的SELECT语句n

  • 案例:查询所有国家的年龄>20岁的用户信息
SELECT * FROM chinese WHERE age>20 UNION
SELECT * FROM usa WHERE uage>20;

特点

  1. 多条待联合的查询语句的查询列数必须一致;列数一致但不对应,有结果但没有意义
  • 案例:查询所有国家的用户姓名和年龄
SELECT uname,uage FROM usa
UNION
SELECT age,'name' FROM chinese;
  1. UNION默认去重/UNION ALL实现全部查询,包含重复项

DDL数据定义语言

说明:Data Define Language数据定义语言,用于对数据库和表的管理和操作

库的管理

创建数据库

CREATE DATABASE 库名;
CREATE DATABASE IF NOT EXISTS 库名;

删除数据库

DROP DATABASE 库名;
DROP DATABASE IF EXISTS stuDB;

表的管理

常见的数据类型

1. 整型

  • int:整型, TINYINT, SMALLINT, INT, BIGINT

2. 浮点型

  • double(m,n) / float(m,n):浮点型,例如double(5,2)表示最多5位,其中必须有2位小数,能保存的数值范围为-999.99到999.99,(m,n)可省略
  • decimal(m,n):浮点型,在表示钱方面使用该类型,精确度最高, (m,n)可省略
    n: 小数点后最多保留的位数
    m: 整数+小数最多保留的位数

3. 字符型

  • char(n):固定长度字符串类型,(n)可省略,n表示最多字符个数
  • varchar(n):可变长度字符串类型,(n)不可省略
  • text:字符串类型;表示存储较长文本
意思 格式 n的解释 特点 效率
Char 固定长度字符 Char(n) 最大字符个数可以省略不写CHAR,默认:1 不管实际存储,开辟的空间都是n个字符
Varchar 可变长度字符 Varchar(n) 最大的字符个数,必选 根据实际存储决定开辟的空间

4. 日期型

  • date:日期类型,格式为:yyyy-MM-dd;
  • time:时间类型,格式为:hh:mm:ss
  • timestamp/datetime:日期+时间 yyyyMMdd hhmmss
保存范围 所占字节
Datetime 1900-1-1~xxxx年 能表示的日期范围较大 8
Timestamp 1970-1-1~2038-12-31 能表示的日期范围较小 4

5. 二进制型

  • blob:存储图片数据,字节类型;//jpg mp3 avi

6个常见约束

说明:用于限制表中字段的数据,从而进一步保证数据表的数据是一致的、准确的、可靠的

  • NOT NULL非空约束:用于限制该字段为必填项
  • DEFAULT 默认约束:用于限制该字段没有显示插入值,则直接显示默认值
  • PRIMARY KEY 主键约束:用于限制该字段的值不能重复,设置为主键列的字段默认不能为空。一个表只能有一个主键,也可以是组合主键
  • UNIQUE 唯一约束:用于限制该字段的值不能重复,但该字段可以为空,一个表可以有n个唯一
  • CHECK 检查约束:用于限制该字段的值必须满足指定条件,MySQL中不支持
    CHECK(age BETWEEN 1 AND 100)
  • FOREIGN KEY 外键约束:用于限制两个表的关系,要求外键列的值必须来自于主表的关联列
    要求:
  1. 主表的关联列和从表的关联列的类型和意义必须一致,名称无要求
  2. 主表的关联列必须是主键
  • 分类:表级约束和列级约束
  • 支持列级约束:NOT NULL,DEFAULT,UNIQUE,PRIMARY KEY,CHECK
  • 支持表级约束:UNIQUE,PRIMARY KEY,FOREIGN KEY
  • 案例:列级约束
CREATE TABLE stuinfo(
       id INT PRIMARY KEY,
       name VARCHAR(20) UNIQUE,
       gender CHAR default '男',
       age INT CHECK(age>=1 AND age<=100),
       email VARCHAR(20) NOT NULL,
       majorid INT,
       CONSTRAINT fk FOREIGN KEY(majorid) REFERENCES major(id)
);       
  • 案例:表级约束
CREATE TABLE stuinfo(
       id INT,
       name VARCHAR(20),
       gender CHAR default '男',
       age INT CHECK(age>=1 AND age<=100),
       email VARCHAR(20) NOT NULL,
       majorid INT,
       PRIMARY KEY(id),#添加主键
       CONSTRAINTUNIQUE(name),#添加唯一建
       CONSTRAINT fk FOREIGN KEY(majorid) REFERENCES major(id)
);       

创建表

  • 语法
CREATE TABLE IF NOT EXISTS 表名(
       字段名    字段类型   「字段约束」可省略,
       字段名    字段类型   「字段约束」,
       字段名    字段类型   「字段约束」,
       字段名    字段类型   「字段约束」
);
    
  • 案例:没有添加约束
CREATE TABLE IF NOT EXISTS stuinfo(
       stuin INT,
       stuname VARCHAR(20),
       stugender CHAR,
       email VARCHAR(20),
       borndate DATETIME
);
  • 案例:添加约束
CREATE TABLE IF NOT EXISTS stuinfo(
       stuin INT PRIMARY KEY, #添加了主键约束
       stuname VARCHAR(20) UNIQUE NOT NULL,#添加了唯一和非空约束
       stugender CHAR DEFAULT '男',#添加了默认约束
       email VARCHAR(20) NOT NULL,#添加了非空约束
       age INT CHECK(age BETWEEN 1 AND 100)),#添加了检查约束,但Mysql不支持
       majorid INT,#外键约束不能直接在后面加,需要在下一行
       CONSTRAINT fk_stuinfo_major FOREIGN KEY (majorid) REFERENCES major(id)#添加外键约束 constraint+(约束名,不写的话提供默认名)+ foreign key + 从表列 + references + 主表列

修改表

语法

ALTER TABLE 表名 ADD/MODIFY/CHANGE/DROP COLUMN 字段名 字段类型 字段约束(可省略);

modify改列的类型
change改列名

1. 修改表名 RENAME TO

  • ALTER TABLE 表名 RENAME TO 新表名
ALTER TABLE stuinfo RENAME TO students;

2. 添加字段 ADD

  • ALTER TABLE 表名 ADD COLUMN 新字段名 字段类型 (约束)
ALTER TABLE students ADD COLUMN borndate TIMESTAMP NOT NULL;

3. 修改字段名 CHANGE

  • ALTER TABLE 表名 CHANGE COLUMN 旧字段名 新字段名 字段类型 (约束)
ALTER TABLE students CHANGE COLUMN borndate birthday(新字段名) DATETIME NULL;

4. 修改字段类型 MODIFY

  • ALTER TABLE 表名 MODIFY COLUMN 字段名 字段类型 (约束)
ALTER TABLE students MODIFY COLUMN birthday TIMESTAMP;

5. 删除字段 DROP

  • ALTER TABLE 表名 DROP COLUMN 字段名
ALTER TABLE students DROP COLUMN birthday;

删除表

DROP TABLE IF EXISTS 表名;

复制表

复制表的结构

CREATE TABLE 新表名 LIKE 旧表;

复制表的结构+数据

  • CREATE TABLE 表名 子查询;
CREATE TABLE newTable1 SELECT * FROM girls.beauty;
  • 案例:复制employee表中的last_name,department_id,salary字段到新表emp表,但不复制数据
CREATE TABLE emp
SELECT last_name,department_id,salary
FROM myemployees,employees
WHERE 1=2; #1=2表示恒不成立

测试题讲解

  1. 使用分页查询,查询员工信息表中部门为50号的工资最低的5名员工信息
SELECT *
FROM employees
WHERE department_id=50
ORDER BY salary ASC
LIMIT 5;
  1. 使用子查询实现城市为Toronto的,且工资>10000的员工姓名

第一步:查询城市为Toronto的部门编号

SELECT department_id
FROM departments d
JOIN locations l
ON d.location_id=l.location_id 
WHERE city='Toronto';

第二步:查询结果1中工资>10000的员工姓名

SELECT last_name
FROM employees
WHERE salary>10000
AND department_id IN(
      SELECT department_id
      FROM departments d
      JOIN locations l
      ON d.location_id=l.location_id 
      WHERE city='Toronto'
);
  1. 创建表qqinfo,里面包含qqid(添加主键约束)、昵称nickname(添加唯一约束)、邮箱email(添加非空约束)、性别gender
CREATE TABLE IF NOT EXISTS qqinfo(
       qqid INT PRIMARY KEY,
       nickname VARCHAR(20) UNIQUE,
       email VARCHAR(20) NOT NULL,
       gender CHAR
);
  1. 删除表qqinfo
DROP TABLE IF EXISTS qqinfo;
  1. 写出SQL查询语句的定义顺序和执行顺序
  • 定义顺序(书写顺序)
    select distinct 查询列表
    from 表名1 别名
    join 表名2 别名
    on 连接条件
    where 筛选条件
    group by 分组列表
    having 分组后筛选
    order by 排序列表
    limit 条目数

  • 执行顺序
    from 子句
    join 子句
    on子句
    where 子句
    group by子句
    having 子句
    select 子句
    order by 子句
    limit 子句

DML数据操纵语言

  • DML(Data Manipulation Language)数据操纵语言
  • insert/update/delete实现对表中数据的增删改

插入数据

语法

  • 插入单行
INSERT INTO 表名(字段名1,字段名2...) VALUES(1,值2...)
  • 插入多行
INSERT INTO 表名(字段名1,字段名2...) 
VALUES(1,值2...),(1,值2...),(1,值2...),(1,值2...);

特点

  1. 字段和值一一对应,字段类型、约束等必须匹配
  2. 数值型的值不用单引号;非数值型的值必须使用单引号
  3. 字段顺序无要求,只要和值一一对应
  • 案例1:要求字段和值列表一一对应,且遵循类型和约束的限制
INSERT INTO stuinfo(stuid,stuname,stugender,email,age,majorid)
VALUES(1,'吴倩','男','[email protected]',12,1);
  • 案例2: 可以为空字段如何插入(age NULLABLE)

方法一:字段名和值都不写

INSERT INTO stuinfo(stuid,stuname,stugender,email,majorid)
VALUES(1,'吴倩','男','[email protected]',1);

方法二:写字段名,值写NULL

INSERT INTO stuinfo(stuid,stuname,stugender,email,age,majorid)
VALUES(1,'吴倩','男','[email protected]',NULL,1);
  • 案例3:默认字段如何插入(stugender默认为男)

方法一:字段名和值都不写

INSERT INTO stuinfo(stuid,stuname,email,majorid)
VALUES(1,'吴倩','[email protected]',1);

方法二:写字段名,值写DEFAULT

INSERT INTO stuinfo(stuid,stuname,stugender,email,majorid)
VALUES(1,'吴倩',DEFAULT,'[email protected]',1);
  • 案例4:可以省略字段名列表,默认所有字段
INSERT INTO stuinfo VALUES(1,'林忆莲',DEFAULT,'[email protected]',12,3);

设置自增长列

  • 特点
  1. 自增长列要求必须设置在一个键上,例如主键或唯一键
  2. 自增长列要求数据类型为数值型
  3. 一个表至多有一个自增长列
  • 操作方法
  1. 可视化界面操作
  2. SQL语言操作
CREATE TABLE 表名(
       字段名 字段类型 约束 AUTO_INCREMENT,
)
CREATE TABLE gradeinfo(
       gradeid INT PRIMARY KEY AUTO_INCREMENT,#设置gradeid为自增长列
       gradename VARCHAR(20)
);

写法一:自增长列对应值可以写NULL

INSERT INTO gradeinfo VALUES(NULL,'1年级'),(NULL,'2年级'),(NULL,'4年级');

写法二:直接不写自增长列和对应值

INSERT INTO gradeinfo(gradename) VALUES('1年级'),('2年级'),('4年级');

修改数据

  • 语法:
UPDATE 表名 SET 字段名1=新值1,字段名2=新值2,......
WHERE 筛选条件;
UPDATE stuinfo SET majorid=3,email='[email protected]'
WHERE age<20;

删除数据,不影响表的结构

  • 方式1:delete语句,删除整行
    语法:
DELETE FROM 表名
WHERE 筛选条件;

案例1:删除姓李的所有信息

DELETE FROM stuinfo
WHERE stuname LIKE '李%';
  • 方式2:truncate语句,删除表中所有数据,不影响表的结构
    语法:
TRUNCATE TABLE 表名;

案例2:删除表中所有数据

TRUNCATE TABLE stuinfo;

面试题:delete和truncate的区别

  1. delete可以添加where条件,删除部分数据;
    truncate不能添加where条件,一次性清楚所有数据
  2. truncate的效率较高
  3. 如果删除带自增长列的表
    使用delete删除后,重新插入数据,记录从断电处开始
    使用truncate删除后,重新插入数据,记录从1开始
  4. delete删除数据,会返回受影响的行数
    truncate删除数据,不会返回受影响的行数
  5. delete删除数据,可以支持事物回滚
    truncate删除数据,不支持事物回滚

事务

概念

一个事务是由一条或者多条sql语句构成,这一条或者多条sql语句要么全部执行成功,要么全部执行失败

事务的四大特性(ACID)

  • 原子性(Atomicity):事务中所有操作是不可再分割的原子单位。事务中所有操作要么全部执行成功,要么全部执行失败
  • 一致性(Consistency):事务执行后,数据库状态与其它业务规则保持一致。如转账业务,无论事务执行成功与否,参与转账的两个账号余额之和应该是不变的
  • 隔离性(Isolation):隔离性是指在并发操作中,不同事务之间应该隔离开来,使每个并发中的事务不会相互干扰
  • 持久性(Durability):一旦事务提交成功,事务中所有的数据操作都必须被持久化到数据库中,即使提交事务后,数据库马上崩溃,在数据库重启时,也必须能保证通过某种机制恢复数据

分类

  • 隐式事务:没有明显的开启和结束标记
    比如DML语句中insert、update、delete语句本身就是一条事务
INSERT INTO stuinfo VALUES(1,'john','男','[email protected]',12)

如果字段和值不对应,整条语句都无法插入,而不是只有不对应字段和值无法插入

  • 显式事务:具有明显的开启和结束标记
    一般由多条SQL语句组成,必须具有明显的开启和结束标记
    步骤:
  1. 取消隐式事务自动开启的功能
SET autocommit=0;
  1. 开启事务
START TRANSACTION;
  1. 编写事务需要的SQL语句(1条或多条)

将张三的钱-5000

UPDATE stuinfo SET balance=balance-5000 WHERE stuid=1;

将赵四的钱+5000

UPDATE stuinfo SET balance=balance+5000 WHERE stuid=2;
  1. 结束事务
  • 提交
COMMIT
  • 回滚
ROLLBACK

select * from data.dataanalyst
where city = ‘上海’
and education = ‘本科’
or worker = ‘1-3年’

and优先计算,实际运算逻辑如下
select * from data.dataanalyst
where (city = ‘上海’
and education = ‘本科’)
or worker = ‘1-3年’

  • 遇到复杂逻辑,勤用括号()
    在select语句中使用别名,但别名不能立刻在select语句中使用。

  • locate(substr,str)定位函数,获取substr在str中第一次出现的索引.和Excel里find函数功能一样
    locate(‘k’,salary):获取k在salary(10k-15k)中的位置
    locate(substr,str,从哪个位置开始查找)也可添加第三个参数
    和instr函数功能相同,INSTR(str,substr):获取substr在str中第一次出现的索引

  • left join应用:排除法统计

  • 新人刚进公司需要搞清楚表与表之间的关系和逻辑

  • leetcode.com 牛客网 刷题

  • date(str)将日期格式转化为年月日

  • date_add(str,interval 1 day/week/month)加上一天

  • power Bi连接mysql,提前写好SQL查询,占用服务器性能,直接返回结果到PowerBI;如果用原始数据在PowerBI里进行查询,会占用自己电脑的性能

你可能感兴趣的:(mysql,数据库,sql)