暂无图片
暂无图片
暂无图片
暂无图片
暂无图片

《SQL必知必会》读书笔记,30分钟入门SQL!

SQL数据库开发 2019-11-13
830

点击上方SQL数据库开发,关注获取SQL视频教程


SQL专栏

SQL数据库基础知识汇总

SQL数据库高级知识汇总

来源:https://segmentfault.com/p/1210000011760973/read


本篇文章是 《SQL 必知必会》 的读书笔记,SQL必知必会的英文名叫做 Sams Teach Yourself in 10 Minutes。但是,我肯定是不能够在10分钟就能学会本书所有涉及到的sql,所以就起个名字叫30分钟学会SQL语句。

目前手边的数据库是 mysql,所以以下示例均是由 mysql 演示。由于现在大部分工具都支持语法高亮,所以以下关键字都使用小写。


准备

工具

mycli,一个使用python编写的终端工具,支持语法高亮,自动补全,多行模式,并且如果你熟悉vi的话,可以使用vi-mode快速移动,编辑。总之,vi + mycli 简直是神器!

同样, postgreSQL
可以使用pgcli。

  1. pip install -U mycli    # 默认你已经安装了pip


样例表

示例中有两个表,分为 student 学生表与 class 班级表。student 表中有 class_id 关联 class 表。以下是两个表数据的 sql。另外,最后有三道小练习题会用到样例表

  1. create table class (

  2.  id int(11) not null auto_increment,

  3.  name varchar(50) not null,

  4.  primary key (id)

  5. );


  6. create table student (

  7.  id int(11) not null auto_increment,

  8.  name varchar(50) not null,

  9.  age smallint default 20,

  10.  sex enum('male', 'famale'),

  11.  score tinyint comment '入学成绩',

  12.  class_id int(11),

  13.  createTime timestamp default current_timestamp,

  14.  primary key (id),

  15.  foreign key (class_id) references class (id)

  16. );


  17. insert into class (name) values ('软件工程'), ('市场营销');


  18. insert into student (name, age, sex, score, class_id) values ('张三', 21, 'male', 100, 1);

  19. insert into student (name, age, sex, score, class_id) values ('李四', 22, 'male', 98, 1);

  20. insert into student (name, age, sex, score, class_id) values ('王五', 22, 'male', 99, 1);

  21. insert into student (name, age, sex, score, class_id) values ('燕七', 21, 'famale', 34, 2);

  22. insert into student (name, age, sex, score, class_id) values ('林仙儿', 23, 'famale', 78, 2);


SQL 基础

术语

Database

数据库值一系列有关联数据的集合,而操作和管理这些数据的是DBMS,包括MySQL,PostgreSQL,MongoDB,Oracle,SQLite等等。RDBMS 是基于关系模型的数据库,使用 SQL
管理和操纵数据。另外也有一些 NoSQL
数据库,比如 MongoDB。因为 NoSQL
为非关系型数据库,一般不支持join操作,因此会有一些非正则化(denormalization)的数据,查询也比较快。

Table

具有特定属性的结构化文件。比如学生表,学生属性有学号,年龄,性别等。schema (模式) 用来描述这些信息。NoSQL
不需要固定列,一般没有 schema,同时也利于垂直扩展。

Column

表中的特定属性,如学生的学号,年龄。每一列都具有数据类型。

Data Type

每一列都具有数据类型,如 char, varchar,int,text,blob, datetime,timestamp。根据数据的粒度为列选择合适的数据类型,避免无意义的空间浪费。如下有一些类型对比

char, varchar

  1. 需要存储数据的长度方差小的时候适合存储`char`,否则`varchar`

  2. `varchar` 会使用额外长度存储字符串长度,占用存储空间较大。

  3. 两者对字符串末尾的空格处理的策略不同,不同的DBMS又有不同的策略,设计数据库的时候应当注意到这个区别。

