开发环境:操作系统Linux、数据库MariaDB(MySQL的一个分支,与MySQL的操作命令几乎一致)、数据源student.sql
一、简答题
1. MySQL常见的三种存储引擎(InnoDB、MyISAM、MEMORY)的区别(至少5点)?
- 事务安全:InnoDB支持事务处理、回滚、崩溃修复能力和多版本并发控制的事务安全;MyISAM和MEMORY不支持事务安全
- 外键:InnoDB支持外键;MyISAM和MEMORY不支持外键
- 锁:InnoDB支持行锁;MyISAM支持表锁;MEMORY支持表锁
- 存储限制:InnoDB存储限制64TB;MyISAM和MEMORY具有存储限制
- 索引:InnoDB支持BTREE索引;MyISAM支持BTREE索引、全文索引;MEMORY支持BTREE索引、哈希索引
- 空间使用率:InnoDB空间使用率高;MyISAM和MEMORY空间使用率较低
- 应用场景:InnoDB适合
频繁修改以及涉及到安全性较高的应用
;MyISAM适合查询以及插入为主
的应用;Memory表被存储在内存中对创建暂时表
很实用,但是当server关闭之后全部存储在Memory表里的数据也会丢失。
【小结】:
InnoDB:优势在于提供了良好的事务处理、崩溃修复能力和并发控制。缺点是读写效率较差,占用的数据空间相对较大。
MyISAM:优势在于占用空间小,处理速度快。缺点是不支持事务的完整性和并发性。
MEMORY:存储的表实际对应一个磁盘文件。该文件的文件名与表名相同,类型为frm类型。该文件中只存储表的结构。而其数据文件,都是存储在内存中,这样有利于数据的快速处理,提高整个表的效率。值得注意的是,服务器需要有足够的内存来维持MEMORY存储引擎的表的使用。如果不需要了,可以释放内存,甚至删除不需要的表。
注意:同一个数据库也可以使用多种存储引擎的表。如果一个表要求比较高的事务处理
,可以选择InnoDB。这个数据库中可以将查询要求比较高的表选择MyISAM存储
。如果该数据库需要一个用于查询的临时表
,可以选择MEMORY存储引擎。
2. 数据库事务的四个特性及含义
- 原子性(Atomicity):原子性是指事务是一个不可分割的工作单位,事务中的操作要么都发生,要么都不发生。
- 一致性(Consistency):事务前后数据的完整性必须保持一致,即几个并行执行的事务其执行结果必须与按某一顺序串行执行的结果相一致。
- 隔离性(Isolation):事务的隔离性是指多个用户并发访问数据库时,一个用户的事务不能被其他用户的事务所干扰,多个并发之间数据要相互隔离。
- 持久性(Durability):持久性是指一个事务一旦被提交,它对数据库中数据的改变就是永久性的,即使数据库发生故障也不会对其有任何影响。
3. 数据库三范式是什么?
经过研究和对使用中问题的总结,对设计数据库提出了一些规范,这些规范被称为范式
- 第一范式(1NF):
列不可拆分,即无重复的域
- 第二范式(2NF):
唯一标识,即拥有实体的唯一标识(eg: 身份证、id号等)
- 第三范式(3NF):
引用主键,即每列数据都与主键直接相关
说明:关系型数据库有六种范式。一般说来,数据库只需满足第三范式(3NF)就行了。
4. 数据库支持的SQL数据类型常用的有哪些?
- 数字:int,decimal(5, 3) --> 5位数,小数位3位
- 字符串:char(不可变),varchar(可变),text(大文本)
- 日期:datetime,date
- 布尔:bit
5. SQL数据类型varchar和char的区别?
- varchar:可变长的字符串,需要在声明字段时指定能存储的最大字符数,真实占用的空间取决于存入的字符数,存入的越多占用空间越多。适合保存内容长度不定的字符类型数据。存储数据的大小,mysql5.0以前0~255字节,mysql5.0以后0~65535字节
- char:定长字符串,需要在声明字段时指定固定字符数。即使存入的字符数少于该长度,该字段也会占用该固定长度。适合存储长度不变的字符类型数据。存储数据的大小,0~255字节
6. SQL 约束有哪几种并解释含义(eg:NOT NULL、UNIQUE等)?
- primary key:主键,不能重复的唯一标识,一个表中只可有一个
- not null:非空
- unique:唯一,一个表中可以有多个
- default:默认
- foreign key:外键
7. 数据库内连表、左连表、右连表有什么区别?
- 内连表:table_A inner join table_B,表 table_A 和 table_B 相匹配的行出现在结果集中
- 左连表:table_A left join table_B,表 table_A 和 table_B 相匹配的行出现在结果集中,外加表 table_A 中独有的数据,为对应的数据用 null 填充
- 右连表:table_A right join table_B,表 table_A 和 table_B 相匹配的行出现在结果集中,外加表 table_B 中独有的数据,为对应的数据用 null 填充
8. SQL语句查询时如何实现分页?
# 表示从第 start 条数据开始,获取 count 条数据,默认情况下索引从0开始
select * from table_name limit start, count
9. 什么是SQL注入?
SQL注入:是现在普通使用的一种攻击手段,通过把非法的SQL命令插入到Web表单中或页面请求查询字符串中,最终达到欺骗服务器执行恶意的SQL语句的目的。SQL注入一旦成功,轻则直接绕开服务器验证,直接登录成功,重则将服务器端数据库中的内容一览无余,更有甚者,直接篡改数据库内容等。
SQL注入的产生条件:1). 有参数传递;2). 参数值带入数据库查询并且执行
为防止SQL注入,需要对用户的输入进行过滤,因为在Web攻防中,我们永远不要相信用户的输入。
防止SQL注入包括:
- 使用预编译语句,绑定变量。
- 使用安全的存储过程对抗SQL注入。
- 检查数据类型。
- 使用安全函数。
10. 数据库怎么优化查询效率?
- 储存引擎选择:如果数据表需要事务处理,应该考虑使用 InnoDB,因为它完全符合 ACID 特性。如果不需要事务处理,使用默认存储引擎 MyISAM 是比较明智的
- 对查询进行优化,要尽量避免全表扫描,首先应考虑在 where 及 order by 涉及的列上建立索引
- 应尽量避免在 where 子句中对字段进行 null 值判断,否则将导致引擎放弃使用索引而进行全表扫描
- 应尽量避免在 where 子句中使用 != 或 <> 操作符,否则将引擎放弃使用索引而进行全表扫描
- 应尽量避免在 where 子句中使用 or 来连接条件,如果一个字段有索引,一个字段没有索引,将导致引擎放弃使用索引而进行全表扫描
- Update 语句,如果只更改 1、2 个字段,不要 Update 全部字段,否则频繁调用会引起明显的性能消耗,同时带来大量日志
- 对于多张大数据量(这里几百条就算大了)的表 JOIN,要先分页再 JOIN,否则逻辑读会很高,性能很差。
二、SQL语句查询操作。
数据恢复以用于后续操作练习,加载数据源student.sql文件:
# 1. 数据恢复,开启MariaDB后,首先创建一个数据库 StuProject
MariaDB [(none)]> create database StuProject charset=utf8;
Query OK, 1 row affected (0.001 sec)
# 2. 退出数据库,在终端输入:
python@ubuntu:~ $ mysql -uroot -pmysql StuProject < student.sql
# 3. 查看数据恢复情况
MariaDB [(none)]> use StuProject;
MariaDB [StuProject]> show tables;
+----------------------+
| Tables_in_StuProject |
+----------------------+
| courses |
| scores |
| students |
| teachers |
+----------------------+
4 rows in set (0.000 sec)
# 5.成功~~
MariaDB [StuProject]> select * from courses;
+-------+-----------------+-----+
| cno | cname | tno |
+-------+-----------------+-----+
| 3-105 | 计算机导论 | 825 |
| 3-245 | 操作系统 | 804 |
| 6-166 | 数据电路 | 856 |
| 9-888 | 高等数学 | 100 |
+-------+-----------------+-----+
4 rows in set (0.001 sec)
1. 创建数据库表employees, 包含的信息有:
属性名 | 属性含义 | 属性类型 |
---|---|---|
emp_no | 员工编号 | 整形、主键、自增 |
birth_date | 出生年月 | 日期、非空 |
name | 姓名 | 字符串、非空 |
gender | 性别 | 整形、非空、1-男 2-女 |
hire_date | 入职日期 | 日期、非空 |
代码示例:
MariaDB [StuProject]> create table employees(
-> emp_no int auto_increment primary key comment '员工编号',
-> birth_date date not null comment '出生日期',
-> name varchar(20) not null comment '姓名',
-> gender int not null default 1 comment '男1女2',
-> hire_date datetime not null comment '入职日期');
MariaDB [StuProject]> desc employees;
+------------+-------------+------+-----+---------+----------------+
| Field | Type | Null | Key | Default | Extra |
+------------+-------------+------+-----+---------+----------------+
| emp_no | int(11) | NO | PRI | NULL | auto_increment |
| birth_date | date | NO | | NULL | |
| name | varchar(20) | NO | | NULL | |
| gender | int(11) | NO | | 1 | |
| hire_date | datetime | NO | | NULL | |
+------------+-------------+------+-----+---------+----------------+
5 rows in set (0.002 sec)
【修改一下】hire_date的数据类型:datetime --> date,以方便后续时间的计算
ps:data(1991-02-01)和datatime(2017-10-10 00:00:00)数据类型区别。
MariaDB [StuProject]> alter table employees modify column hire_date date;
Query OK, 5 rows affected (0.069 sec)
Records: 5 Duplicates: 0 Warnings: 0
MariaDB [StuProject]> desc employees;
+------------+-------------+------+-----+---------+----------------+
| Field | Type | Null | Key | Default | Extra |
+------------+-------------+------+-----+---------+----------------+
| emp_no | int(11) | NO | PRI | NULL | auto_increment |
| birth_date | date | NO | | NULL | |
| name | varchar(20) | NO | | NULL | |
| gender | int(11) | NO | | 1 | |
| hire_date | date | YES | | NULL | |
+------------+-------------+------+-----+---------+----------------+
5 rows in set (0.002 sec)
2. 批量插入数据如下:
emp_no | birth_date | name | gender | hire_date |
---|---|---|---|---|
1 | 1996-10-10 | 高崎 | 女 | 2019-10-10 |
2 | 1995-10-10 | 刘欢 | 女 | 2019-6-10 |
3 | 1993-10-10 | 王佗 | 男 | 2019-1-10 |
4 | 1995-10-10 | 洪笙宁 | 男 | 2018-10-10 |
5 | 1991-2-1 | 张三 | 男 | 2017-10-10 |
代码示例:
MariaDB [StuProject]> insert into employees values
-> (1,'1996-10-10','高崎','2','2019-10-10'),
-> (2,'1995-10-10','刘欢',2,'2019-6-10'),
-> (3,'1993-10-10','王佗',1,'2019-1-10'),
-> (4,'1995-10-10','洪笙宁',1,'2018-10-10'),
-> (5,'1991-2-1','张三',1,'2017-10-10');
MariaDB [StuProject]> select * from employees;
+--------+------------+-----------+--------+------------+
| emp_no | birth_date | name | gender | hire_date |
+--------+------------+-----------+--------+------------+
| 1 | 1996-12-12 | 高崎 | 2 | 2019-10-10 |
| 2 | 1995-10-10 | 刘欢 | 2 | 2019-06-10 |
| 3 | 1993-10-10 | 王佗 | 1 | 2019-01-10 |
| 4 | 1995-10-10 | 洪笙宁 | 1 | 2018-10-10 |
| 5 | 1991-02-01 | 张三 | 1 | 2017-10-10 |
+--------+------------+-----------+--------+------------+
5 rows in set (0.000 sec)
3. 更新高崎的出生日期为1996-12-12.
MariaDB [StuProject]> update employees set birth_date='1996-12-12' where name='高崎';
Query OK, 1 row affected (0.007 sec)
Rows matched: 1 Changed: 1 Warnings: 0
MariaDB [StuProject]> select * from employees where name='高崎';
+--------+------------+--------+--------+------------+
| emp_no | birth_date | name | gender | hire_date |
+--------+------------+--------+--------+------------+
| 1 | 1996-12-12 | 高崎 | 2 | 2019-10-10 |
+--------+------------+--------+--------+------------+
1 row in set (0.001 sec)
4. 统计员工总人数。
MariaDB [StuProject]> select count(*) as stu_conut from employees;
+-----------+
| stu_conut |
+-----------+
| 5 |
+-----------+
1 row in set (0.001 sec)
5. 统计入职时间超过两年的员工
推荐博文:MySQL中日期数据类型、时间类型使用总结https://www.cnblogs.com/scwbky/p/9803586.html
# 利用函数curdate()获取当前日期并利用 @dt将其保存起来
MariaDB [StuProject]> set @dt=curdate();
Query OK, 0 rows affected (0.000 sec)
MariaDB [StuProject]> select @dt;
+------------+
| @dt |
+------------+
| 2020-01-10 |
+------------+
1 row in set (0.000 sec)
# 两年即730天
MariaDB [StuProject]> select * from employees where datediff(@dt, hire_date)>730;
+--------+------------+--------+--------+------------+
| emp_no | birth_date | name | gender | hire_date |
+--------+------------+--------+--------+------------+
| 5 | 1991-02-01 | 张三 | 1 | 2017-10-10 |
+--------+------------+--------+--------+------------+
6. 查找最晚入职员工的所有信息
MariaDB [StuProject]> select * from employees where hire_date=(select max(hire_date) from employees);
+--------+------------+--------+--------+------------+
| emp_no | birth_date | name | gender | hire_date |
+--------+------------+--------+--------+------------+
| 1 | 1996-12-12 | 高崎 | 2 | 2019-10-10 |
+--------+------------+--------+--------+------------+
1 row in set (0.002 sec)
7. 查询最早入职员工的所有信息
MariaDB [StuProject]> select * from employees where hire_date=(select min(hire_date) from employees);
+--------+------------+--------+--------+------------+
| emp_no | birth_date | name | gender | hire_date |
+--------+------------+--------+--------+------------+
| 5 | 1991-02-01 | 张三 | 1 | 2017-10-10 |
+--------+------------+--------+--------+------------+
1 row in set (0.001 sec)
8. 查询成绩在85到90分之间的学生姓名、课程名和成绩。
分析:学生姓名在数据路表students中;课程名在数据路表courses中;成绩在数据路表scores中
MariaDB [StuProject]> select sname as '姓名',cname as '课程', degree as '成绩' from scores
-> right join students on students.sno=scores.sno
-> right join courses on scores.cno=courses.cno
-> group by degree having degree between 85 and 90;
+--------+-----------------+--------+
| 姓名 | 课程 | 成绩 |
+--------+-----------------+--------+
| 李军 | 数据电路 | 85.0 |
| 陆君 | 操作系统 | 86.0 |
| 匡明 | 计算机导论 | 88.0 |
+--------+-----------------+--------+
3 rows in set (0.003 sec)
9. 查询高等数学成绩最高的学生名和学生分数。
# 1. 查看得知最高分的学生为 | 103 | 3-105 | 92.0 | 103 | 陆君 | 男 | 1974-06-03 00:00:00 | 95031 |
MariaDB [StuProject]> select * from scores right join students on students.sno=scores.sno;
+------+-------+--------+-----+--------+------+---------------------+-------+
| sno | cno | degree | sno | sname | ssex | sbirthday | class |
+------+-------+--------+-----+--------+------+---------------------+-------+
| 103 | 3-245 | 86.0 | 103 | 陆君 | 男 | 1974-06-03 00:00:00 | 95031 |
| 105 | 3-245 | 75.0 | 105 | 匡明 | 男 | 1975-10-02 00:00:00 | 95031 |
| 109 | 3-245 | 68.0 | 109 | 王芳 | 女 | 1975-02-10 00:00:00 | 95031 |
| 103 | 3-105 | 92.0 | 103 | 陆君 | 男 | 1974-06-03 00:00:00 | 95031 |
| 105 | 3-105 | 88.0 | 105 | 匡明 | 男 | 1975-10-02 00:00:00 | 95031 |
| 109 | 3-105 | 76.0 | 109 | 王芳 | 女 | 1975-02-10 00:00:00 | 95031 |
| 101 | 3-105 | 64.0 | 101 | 李军 | 男 | 1976-02-20 00:00:00 | 95033 |
| 107 | 3-105 | 91.0 | 107 | 王丽 | 女 | 1976-01-23 00:00:00 | 95033 |
| 108 | 3-105 | 78.0 | 108 | 曾华 | 男 | 1977-09-01 00:00:00 | 95033 |
| 101 | 6-166 | 85.0 | 101 | 李军 | 男 | 1976-02-20 00:00:00 | 95033 |
| 107 | 6-106 | 79.0 | 107 | 王丽 | 女 | 1976-01-23 00:00:00 | 95033 |
| 108 | 6-166 | 81.0 | 108 | 曾华 | 男 | 1977-09-01 00:00:00 | 95033 |
+------+-------+--------+-----+--------+------+---------------------+-------+
12 rows in set (0.003 sec)
# 2. 获取
MariaDB [StuProject]> select sname as '姓名',cname as '课程', degree as '成绩' from scores
-> right join students on students.sno=scores.sno
-> right join courses on scores.cno=courses.cno
-> where degree=(select max(degree) from scores);
+--------+-----------------+--------+
| 姓名 | 课程 | 成绩 |
+--------+-----------------+--------+
| 陆君 | 计算机导论 | 92.0 |
+--------+-----------------+--------+
1 row in set (0.001 sec)
10. 查询李军选修的课程名称。
MariaDB [StuProject]> select sname as '姓名',cname as '课程' from scores
-> right join students on students.sno=scores.sno
-> right join courses on scores.cno=courses.cno
-> where sname='李军';
+--------+-----------------+
| 姓名 | 课程 |
+--------+-----------------+
| 李军 | 计算机导论 |
| 李军 | 数据电路 |
+--------+-----------------+
2 rows in set (0.001 sec)