赞
踩
前言
MySQL在面试和工作中非常重要,故在此整理学习MySQL时的知识点,作为一个学习笔记。
文中内容出自尚硅谷,链接:尚硅谷MySQL数据库教程天花板,mysql安装到mysql高级。
非常感谢尚硅谷的视频教程!
2. count(*),count(1),count(column)区别
1.基本sql语句
- SELECT 字段1,字段2
- FROM 表名
- WHERE 过滤条件;
- SELECT 字段1,字段2 "字段2的别名"
- FROM 表名
- WHERE 过滤条件;
3.去重
- SELECT DISTINCT 字段1
- FROM 表名;
4.空值参与运算
books表:
bookID | bookname | discount | price |
1 | go | 1 | 50 |
2 | mysql | null | 60 |
3 | linux | 0.8 | 70 |
- #IFNULL(字段, 1) 如果字段不为空,则使用字段原有值;如果字段为空,则用1替换
- SELECT bookname, price * IFNULL(discount,1) "cost"
- FROM books;
5.显示表结构
DESC 表名;
比较运算符用来对表达式左边的操作数和游标的操作数进行比较,比较结果为真返回1,比较结果为假返回0,其他情况返回NULL。
比较运算符经常被用来作为select查询语句的条件来使用,返回符合条件的结果记录。
- SELECT 0 = 'a' # 整数与字符串比较,mysql会将字符串转化为数字进行比较
- FROM dual;
-
-
- SELECT 'a' = 'b' # 字符串之间比较,其比较的是每个字符串中字符的ASCII编码是否相等
- FROM dual;
安全等于运算符(<=>)与等于运算符作用相似,唯一的区别就是安全等于可以用来对NULL进行判断。使用安全等于运算符时,两边的操作数都为NULL时,返回的结果为1;当一个操作数为NULL时,其返回值为0。
SELECT NULL = NULL, NULL <=> NULL, 1 = NULL, 1 <=> NULL;
运算符 | 作用 |
is null | 判断值、字符串或者表达式是否为空 |
is not null | 判断值、字符串或者表达式是否不为空 |
least | 在多个值中返回最小值 |
greatest | 在多个值中返回最大值 |
between...and... | 判断一个值是否在两个值之间,包含边界值 |
isnull | 判断值、字符串或者表达式是否为空 |
in | 判断值是否为列表中任意的一个值 |
not in | 判断一个值是否不是一个列表中的任意一个值 |
like | 判断一个值是否符合模糊匹配规则 |
- SELECT bookName
- FROM books
- WHERE discount IS NOT NULL;
- SELECT bookName
- FROM books
- WHERE discount IS NULL;
- SELECT bookName, price
- FROM books
- WHERE price
- BETWEEN 50 AND 70;
- SELECT bookName, price
- FROM books
- WHERE price IN (50,60);
- SELECT bookName
- FROM books
- WHERE bookName
- LIKE '%i%';
- SELECT bookName
- FROM books
- WHERE bookName
- # _ 代表一个不确定的字符
- LIKE '__n%';
使用limit的好处:约束返回结果的数量可以减少数据表的网络传输量,也可以提升查询效率。如果知道返回结果只有一条,就可以使用limit 1,告诉select语句只需要返回一条记录即可。好处是select不需要扫描完整的表,只需要检索到一条符合条件的记录即可返回。
limit后第一个参数表示开始的下标,第二个参数表示个数
注意:limit必须放在整个select语句的最后。
- SELECT 字段1,字段2
- FROM 表名
- WHERE 过滤条件
- ORDER BT 字段 ASC/DESC
- LIMIT (pageNum - 1) * pageSize, pageSize;
- SELECT bookName, price
- FROM books
- ORDER BY price DESC;
- SELECT bookName, price
- FROM books
- LIMIT 10,10;
emp_id |
first_name |
last_name |
phone |
job_id |
salary |
commission_pct |
manager_id |
dept_id |
dept_id |
dept_name |
manager_id |
location_id |
location_id |
street_address |
postal_code |
city |
state_province |
country_id |
多表查询的结果数为多个表中数据条目数的乘积,笛卡尔积会把第一张表的每一个值与第二张表的每一个值进行匹配。
为避免笛卡尔积,可以在where加入有效的连接条件
- SELECT table1.column, table2.column
- FROM table1, table2
- WHERE table1.column1 = table2.column2; #连接条件
内连接:合并具有同一列的两个以上的表的行,结果集中不包含一个表与另一个表不匹配的行。
- SELECT 字段列表
- FROM A表 INNER JOIN B表
- ON 关联条件
- WHERE 等其他子句;
外连接:两个表在连接过程中除了返回满足连接条件的行以外还返回左表或右表中不满足条件的行,这种连接也称为左外连接或右外连接。没有匹配的行时,结果表中对应的列为空。MySQL不支持sql92的外连接写法。
左外连接连接条件左表的表为主表,右边的表为从表;右外连接则相反。
外连接的分类:左外连接、右外连接、满外连接
- SELECT e.last_name, e.dept_id, d.dept_name
- FROM emp e
- LEFT OUTER JOIN dept d
- ON (e.dept_id = d.dept_id);
- SELECT e.last_name, e.dept_id, d.dept_name
- FROM emp e
- RIGHT OUTER JOIN dept d
- ON (e.dept_id = d.dept_id);
union和union all的区别:union会执行去重操作;union all不会执行去重操作。
- 内连接 A∩B
- SELECT emp_id,last_name,dept_name
- FROM emp a JOIN dept b
- ON a.`dept_id` = b.`dept_id`;
- 左外连接
- SELECT emp_id,last_name,dept_name
- FROM emp a LEFT JOIN dept b
- ON a.`dept_id` = b.`dept_id`;
- 右外连接
- SELECT emp_id, last_name, dept_name
- FROM emp a RIGHT JOIN dept b
- ON a.`dept_id` = b.`dept_id`;
- A - A∩B
- SELECT emp_id,last_name,dept_name
- FROM emp a LEFT JOIN dept b
- ON a.`dept_id` = b.`dept_id`
- WHERE b.`dept_id` IS NULL;
- B - A∩B
- SELECT emp_id,last_name,dept_name
- FROM emp a RIGHT JOIN dept b
- ON a.`dept_id` = b.`dept_id`
- WHERE a.`dept_id` IS NULL;
- 满外连接
- SELECT emp_id,last_name,dept_name
- FROM emp a LEFT JOIN dept b
- ON a.`dept_id` = b.`dept_id`
- WHERE b.`dept_id` IS NULL
- UNION ALL #没有去重操作,效率高
- SELECT emp_id,last_name,dept_name
- FROM emp a RIGHT JOIN dept b
- ON a.`dept_id` = b.`dept_id`;
- (A - A∩B)∪(B - A∩B)
- SELECT emp_id,last_name,dept_name
- FROM emp a LEFT JOIN dept b
- ON a.`dept_id` = b.`dept_id`
- WHERE b.`dept_id` IS NULL
- UNION ALL
- SELECT emp_id,last_name,dept_name
- FROM emp a RIGHT JOIN dept b
- ON a.`dept_id` = b.`dept_id`
- WHERE a.`dept_id` IS NULL
sql99的自然连接可以理解为sql92中的等值连接,自然连接可以自动查询两张表中所有相同的字段,然后进行等值连接。
sql92:
- SELECT emp_id,last_name,dept_name
- FROM emp a JOIN dept b
- ON a.`dept_id` = b.`dept_id`
- AND a.`manager_id` = b.`manager_id`;
- SELECT emp_id,last_name,dept_name
- FROM emp a NATURAL JOIN dept b;
sql99支持using指定数据表里的同名字段进行等值连接,但是只能配合join一起使用。USING 指定了具体的相同的字段名称,你需要在 USING 的括号 () 中填入要指定的同名字段。
- SELECT emp_id,last_name,dept_name
- FROM emp a JOIN dept b
- USING (dept_id);
等同于:
- SELECT emp_id,last_name,dept_name
- FROM emp a ,dept b
- WHERE a.dept_id = b.dept_id;
对于myisam来说没有区别,因为myisam内部有计数器在维护行数;而对于innodb来说,是用count(*),count(1)直接读行数,复杂度是O(n),但优于count(column);count(*)会统计值为 NULL 的行,而 count(column)不会统计此列为 NULL 值的行,所以不能用count(column)替换count(*)。
mysql中可以使用group by子句将表中的数据分成若干组
- SELECT column, group_function(column)
- FROM table
- [WHERE condition]
- [GROUP BY group_by_expression]
- [ORDER BY column];
注意:
作用:过滤数据
例子1:查询各个部门最高工资高于10000的部门信息
- SELECT dept_id, MAX(salary)
- FROM emp
- GROUP BY dept_id
- HAVING MAX(salary)>10000;
如果使用了聚合函数,就必须使用having来替换where
例子2:查询部门id为10,20,30中最高工资大于10000的部门信息
- #方式一
- select dept_id,max(salary)
- from emp
- where dept_id in (10,20,30)
- group by dept_id
- having max(salary) > 10000;
-
- #方式二
- select dept_id,max(salary)
- from emp
- group by dept_id
- having max(salary) > 10000 and dept_id in (10,20,30);
推荐方式一,方式一执行效率较高。
where与having的区别:
sql语句的完整结构
- #sql92语法
- select ... , ...
- from table1, table2
- where 过滤条件(不能包含聚合函数)
- group by ...
- having 过滤条件(可以包含聚合函数的)
- order by ...
- limit ...;
-
-
- #sql99语法
- select ... , ...
- from table1 (left/right) join table2 on 连接条件
- where 过滤条件(不能包含聚合函数)
- group by ...
- having 过滤条件(可以包含聚合函数的)
- order by ...
- limit ...;
select语句的执行顺序:
知道了select语句的执行顺序以后,我们就可以搞清楚以下几个问题了
- 为什么包含聚合函数的过滤条件不能写在where里?
聚合函数的使用前提是必须有结果集,select语句第二步执行到where时还没有结果集。而执行到第三步group by的时候进行分组,分组之后便有了结果集(如果没有group by则执行到select才会有结果集),所以having就可以使用包含聚合函数的过滤条件。
- 为什么字段别名不能在where条件中使用,而能在order by中使用?
因为字段别名是在select中起的,而where执行时还不存在别名,自然就不能使用别名。
子查询指一个查询语句嵌套在另一个查询语句内部的查询。
- SELECT last_name, job_id, salary
- FROM emp
- WHERE salary = (
- SELECT MIN(salary)
- FROM emp
- );
题目:显式员工的employee_id,last_name和location。其中,若员工department_id与location_id为1800 的department_id相同,则location为’Canada’,其余则为’USA’。
- SELECT emp_id, last_name,
- (CASE dept_id
- WHEN
- (SELECT dept_id FROM dept
- WHERE location_id = 1800)
- THEN 'Canada' ELSE 'USA' END) location
- FROM emp;
多行子查询就是子查询返回了多行,此时应该使用多行比较符。
题目:返回其它job_id中比job_id为‘IT_PROG’部门任一工资低的员工的员工号、姓名、job_id 以及salary
- select emp_id,last_name,job_id,salary
- from emp
- where salary < any (
- select salary
- from emp
- where job_id = 'IT_PROG'
- )
- and job_id <> 'IT_PROG';
对于可以使用自连接又可以使用子查询的题目,一般建议使用自连接,因为在许多DBMS的处理过程中,对于自连接的处理速度比子查询快。子查询实际上是通过未知表进行查询后的条件判断,而自连接是通过已知的自身数据表进行条件判断,因此在大部分DBMS中对自连接处理进行了优化。
在MySQL中, 一个完整的数据存储过程总共有 4 步,分别是创建数据库、确认字段、创建数据表、插入数据。
MySQL 数据库系统从大到小依次是 数据库服务器 、 数据库 、 数据表 、数 据表的 行与列 。
数据库名、表名不得超过30个字符
只能包含A–Z, a–z, 0–9, _共63个字符
数据库名、表名、字段名中间不能包含空格
同一个MySQL软件中,数据库名不能同名;同一个库中,表名不能重复;同一个表中,字段名不能重复
建议字段中没有与保留字冲突的字段,否则需要使用着重号(``)引起来
create database 数据库名;
create database 数据库名 character 字符集;
create database if not exist 数据库名;
show databases;
select database();
show tables from 数据库名;
use 数据库名;
alter database 数据库名 character set 字符集;
drop database if exists 数据库名;
- create table [if not exists] 表名(
- 字段1, 数据类型 [约束条件] [默认值],
- 例如:id int(2) auto_increment,
- ……
- [表约束条件]
- );
-
-
- # 创建一个表,并且复制另一个表的结构、数据
- create table 新表名
- as
- select *
- from 表名;
-
- # 创建一个表,并且复制另一个表的结构,不要数据
- create table 新表名
- as
- select *
- from 表名
- where 1=2;
- desc 表名; #方式1
- describe 表名; #方式2
- show create table 表名; #方式3
- # 语法格式
- alter table 表名 add [column] 字段名 字段类型 [first|after 字段名];
- # 举例
- alter table books add author1 varchar(20);
- # 语法格式
- alter table 表名 modify [column] 字段名1 字段类型 [default 默认值][first|after 字段名
- 2];
-
- # 举例
- alter table books modify author CHAR;
说明:修改列可以修改列的数据类型,长度,默认值和位置
- # 语法格式
- alter table 表名 change [column] 列名 新列名 新数据类型;
-
- # 举例
- alter table books change author author_name varchar(20);
- # 或
- alter table books change column author authorvarchar_name varchar(20);
- # 语法格式
- alter table 表名 drop [column]字段名;
-
-
- # 举例
- alter table books drop author;
- # 方式1
- rename table `books` to books1;
-
- # 方式2
- alter table books1 rename books2;
- # 语法格式
- drop table [if exists] 数据表1 [, 数据表2, …, 数据表n];
-
- # 举例
- drop table if exists books2;
- # MySQL5.7
- drop table books1,books2;
-
在MySQL5.7中上述语句等价于
- drop table books1;
- drop table books2;
而在MySQL8.0中新增了DDL的原子化如果其中一张表不存在,则另一张表不会删除。
说明:
- truncate table books;
-
- delete from books;
表删除操作将把表结构和数据一起删除,并且没有提示。删除表之前应该对表数据进行备份。
在使用 ”alter table 表名 drop 列名“ 进行表的基本修改时,在执行操作之前也应该对表进行备份。
语法格式:
- insert into 表名
- values (value1,value2,....);
方式1需要为表中每一个字段指定值,并且顺序要与表中字段一致
- insert into 表名(column1 , column2, ...)
- values (value1 ,value2, ...);
-
- # 多行插入
- insert into 表名(column1, column2...)
- values
- (value1, value2...),
- (value1, value2...);
多行插入时,MySQL会返回:
语法格式:
- update 表名
- set column1 = value1, column2 = value2,...
- where 条件;
语法格式:
delete from 表名 where 条件;
说明:红色为常用类型
整数类型:tinyint、smallint、mediumint、int(范围-2^31~2^32-1,存储大小为4个字节)、bigint
浮点型:float、double
定点数类型:decimal
日期类型:year、time、date、timestamp、datetime
文本字符串类型:char、varchar、tinytext、text(长文本数据,最大4G)、mediumtext、longtext
char:定长字符数据,保存时数据的实际长度比char类型声明长度小,则会在右侧填充空格以达到指定长度;如未指定长度,默认为1个字符,最大长度255,速度比varchar快,适用于速度要求高的场景。
varchar:可变长字符数据,根据字符串实际长度保存,必须指定长度
float:单精度,占用4个字节
double:双精度,占用8个字节
整数类型 | 字节 | 范围 | 无符号范围 |
tinyint | 1 | (-2^7)~(2^7-1) | 0~2^8-1 |
smallint | 2 | -(2^15)~(2^15-1) | 0~2^16-1 |
mediumint | 3 | -(2^23)~(2^23-1) | 0~2^24-1 |
int | 4 | -(2^31)~(2^31-1) | 0~2^32-1 |
bigint | 8 | -(2^64)~(2^64-1) | 0~2^64-1 |
整数类型 | 使用场景 |
tinyint | 枚举类型 |
smallint | 用于较小范围的数据统计 |
mediumint | 用于较大的整数计算 |
int | 一般情况下不用考虑超限问题,用的最多,比如商品编号 |
bigint | 处理特别大的整数时用到 |
整数类型如何选择?
在选择整数类型时,需要考虑存储空间和可靠性问题,占用字节数少的可以节省存储空间,但是容易有超出范围的情况出现系统故障,影响可靠性。在实际场景中,出现系统故障的成本大于增加几个字段存储空间的成本,建议使用不会超出取值范围的数据类型。
float占用字节数少,取值范围小;double占用字节数多,取值范围大。
浮点数时不准确的,应该避免使用 ”=“来判断两个数是否相等,在对精度要求比较高的场景,最好不要使用浮点数,浮点数容易造成结果错误。建议使用定点数类型decimal。
定点数在MySQL内部是以字符串的形式存储的,所以一定是精准的。
浮点数:在长度一定的情况下,浮点数的取值范围比定点数大,但是不精准,所以适用于需要取值范围大,又可以容忍微小误差的场景。
定点数:取值范围相对小,但是精准,适用于对精度要求极高的场景。
类型 | 名称 | 字节 | 日期格式 | 最小值 | 最大值 |
year | 年 | 1 | YYYY | 1901 | 2155 |
time | 时间 | 3 | HH:MM:SS | -838:59:59 | 838:59:59 |
date | 日期 | 3 | YYYY-MM-DD | 1000-01-01 | 9999-12-03 |
datetime | 日期时间 | 8 | YYYY-MM-DD HH:MM:SS | 1000-01-01 00:00:00 | 9999-12-31 23:59:59 |
timestamp | 日期时间 | 4 | YYYY-MM-DD HH:MM:SS | 1970-01-01 00:00:00 UTC | 2038-01-19 03:15:07UTC |
datetime、timestamp的区别:timestamp多了一个时区
约束是表记的强制规定,可以在创建表时规定约束,或者在表创建之后通过alter table语句规定。
为了保证数据完整性,SQL规范以约束的方式对表数据进行额外的条件限制。
完整性的体现:
根据约束数据列的限制分为:
根据作用范围分为:
根据作用分为:
限制某个字段的值不能为空
- # 方式1,建表时
- create table 表名 (
- 字段名 数据类型 not null
- );
-
-
- # 方式2,建表后
- alter table 表名 modify 字段名 数据类型 not null;
alter table 表名 modify 字段名 数据类型;
限制某个字段在同一张表中不能重复。
- # 方式1, 建表时
- create table 表名 (
- 字段名 数据类型 unique, # 列级约束
- constraint [约束名] unique(字段名1,字段名2) # 表级约束
- );
-
- # 方式2, 建表后
- alter table 表名 modify 字段名 字段类型 unique;
- # 或者
- alter table 表名 add unique key(字段名);
- alter table 表名
- drop index 唯一约束名;
用来唯一标识表中的一行记录。
- # 列级
- create table 表名 (
- 字段1 数据类型 primary key,
- 字段2 数据类型
- );
-
-
- # 表级
- create table 表名 (
- 字段1 数据类型,
- 字段2 数据类型,
- primary key(字段名)
- );
-
- # 建表后添加
- alter table 表名 add primary key(字段名);
- create table 表名(
- 字段1 数据类型,
- 字段2 数据类型,
- primary key(字段1,字段2)
- );
alter table 表名 drop primary key;
字段的值自增。
- # 方式1,建表时
- create table 表名 (
- 字段1 int primary key auto_increment,
- 字段2 数据类型
- );
- # 或者
- create table 表名 (
- 字段1 int unique key auto_increment,
- );
-
- # 方式2,建表后
- alter table 表名 modify 字段名 int auto_increment;
alter table 表名 modify 字段名 数据类型;
限定某个表的某个字段引用的完整性。
被参考的表为主表,参考别的表为从表。例如:
员工表的员工所在部门需要参考部门表,则部门表为主表,员工表是从表
- # 方式1,建表时
- create table 主表 (
- 字段1 数据类型 primary key,
- 字段2 数据类型
- );
-
- create table 从表 (
- 字段1 数据类型 primary key,
- 字段2 数据类型,
- foreign key (从表字段) references 主表(主表被参考字段)
- );
-
-
- # 方式2,建表后
- alter table 从表 add foreign key (从表字段) references 主表(主表被参考字段);
有外键约束:创建表、删除表、添加、修改、删除会受到限制,例如在员工表中不能添加一个员工信息,他的部门在部门表中找不到。
没有外键约束:创建表、删除表、添加、修改、删除不受限制,要保证数据的引用完整性只能依靠程序员自觉。
给某个字段指定默认值,一旦设置默认值,在插入默认值时,如果字段没有显式赋值,则赋值为默认值。
- # 方式1, 建表时
- create table 表名 (
- 字段1 数据类型 primary key,
- 字段2 数据类型 not null default 默认值
- );
-
- # 方式2, 建表后
- alter table 表名 modify 字段名 数据类型 default 默认值;
- # 删除默认值约束,也不保留非空约束
- alter table 表名 modify 字段名 数据类型;
-
- # 删除默认值约束,保留非空约束
- alter table 表名 modify 字段名 数据类型 not null;
-
不想让表中出现null值,因为null值不好比较,null时特殊值,比较时只能用专门的is null或者 is not null比较。碰到运算符,通常返回null。而且效率不高,影响提高索引效果,所以往往在建表时加not null default '' 或default 0。
设置自增约束时,如果指定了第一条插入记录的自增字段的值,后面新插入的记录的自增字段值会从初始值开始,例如插入第一条记录指定自增字段值为5,那么后面插入就会从6开始。
外键约束不能跨引擎使用。如果多表之间需要关联外键,这些表必须使用相同的存储引擎。
视图是一种虚拟表,不存储数据,占用很少的内存空间,向视图提供数据内容的语句为select语句,可以将视图理解为存储起来的select语句。视图的建立是在已有表的基础上,视图依赖建立的那些表称为基表。
视图的创建和删除只影响视图本身,不影响对应的基表。但是对视图中的数据进行增删改操作时,数据表中的数据会发生变化。
视图可以帮助我们使用表的一部分,而不是所有的表,同时还可以针对不同用户定制不同的查询视图。在数据表比较复杂的情况下,视图可以帮助我们吧经常查询的结果放到虚拟表中,提升使用效率。
- create view 视图名
- as 查询语句;
-
- # 查看创建的视图
- select * from 视图名;
-
- # 查看视图的结构
- desc 视图名;
- alter view 视图名
- as
- 查询语句;
drop view if exists 视图名;
在数据表上创建了视图,如果数据表的结构发生改变,就需要对相关视图进行维护,特别是嵌套的视图,维护比较复杂,可读性差。另外视图太多会导致数据库维护成本加大。
一组经过预编译的SQL语句的封装
视图是虚拟表,通常不对底层数据表直接操作。
存储过程时程序化SQL,可以直接操作底层数据表,能够实现一些更复杂的数据处理。存储过程一旦被创建,使用时直接调用存储过程名即可。
- create procedure 存储过程名(in/out/inout 参数名 参数类型,...)
- begin
- 存储过程
- end
-
-
- # 参数修饰符的含义
- # 1. in :当前参数为输入参数
- # 2. out :当前参数为输出参数
- # 3. inout :当前参数既可以为输入参数,也可以作为输出参数
call 存储过程名(参数)
本文介绍了mysql的基础知识,文中如有不足还望指教。
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。