datetime, timestamp

  1. `datetime` 存储时间范围从1001年到9999年。

  2. `timestamp` 保存了自197011日的秒数,因为存储范围比较小,自然存储空间占用也比较小。

  3. 日期类型可以设置更新行时自动更新日期,建议日期时间类型根据精度存储为这两个类型。

  4. 如今 DBMS 能够存储微秒级别的精度,比如 `mysql` 默认存储精度为秒,但可以指定到微秒级别,即小数点后六位小数

enum

  1. 对于一些固定,不易变动的状态码建议存储为 `enum` 类型,具有更好的可读性,更小的存储空间,并且可以保证数据有效性。

小问题:如何存储IP地址?

Row

数据表的每一行记录。如学生张三。


检索数据
  1. # 检索单列

  2. select name from student;


  3. # 检索多列

  4. select name, age, class from student;


  5. # 检索所有列

  6. select * from student;


  7. # 对某列去重

  8. select distinct class from student;


  9. # 检索列-选择区间

  10. # offset 基数为0,所以 `offset 1` 代表从第2行开始

  11. select * from student limit 1, 10;

  12. select * from student limit 10 offset 1;


排序

默认排序是 ASC
,所以一般升序的时候不需指定,降序的关键字是 DESC
。使用 B-Tree
索引可以提高排序性能,但只限最左匹配。关于索引可以查看以下 FAQ。

  1. # 根据学号降序排列

  2. select * from student order by number desc;


  3. # 添加索引 (score, name) 可以提高排序性能

  4. # 但是索引 (name, score) 对性能毫无帮助,此谓最左匹配

  5. select * from student order by score desc, name;


数据过滤
  1. # 找到学号为1的学生

  2. select * from student where number = 1;


  3. # 找到学号为在 [1, 10] 的学生(闭区间)

  4. select * from student where number between 1 and 10;


  5. # 找到未设置电子邮箱的学生

  6. # 注意不能使用 =

  7. select * from student where email is null;


  8. # 找到一班中大于23岁的学生

  9. select * from student where class_id = 1 and age > 23;


  10. # 找到一班或者大于23岁的学生

  11. select * from student where class_id = 1 or age > 22;


  12. # 找到一班与二班的学生

  13. select * from student where class_id in (1, 2);


  14. # 找到不是一班二班的学生

  15. select * from student where class_id not in (1, 2);


计算字段

CONCAT

  1.  select concat(name, '(', age, ')') as nameWithAge from student;


  2.  select concat('hello', 'world') as helloworld;

Math

  1.  select age - 18 as relativeAge from student;


  2.  select 3 * 4 as n;

更多函数可以查看 API 手册,同时也可以自定义函数(User Define Function)。

可以直接使用 select
调用函数

  1. select now();

  2. select concat('hello', 'world');


数据汇总

聚集函数,一些对数据进行汇总的函数,常见有 COUNT
MIN
MAX
AVG
SUM
五种。

  1. # 统计1班人数

  2. select count(*) from student where class_id = 1;


数据分组

使用 group by
进行数据分组,可以使用聚合函数对分组数据进行汇总,使用 having
对分组数据进行筛选。

  1. # 按照班级进行分组并统计各班人数

  2. select class_id, count(*) from student group by class_id;


  3. # 列出大于三个学生的班级

  4. select class_id, count(*) as cnt from student group by class_id having cnt > 3;


子查询
  1. # 列出软件工程班级中的学生

  2. select * from student where class_id in (

  3.  select id from class where class_id = '软件工程'

  4. );


关联联接

虽然两个表拥有公共字段便可以创建联接,但是使用外键可以更好地保证数据完整性。比如当对一个学生插入一条不存在的班级的时候,便会插入失败。一般来说,联接比子查询拥有更好的性能。

  1. # 列出软件工程班级中的学生

  2. select * from student, class

  3. where student.class_id = class.id and class.name = '软件工程';

内联接

内联接又叫等值联接。

  1.  # 列出软件工程班级中的学生

  2.  select * from student

  3.  inner join class on student.class_id = class.id

  4.  where class.name = '软件工程';

