第一章:Hello SQL
写在前面
本教程所有代码均可在线执行测试,通过 LintCode 学习平台即可在线学习、测试,并提供在线练习题方便练习,从而让你更高效、熟练得掌握 SQL 语句相关知识。
1.1 什么是数据库
数据库
常会出现在以下场景中:
一个网站需要有数据库来存储网站信息
一个学校需要用数据库来存储学生和教师的信息
一个公司则需要用数据库来存储员工和公司的资料……
而在正式学习数据库之前,我们先简单的了解下什么是数据库。
数据库(Database):按照数据结构来组织、存储和管理数据的仓库 。
简单理解就是一个存储数据的仓库,为了方便数据的存储和管理,它能将数据按照特定的规律存储在磁盘上。并且,通过数据库管理系统,我们可以有效地组织和管理存储在数据库中的数据。
以你正在使用的 炼码(LintCode) 为例,LintCode 是一个面向未来的学习编程技术网站,是为了让你终生学习的一款 All in One 在线学习平台。我们有大量的练习和正在开发中的项目,也有从事相关教程(Tutorial)研发的教师,以及正在开发中的课程。
根据以上信息,我们来设计一个简单的数据库—— LintCode,用来存放我们 LintcCode 的相关信息。
在 LintCode 数据库中,有两张表——课程表 courses 和教师表 teachers :
courses 表中存放着课程的信息 ,包括:课程名称 name 、学生总数 student_count 、开课时间 created_at 以及讲师 ID teacher_id ;
teachers 表中存放着教师的信息,包括:讲师姓名 name 、讲师邮箱 email 、讲师年龄 age 以及讲师国籍 country ;
演示数据库
课程表 courses
+----+-------------------------+---------------+------------+------------+
| id | name | student_count | created_at | teacher_id |
+----+-------------------------+---------------+------------+------------+
| 1 | Advanced Algorithms | 880 | 2020-06-01 | 4 |
| 2 | System Design | 1350 | 2020-07-18 | 3 |
| 3 | Django | 780 | 2020-02-29 | 3 |
| 4 | Web | 340 | 2020-04-22 | 4 |
| 5 | Big Data | 700 | 2020-09-11 | 1 |
| 6 | Artificial Intelligence | 1660 | 2018-05-13 | 3 |
| 7 | Java P6+ | 780 | 2019-01-19 | 3 |
| 8 | Data Analysis | 500 | 2019-07-12 | 1 |
| 10 | Object Oriented Design | 300 | 2020-08-08 | 4 |
| 12 | Dynamic Programming | 2000 | 2018-08-18 | 1 |
+----+-------------------------+---------------+------------+------------+
教师表 teachers
+----+------------------+---------------------------+-----+---------+
| id | name | email | age | country |
+----+------------------+---------------------------+-----+---------+
| 1 | Eastern Heretic | eastern.heretic@gmail.com | 20 | UK |
| 2 | Northern Beggar | northern.beggar@qq.com | 21 | CN |
| 3 | Western Venom | western.venom@163.com | 28 | USA |
| 4 | Southern Emperor | southern.emperor@qq.com | 21 | JP |
| 5 | Linghu Chong | NULL | 18 | CN |
+----+------------------+---------------------------+-----+---------+
以上的两个表就是我们的课程表 courses 和教师表 teachers 的展现形式,与我们常见的 Excel 表格别无二致。
下面我们对上述两个表格进行一些简单的介绍,方便大家对后续课程的理解:
- 数据库:数据库是一些关联表的集合。LintCode 就是我们用来存放课程表 courses 和教师表 teachers 的数据库。
- 数据表:数据表是数据的矩阵。课程表 courses 和 教师表 teachers 就是数据表,看起来像一个简单的 Excel 表格。
- 列: 一列(数据元素)包含了相同类型的数据。例如教师表 teachers 中的姓名 name 就是一个列,这一列中的教师姓名都是用字符方式存储。
- 行: 一行数据(可以称为元组,或记录)是一组相关的数据。例如教师表 teachers 每一行都对应一位教师信息
此外,大家是否注意到,我们在教师表 teachers 和课程表 courses 中,有一个额外的列,叫作 id,那么这个列的作用是什么呢?
这个名为 id 的列,是我们用来唯一标识每行数据的,称为主键。实际上,教师的姓名是不能唯一标识一条记录的(因为教师姓名可能会有相同),所以我们使用额外的一列来唯一标识每行。这个列我们一般取名为 id,即 identification 的缩写,当然,我们也可以取别的名称。但需要注意的是,一个数据表只能有一个主键。
此外,在课程表 courses 中,我们发现有一列数据 teacher_id 和教师表 teachers 中的 id 相关联,即每个 teacher_id 都指向教师表中的某一个教师记录,这种用于关联其它表某一列的列,我们称为外键(foreign key)。
1.2 SQL 是什么
SQL(Structured Query Language)中文全称为”结构化查询语句“,在 1986 年成为 ANSI(American National Standards Institute 美国国家标准化组织)的一项标准,在 1987 年成为国际标准化组织(ISO)标准。
SQL 是一种数据库查询和程序设计语言,用于存取数据以及查询、更新和管理关系数据库系统。
简单理解就是对我们数据库和数据库中的表进行”增删改查“操作的编程语言。
虽然 SQL 是一门 ANSI(American National Standards Institute 美国国家标准化组织)标准的计算机语言,但仍然存在着多种不同版本的 SQL 语言。
然而,为了与 ANSI 标准相兼容,它们必须以相似的方式共同地来支持一些主要的命令(比如 SELECT、UPDATE、DELETE、INSERT、WHERE 等等)。
1.3 SQL 能做什么
SQL 具体能完成下面功能:
- 面向数据库执行查询
- 可从数据库取回数据
- 可在数据库中插入新的记录
- 可更新数据库中的数据
- 可从数据库删除记录
- 可创建新数据库
- 可在数据库中创建新表
- 可在数据库中创建存储过程
- 可在数据库中创建视图
- 可以设置表、存储过程和视图的权限
总结起来,其实就“增删改查”四大功能,但是实际上我们平时用到最多的是查询
😀
因此本教程在涵盖“增删改”的基础上,重点将通过练习查询功能的方式,帮助大家融会贯通从基础到困难,共 10 个难度等级的 SQL 常见知识点和面试题。
1.4 关于本教程
本教程除了少部分简单内容,大部分实例和练习题均可在线学习,让大家可以快速上手!
我们先通过一个简单的案例学习下如何在平台上学习 SQL 吧!
下面就是我们将经常用到的在线编辑器,通过在上半部分书写你的 SQL 语句并点击右下角的”运行测试数据“按钮,就能在线检测你的 SQL 语句书写是否正确,语句执行结果,以及是否真正的掌握到了我们教程中提到的知识。
怎么样,是不是很方便易学呢?是不是心动得迫不及待想要试试在线测试的功能呢?
那么我们可以在最下面的编辑框中,输入下列语句SELECT "Hello SQL!";
请注意哦~SQL 语句以 ; 作为结束的标志,不要忘记哦~
1.5 习题演练
第二章:简单的 SELECT 语句
2.1 使用 SELECT COLUMN 查询单个列
SELECT 语句是最常用的 SQL 语句,它能帮助我们从一个或多个表中查询信息。查询是数据库中最常用的功能,因此我们选择它作为 SQL 语句学习的第一步。
SELECT 语句用于从数据库中选取数据,并将结果存储在一个临时结果表中,这个表称为结果集。结果集实际上也是一种包含行与列的表,只不过是未持久化的,即临时表。
示例代码
在使用 SELECT 语句检索表数据时,至少需要给出两条信息——想检索的列名(column_name)和被检索内容的表名(table_name)。
基础语法如下:SELECT
column_nameFROM
table_name;
大家可能会有些疑惑,列名 column_name 和表名 table_name 左右加的是什么?
这是反引号(``),它就在我们电脑键盘的左上角数字 1 的左边位置。
上述代码不加反引号的效果如下:SELECT column_name FROM table_name;
可能有些同学会问:在平时编写程序时不常加反引号,那不加反引号可以吗?反引号的作用是什么?
其实是可以的,在绝大部分时候,不加反引号并不会导致程序出错。但其实我们在命名字段的时候,字段名可能会与 SQL 关键字冲突,这时候要用反引号将列名和表名包含一下,避免关键字冲突。因此,在本课程所有小节的学习中,都会更加严谨地加上反引号。
当我们仅需要查询某一列的信息,且知道该列的列名时,可以使用简单的 SELECT COLUMN 的语句查询单个列来获取该列的信息。
👇我们可以通过下面的实例来感受一下 SELECT COLUMN 的用法。
假设我们要查询课程表 courses 中所有课程的名称。
我们可以使用下面的 SQL 语句:SELECT
nameFROM
courses;
执行输出结果
mysql> SELECT `name` FROM `courses`;
+-------------------------+
| name |
+-------------------------+
| Advanced Algorithms |
| System Design |
| Django |
| Web |
| Big Data |
| Artificial Intelligence |
| Java P6+ |
| Data Analysis |
| Object Oriented Design |
| Dynamic Programming |
+-------------------------+
10 rows in set (0.00 sec)
2.2 使用 SELECT COLUMN, COLUMN 查询多个列
当我们想要从一个表中查询多个列时,使用的 SELECT 语句与查询一个列时使用的语句相似,但是需要在 SELECT 关键字后给出多个列名,并且列名之间必须以逗号分隔。
语法:SELECT
column_name_1,
column_name_2FROM
table_name;
👇我们可以通过下面的实例来感受一下 SELECT COLUMN, COLUMN 的用法。
假如我们要从课程表 courses 表中选取课程名称 name 和课程开课日期 created_at 的列。
我们可以使用下面的 SQL 语句:SELECT
name,
created_atFROM
courses;
执行输出结果
mysql> SELECT `name`, `created_at` FROM `courses`;
+-------------------------+------------+
| name | created_at |
+-------------------------+------------+
| Advanced Algorithms | 2020-06-01 |
| System Design | 2020-07-18 |
| Django | 2020-02-29 |
| Web | 2020-04-22 |
| Big Data | 2020-09-11 |
| Artificial Intelligence | 2018-05-13 |
| Java P6+ | 2019-01-19 |
| Data Analysis | 2019-07-12 |
| Object Oriented Design | 2020-08-08 |
| Dynamic Programming | 2018-08-18 |
+-------------------------+------------+
10 rows in set (0.00 sec)
2.3 使用 SELECT * 查询所有列
SELECT 语句可以直接检索表中所有信息,即检索所有的列。这可以通过在列名的位置使用星号( * )通配符来实现,输出的列的顺序一般是列在表定义中出现的物理顺序。
通配符是一类键盘字符, (星号) 就是较为常用的通配符之一,可以使用 代替零个、单个或多个字符。
使用 通配符最大的优点就是,当不明确需要检索的列名时,可以通过检索所有列名来确定。
语法:
SELECT FROM table_name
;
👇我们可以通过下面的实例来感受一下 SELECT * 的用法。
假如我们要查询课程表 courses 中的所有数据 。
我们可以使用下面的 SQL 语句:
SELECT * FROM courses
;
其中
- 表示所有列,是一种便捷式写法
- FROM 关键字指出从哪个表中检索数据
执行输出结果:
mysql> SELECT * FROM `courses`;
+----+-------------------------+---------------+------------+------------+
| id | name | student_count | created_at | teacher_id |
+----+-------------------------+---------------+------------+------------+
| 1 | Advanced Algorithms | 880 | 2020-06-01 | 4 |
| 2 | System Design | 1350 | 2020-07-18 | 3 |
| 3 | Django | 780 | 2020-02-29 | 3 |
| 4 | Web | 340 | 2020-04-22 | 4 |
| 5 | Big Data | 700 | 2020-09-11 | 1 |
| 6 | Artificial Intelligence | 1660 | 2018-05-13 | 3 |
| 7 | Java P6+ | 780 | 2019-01-19 | 3 |
| 8 | Data Analysis | 500 | 2019-07-12 | 1 |
| 10 | Object Oriented Design | 300 | 2020-08-08 | 4 |
| 12 | Dynamic Programming | 2000 | 2018-08-18 | 1 |
+----+-------------------------+---------------+------------+------------+
10 rows in set (0.00 sec)
2.4 使用 SELECT DISTINCT 查询不同行
经过上面的知识学习,我们可以发现 SELECT 语句会返回所有匹配的行,有时候会遇到数据相同的情况。如果我们只想知道有哪些不同的值,即希望查询的值都是唯一不重复的,我们该怎么办呢?这时候我们就需要用到 DISTINCT 关键字。
语法:SELECT DISTINCT
column_nameFROM
table_name`<br />**提示:**<br />DISTINCT 关键字需位于列名之前。<br />👇我们可以通过下面的实例来感受一下 **SELECT DISTINCT** 的用法。<br />假如我们想了解课程表 courses 中的授课教师有哪些,并查询教师的编号。<br />我们可以使用下面的 SQL 语句:<br />
SELECT DISTINCT teacher_id
FROM courses
;`
执行输出结果:
mysql> SELECT DISTINCT `teacher_id`
-> FROM `courses`;
+------------+
| teacher_id |
+------------+
| 4 |
| 3 |
| 1 |
+------------+
3 rows in set (0.01 sec)
2.5 使用 SELECT WHERE 对行进行筛选过滤
在大多数情况下,我们只希望留下感兴趣的行而过滤掉不感兴趣的行,这时我们可以使用 WHERE 子句来帮助我们。SELECT WHERE 语句是筛选查询很重要的操作,WHERE 关键字后面加上条件可以过滤掉我们不需要的信息,对查询效率有着很大的提高。
在使用 SELECT WHERE 语句检索表数据时,需要给出检索的表名 (table_name)、检索的列名 (column_name) 和操作符 (operator) 。
语法:SELECT
column_name1,
column_name2… FROM
table_nameWHERE
column_nameoperator
value;
其中:
- column_name 对应指定列的名称,或者是多列,用逗号( , )分隔开
- table_name 对应查询表的名称
- operator 为操作符,常用的有等于 = 、小于 < 、大于 > 、不等于<> 或 !=,我们会在后续课程中更加深入地学习它。
👇 我们可以通过下面的实例来感受一下 SELECT WHERE 的用法。
如果我们想要查询我们可以选择哪些课,或者对一些感兴趣的课程想要详细了解,那我们要到课程信息中去一个一个查找吗?不!这里会给你最简单直接有效的方式:
如果我们要从课程表 courses 中选取课程名为 ‘System Design’ 的课程。
我们可以使用下面的 SQL 语句:SELECT * FROM
coursesWHERE
name= 'System Design';
执行输出结果:
mysql> SELECT * FROM `courses` WHERE `name` = 'System Design';
+----+---------------+---------------+------------+------------+
| id | name | student_count | created_at | teacher_id |
+----+---------------+---------------+------------+------------+
| 2 | System Design | 1350 | 2020-07-18 | 3 |
+----+---------------+---------------+------------+------------+
1 row in set (0.01 sec)
2.6 习题演练
2013 · 查询老师的姓名
2007 · 查询课程名称和上课人数
2009 · 查询所有老师
1981 · 查询所有教师的国籍
2011 · 查询上课人数超过 1000 的课程信息
2012 · 查询课程名为 Artificial Intelligence 的课程信息
第三章:简单的 INSERT 语句
3.1 使用 INSERT INTO 在不指定列的情况下插入数据
我们在学习了从表中查询数据后,如果希望在表中添加新的数据,那么该如何操作呢?这就需要用到我们的 INSERT INTO 语句了。
INSERT INTO 语句用于向表中插入新记录,这边介绍两种编写形式,第一种形式无需指定列名,第二种形式需要指定列名。
在本教程中,我们使用 Lintcode 样本数据库作为我们案例查询表。
在本章中,我们先讲讲 INSERT INTO 的第一种形式。这种形式,不需指定列名,只需提供要插入的数据即可,语法如下:INSERT INTO
table_nameVALUES (value1, value2, value3,...);
其中
value1, value2 …… 为对应插入数据表中的值,每个值的属性需要与对应表中的列名属性相匹配,而且需要把插入的信息填写完整,否则会报错。
👇 我们可以通过下面的实例来感受一下 INSERT INTO 第一种形式的用法。
假设我们要向课程表 courses 中插入一条新行。
我们可以使用下面的 SQL 语句:INSERT INTO
coursesVALUES (13,'Python','400','2021-05-23',3);
执行输出结果
mysql> INSERT INTO `courses` VALUES (13,'Python','400','2021-05-23',3);
Query OK, 1 row affected (0.03 sec)
mysql> SELECT * FROM `courses`;
+----+-------------------------+---------------+------------+------------+
| id | name | student_count | created_at | teacher_id |
+----+-------------------------+---------------+------------+------------+
| 1 | Advanced Algorithms | 880 | 2020-06-01 | 4 |
| 2 | System Design | 1350 | 2020-07-18 | 3 |
| 3 | Django | 780 | 2020-02-29 | 3 |
| 4 | Web | 340 | 2020-04-22 | 4 |
| 5 | Big Data | 700 | 2020-09-11 | 1 |
| 6 | Artificial Intelligence | 1660 | 2018-05-13 | 3 |
| 7 | Java P6+ | 780 | 2019-01-19 | 3 |
| 8 | Data Analysis | 500 | 2019-07-12 | 1 |
| 10 | Object Oriented Design | 300 | 2020-08-08 | 4 |
| 12 | Dynamic Programming | 2000 | 2018-08-18 | 1 |
| 13 | Python | 400 | 2021-05-23 | 3 |
+----+-------------------------+---------------+------------+------------+
11 rows in set (0.00 sec)
3.2 使用 INSERT INTO 在指定的列中插入数据
上一节,我们学习了 INSERT INTO 第一种形式的写法,接下来,我们来讲讲 INSERT INTO 的第二种形式。这种形式需要指定列名,语法如下:
INSERT INTO table_name
(column1
, column2
, column3
,…) VALUES (value1, value2, value3,…);
其中 column1, column2 … 为指定的列名,value1, value2 …… 为对应插入数据表中的值,每个值的属性需要与对应的列名属性相匹配。
💡由此我们不难发现,如果我们需要为表中的每一列插入数据,那么第一种形式的写法会更为方便,如果我们只想在指定列中插入数据,那么第二种形式的写法会更为方便。
👇我们可以通过下面的实例来感受一下 INSERT INTO 第二种形式的用法。
假如我们想向课程表 courses 插入一条新行,但是只在 name 、student_number 、created_at 和 teacher_id 列中插入数据( id 字段会自动更新):
我们可以使用下面的 SQL 语句:
INSERT INTO courses
(name
, student_count
, created_at
, teacher_id
) VALUES (‘Flash Sale’,’100’,’2018-01-01’,5);
执行输出结果
mysql> INSERT INTO `courses` (`name`, `student_count`, `created_at`, `teacher_id`) VALUES ('Flash Sale','100','2018-01-01',5);
Query OK, 1 row affected (0.01 sec)
mysql> SELECT * FROM `courses`;
+----+-------------------------+---------------+------------+------------+
| id | name | student_count | created_at | teacher_id |
+----+-------------------------+---------------+------------+------------+
| 1 | Advanced Algorithms | 880 | 2020-06-01 | 4 |
| 2 | System Design | 1350 | 2020-07-18 | 3 |
| 3 | Django | 780 | 2020-02-29 | 3 |
| 4 | Web | 340 | 2020-04-22 | 4 |
| 5 | Big Data | 700 | 2020-09-11 | 1 |
| 6 | Artificial Intelligence | 1660 | 2018-05-13 | 3 |
| 7 | Java P6+ | 780 | 2019-01-19 | 3 |
| 8 | Data Analysis | 500 | 2019-07-12 | 1 |
| 10 | Object Oriented Design | 300 | 2020-08-08 | 4 |
| 12 | Dynamic Programming | 2000 | 2018-08-18 | 1 |
| 13 | Python | 400 | 2021-05-23 | 3 |
| 14 | Flash Sale | 100 | 2018-01-01 | 5 |
+----+-------------------------+---------------+------------+------------+
12 rows in set (0.00 sec)
3.3 习题演练
2017 · 向课程表中插入 SQL 课程信息
2021 · 向教师表指定的列插入教师信息
第四章:简单的 UPDATE 语句
4.1 使用 UPDATE 更新数据
在我们平时的使用中 UPDATE 语句,也是一种较常用的 SQL 语句,它可以用来更新表中已存在的记录。
我们在查询教师表 teachers 的时候发现,教师姓名 name 为 “Linghu Chong” 的老师邮箱 email 信息为 NULL,即没有该部分信息,我们现在希望更新邮箱信息,这时候就需要用到 UPDATE 语句。
语法
UPDATE table_name
SET column1
=value1,column2
=value2,… WHERE some_column
=some_value;
注意
请注意 UPDATE 语句中的 WHERE 子句!WHERE 子句规定哪条记录或者哪些记录需要更新。如果您省略了 WHERE 子句,所有的记录都将被更新!
👇我们可以通过下面的实例来感受一下 UPDATE 的用法。
假如我们想将教师名 name 为 “Linghu Chong” 的邮箱 email 更新为 ‘LinghuChong@lintcode.com‘。
我们可以使用下面的 SQL 语句:
UPDATE teachers
SET email
= ‘LinghuChong@lintcode.com’ WHERE name
= ‘Linghu Chong’;
执行输出结果
mysql> UPDATE `teachers`
-> SET `email` = 'LinghuChong@lintcode.com'
-> WHERE `name` = 'Linghu Chong';
Query OK, 1 row affected (0.02 sec)
Rows matched: 1 Changed: 1 Warnings: 0
mysql> SELECT * FROM `teachers` WHERE `name`='Linghu Chong';
+----+--------------+--------------------------+-----+---------+
| id | name | email | age | country |
+----+--------------+--------------------------+-----+---------+
| 5 | Linghu Chong | LinghuChong@lintcode.com | 18 | CN |
+----+--------------+--------------------------+-----+---------+
1 row in set (0.01 sec)
4.2 习题演练
第五章:简单的 DELETE 语句
5.1 使用 DELETE 删除数据
前面我们学习了插入,更新语句,但总有一些数据是我们不需要的,在实际生活中,会员卡过期,银行卡销户之类的,都需要用到 DELETE 关键字对原有的数据进行删除,下面我们就来介绍一下。
示例代码
DELETE FROM table_name
WHERE some_column
= some_value;
其中
- table_name 代表表名称
- some_column 代表列名称,如 id
- some_value 可以为任意值。some_column 和 some_value 构成 WHERE 子句中的搜索条件。
注意
请注意 SQL DELETE 语句中的 WHERE 子句。WHERE 子句规定哪条记录或者哪些记录需要删除。如果省略了 WHERE 子句,所有的记录都将被删除!
👇 我们可以通过下面的实例来感受一下 DELETE 的用法。
假如我们要从课程表 courses 中删除课程名为 ‘Dynamic Programming’ 的课程。
我们可以使用下面的 SQL 语句:
DELETE FROM courses
WHERE name
=’Dynamic Programming’;
执行输出结果:
mysql> DELETE FROM courses
WHERE name
=’Dynamic Programming’; Query OK, 1 row affected (0.01 sec) mysql> SELECT FROM courses
WHERE name
=’Dynamic Programming’; Empty set (0.01 sec)
可以看出删除后的课程表 courses 已经查询不到课程名 name 为动态规划 ‘Dynamic Programming’ 数据了。
*注意
我们可以在不删除表的情况下,删除表中所有的行,这意味着表结构、属性、索引将保持不变。
在删除记录时要格外小心!因为不能重来!