自联接

  1.  # 列出与张三同一班级的学生

  2.  select * from student s1

  3.  inner join student s2 on s1.class_id = s2.class_id

  4.  where s1.name = '张三';

外联接

  1.  # 列出每个学生的班级,弱没有班级则为null

  2.  select name, class.name from student

  3.  left join class on student.class_id = class.id;


插入数据

可以采用以下方法插入一条数据,不过严重依赖表中列的顺序关系,推荐指定列名插入数据,并且可以插入部分列。

  1. # 插入一条数据

  2. insert into student values(8, '陆小凤', 24, 1, 3);


  3. insert into student(name, age, sex, class_id) values(9, '花无缺', 25, 1, 3);


修改数据

更新

  1.  # 修改张三的班级

  2.  update student set class_id = 2 where name = '张三';

删除

  1.  # 删除张三的数据

  2.  delete from student where name = '张三';


  3.  # 删除表中所有数据

  4.  delete from student;


  5.  # 更快地删除表中所有数据

  6.  truncate table student;


创建表与更新表
  1. # 创建学生表

  2. create table student (

  3.  id int(11) not null auto_increment,

  4.  name varchar(50) not null,

  5.  age smallint default 20,

  6.  sex enum('male', 'famale'),

  7.  score tinyint comment '入学成绩',

  8.  class_id int(11),

  9.  createTime timestamp default current_timestamp,

  10.  primary key (id),

  11.  foreign key (class_id) references class (id)

  12. );


  13. # 根据旧表创建新表

  14. create table student_copy as select * from student;


  15. # 删除 age 列

  16. alter table student drop column age;


  17. # 添加 age 列

  18. alter table student add column age smallint;


  19. # 删除学生表

  20. drop table student;


视图

视图是一种虚拟的表,便于更好地在多个表中检索数据,视图也可以作写操作,不过最好作为只读。在需要多个表联接的时候可以使用视图。

  1. create view v_student_with_classname as

  2. select student.name name, class.name class_name

  3. from student left join class

  4. where student.class_id = class.id;


  5. select * from v_student_with_classname;


约束

primiry key

任意两行绝对没有相同的主键,且任一行不会有两个主键且主键绝不为空。使用主键可以加快索引。

  1.  alter table student add constraint primary key (id);

foreign key

外键可以保证数据的完整性。有以下两种情况。

  • 插入张三丰5班到student表中会失败,因为5班在class表中不存在。

  • class表删除3班会失败,因为陆小凤和楚留香还在3班。

  1.  alter table student add constraint foreign key (class_id) references class (id);

unique key

唯一索引保证该列值是唯一的,但可以允许有null。

  1.  alter table student add constraint unique key (name);

check

检查约束可以使列满足特定的条件,如果学生表中所有的人的年龄都应该大于0。

不过很可惜mysql不支持,可以使用触发器代替

  1.  alter table student add constraint check (age > 0);

index

索引可以更快地检索数据,但是降低了更新操作的性能。

  1.  create index index_on_student_name on student (name);


  2.  alter table student add constraint key(name );


触发器

可以在插入,更新,删除行的时候触发事件。

  1. # 创建触发器

  2. # 比如mysql中没有check约束,可以使用创建触发器,当插入数据小于0时,置为0。

  3. create trigger reset_age before insert on student for each row

  4. begin

  5.  if NEW.age < 0 then

  6.    set NEW.age = 0;

  7.  end if;

  8. end;


  9. # 打印触发器列表

  10. show triggers;


存储过程

存储过程可以视为一个函数,根据输入执行一系列的 sql 语句。存储过程也可以看做对一系列数据库操作的封装,一定程度上可以提高数据库的安全性。

  1. # 创建存储过程

  2. create procedure create_student(name varchar(50))

  3. begin

  4.  insert into students(name) values (name);

  5. end;


  6. # 调用存储过程

  7. call create_student('shanyue');


SQL 练习

1. 根据班级学生的分数进行排名,如果分数相等则为同一名次

  1. select id, name, score, (

  2.  select count(distinct score) from student s2 where s2.score >= s1.score

  3. ) as rank

  4. from student s1 order by s1.score desc;

在where以及排序中经常用到的字段需要添加Btree索引,因此 score 上可以添加索引。

Result:

idnamescorerank
1张三1001
3王五992
2李四983
5林仙儿784
4燕七345


2. 写一个函数,获取第 N 高的分数
  1. create function getNthHighestScore(N int) return int

  2. begin

  3.  declare M int default N-1;

  4.  return (

  5.    select distinct score from student order by score desc limit M, 1;

  6.  )

  7. end;


  8. select getNthHighestScore(2);

Result:

getNthHighestScore(2)
99


3. 检索每个班级分数前两名学生,并显示排名
  1. select class.id class_id, class.name class_name,

  2. s.name student_name, score, rank

  3. from (

  4.  select *, (

  5.    select count(distinct score) from student s2

  6. where s2.score >= s1.score and s2.class_id = s1.class_id

  7.  ) as rank from student s1

  8. ) as s left join class on s.class_id = class.id where rank <= 2;


  9. # 如果不想在from中包含select子句,也可以像如下检索,不过不显示排名

  10. select class.id class_id, class.name class_name, s1.name name, score

  11. from student s1 left join class on s1.class_id = class.id

  12. where (select count(*) from student s2

  13. where s2.class_id = s1.class_id and s1.score <= s2.score

  14. ) <= 2

  15. order by s1.class_id, score desc;

Result:

class_namestudent_namescorerank
软件工程张三1001
软件工程王五992
市场营销燕七342
市场营销林仙儿781


FAQ

1. inner join
 与 outer join
 的区别是什么

2. 如何根据一个表的数据更新另一个表

比如以上 student
表保存着成绩,另有一表 score_correct
内存因失误而需修改的学生成绩。

在mysql中,可以使用如下语法

  1. update student, score_correct

  2. set student.score = score_correct.score

  3. where student.id = score_correct.uid;

3. 索引是如何工作的

简单来说,索引分为 hash
B-Tree
两种。hash
查找的时间复杂度为O(1)。B-Tree
其实是 B+Tree
,一种自平衡多叉搜索数,自平衡代表每次插入和删除数据都会需要动态调整树高,以降低平衡因子。B+Tree
只有叶子节点会存储信息,并且会使用链表链接起来。因此适合范围查找以及排序,不过只能搜索最左前缀,如只能索引以 a
开头的姓名,却无法索引以 a
结尾的姓名。另外,Everything is trade offB+Tree
的自平衡特性保证能够快速查找的同时也降低了更新的性能,需要权衡利弊。

4. 如何联接多个行的字段

在mysql中,可以使用 group_concat

  1. select group_concat(name) from student;

5. 如何在一个sql语句中插入多行数据

values 使用逗号相隔,可以插入多行数据

  1. insert into student(id, name) values (), (), ()

6. 如何在 select
中使用条件表达式

示例,在student表中,查询所有人成绩,小于60则显示为0

  1. select id, name, if(score < 60, 0, score) score from student;

7. 如何找到重复项
  1. select name, sex, count(*) times from student

  2. group by name, sex

  3. having times > 1;

8. 什么是SQL注入

如有一条查询语句为

  1. "select * from (" + table + ");"

当table取值 student);drop table student;--
时,语句变为了,会删掉表,造成攻击。

  1. "select * from (student); drop table student; --);"



——End——

关注SQL数据库开发公众号,在后台回复关键字:资料领取,可以获取一份精心整理的技术干货。

推荐阅读

点击「阅读原文」了解SQL训练营

最后修改时间:2019-11-13 09:22:58
文章转载自SQL数据库开发,如果涉嫌侵权,请发送邮件至:contact@modb.pro进行举报,并提供相关证据,一经查实,墨天轮将立刻删除相关内容。

评论