🦏 第1章 数据库简介
📄 一句话概括

数据一开始都躺在文件里,问题是它只会躺着。 想让「找出所有身高超过 170 的女生」变成一句话就得自己写代码遍历, 数据库就是把「存」和「查」一起解决掉的东西。 Demo 里建了一张四字段的小表:插三行读三行,主键重复报 UNIQUE constraint failed, 姓名给空报 NOT NULL constraint failed——两条报错都是表结构在拦,不是程序在拦。

📝 本章和前面几章的关系

这是全书的第一篇,先把「数据库、表、列、行、主键、DBMS、SQL」这六个词摆正, 后面 18 章都在往这套骨架上挂东西。 本篇不写一句查询,只把「为什么要有数据库」讲清楚——这个前提没立住,后面的语法就只是背命令。

一、文件放数据,哪里不够用

存是能存:一个 CSV 放一万行学生,Excel 打开也就那样。 问题是三件事——查得慢(找一个人要扫全表)、一起改会乱(两个人同时改同一个文件, 后保存的赢)、格式靠自觉(这一行多一列、那一行少一列,程序就崩)。 再加上两条更硬的:断电写到一半,文件就废了;谁能看哪一列,文件系统管不了。 数据库把这五件事一起解决了:它有自己的文件格式、自己的索引、自己的并发控制、自己的日志、 自己的权限。你在外面看到的只是一张张表。

二、DBMS 是管数据库的那套软件

DBMS(数据库管理系统)就是那个「管」的东西:你发一句 SQL,它负责解析、优化、执行、落盘、加锁、 写日志。 名字要分清:数据库(database)是数据的集合,DBMS 是管理它的软件, MySQL 是一个具体的 DBMS。 平时说「装个数据库」,其实装的是 DBMS。 主流的那几个:MySQL(开源、Web 项目最多)、PostgreSQL(功能最全、SQL 标准跟得最紧)、 Oracle 与 SQL Server(商业、企业里多)、SQLite(一个文件就是一个库,嵌在手机和浏览器里)。 它们都属「关系型」:数据摆成二维表,表和表之间靠主键外键关联, 用 SQL 说话。 这个模型 1970 年提出,五十多年了还没被取代。

三、表、列、行、主键:四个词就够

表(table)是一张二维表,列(column)是字段,行(row)是一条记录。 这三样和 Excel 长得一样,差别在后面两样。 主键(primary key)是每一行的身份证:一表一个,不重复、不许空。 有了它,「第三行」这种说法就换成了「id 等于 3 的那行」,指认才稳。 外键(foreign key)是别人表的主键:成绩表里存 student_id,指向学生表的 id, 于是「这条成绩是谁的」不用再写一遍名字。 这两样是关系型数据库的骨架,第 9 章会一个个建出来。

四、MySQL 这个名字:它在哪、凭什么流行

MySQL 最早是瑞典 MySQL AB 公司的产品,后来被 Sun 收购,Sun 又被 Oracle 收购, 所以现在归 Oracle。它走双授权:开源版 GPL,商业版付费——这也是它能在 Web 时代铺开的原因之一。 它的位置:LAMP/LNMP 里的那个 M(Linux + Apache/Nginx + MySQL + PHP)。 博客、商城、论坛,很大一部分底座是它。 现在的主线是 8.0(2018 年发布):默认字符集换成 utf8mb4、 加了窗口函数(第 6 章的排名函数就靠它)、加了 CTE、JSON 支持大幅增强、支持角色。 本书按 8 标准写,所以有些语法在 5.7 上跑不了——这一点书里会标出来。

五、小结

教材小结: 数据库是有结构的数据集合,DBMS 是管理它的软件,关系型数据库用二维表存数据、用 SQL 操作; MySQL 是其中用得最广的一个。 本课补三条:一是主键与外键是「指认」和「关联」的两件工具,后面所有多表查询都建立在它们上面; 二是约束是表结构的一部分,写错了进不来,不靠程序自觉; 三是 SQL 是语言、MySQL 是产品,同一句 SQL 换个产品多半也能跑。

六、动手 Demo:一张表、两条约束、三个数

Demo 六段:建一张四字段的学生表,插三行读出来,再故意违反主键和 NOT NULL 各一次,最后数行数、 数字段、算平均身高。 重点看那两条报错——它们不是程序抛的,是表结构拦的。

import sqlite3
c = sqlite3.connect(":memory:")
c.execute("CREATE TABLE students (id INTEGER PRIMARY KEY, "
          "name TEXT NOT NULL, gender TEXT, height REAL)")
c.execute("INSERT INTO students VALUES (1, '张伟', '男', 172.5)")
c.execute("INSERT INTO students VALUES (2, '王芳', '女', 160.2)")
c.execute("INSERT INTO students VALUES (3, '李娜', '女', 165.0)")

# 主键重复:引擎直接拒绝
c.execute("INSERT INTO students VALUES (1, '重复的学号', '男', 170.0)")
# NOT NULL:空姓名进不来
c.execute("INSERT INTO students VALUES (4, NULL, '男', 170.0)")

c.execute("SELECT COUNT(*) FROM students")
c.execute("PRAGMA table_info(students)")   # 字段清单
c.execute("SELECT AVG(height) FROM students")

实跑输出:

--------------------------------------------------------------------
第一课:数据库、表、列、行、主键
--------------------------------------------------------------------

  先造一张表:四个字段,一个主键
  SQL> CREATE TABLE students ( id INTEGER PRIMARY KEY, name TEXT NOT NULL, gender TEXT,
       height REAL)
  完成

  插三行,再整表读出来
  SQL> INSERT INTO students VALUES (1, '张伟', '男', 172.5)
  影响 1 行
  SQL> INSERT INTO students VALUES (2, '王芳', '女', 160.2)
  影响 1 行
  SQL> INSERT INTO students VALUES (3, '李娜', '女', 165.0)
  影响 1 行

  SQL> SELECT id, name, gender, height FROM students
  id     name     gender     height
  ----------------------------------
  1      张伟     男         172.50
  2      王芳     女         160.20
  3      李娜     女         165.00
  (3 行)

  主键是硬约束:同一个 id 插第二次,引擎当场拒绝
  SQL> INSERT INTO students VALUES (1, '重复的学号', '男', 170.0)
  报错:UNIQUE constraint failed: students.id
  **这条报错就是主键在干活:它保证每一行都能被唯一指认。**

  NOT NULL 也是硬约束:姓名不许空着进库
  SQL> INSERT INTO students VALUES (4, NULL, '男', 170.0)
  报错:NOT NULL constraint failed: students.name
  **name 后面写了 NOT NULL,所以空值进不来。这条约束是「表结构」的一部分,不是程序里 if 出来的。**

  数一数这张表:几行、几个字段、平均身高多少
  行数 3,字段数 4,平均身高 165.90
  **这三个数分别对应「行」「列」「算出来的值」——数据库能存也能算,这是它和一张 Excel 表最大的不同。**

  字段清单:名字、类型、能不能空、是不是主键
  位置  字段名    允许空    主键
  --------------------------------------
  0     id        是        是
  1     name      否        否
  2     gender    是        否
  3     height    是        否
  (4 行)
  **这张表就是 Navicat 里「设计表」那个界面背后的东西。**

第一段建表。 四个字段:id 是主键,name 不许空,gender 和 height 随便。 这张表的结构就是后面所有例子的地基。 第二段插三行,第三段整表读出来——三行四列,一行不少。 第四段主键重复。 报 UNIQUE constraint failed: students.id, 注意它连「哪一列」都点出来了。 第五段空姓名。 报 NOT NULL constraint failed: students.name。 这两条报错合起来说明一件事:约束写在表上,谁写数据都得守。 第六段数数。 行数 3、字段数 4、 平均身高 165.90——前两个是「存」,第三个是「算」,数据库两样都能干。 第七段字段清单。 四个字段的类型、能不能空、是不是主键, 这张表就是图形工具「设计表」界面的数据来源。

30 秒实验

  1. 把 id INTEGER PRIMARY KEY 改成 id INTEGER,再插一次重复的 1,还报错吗?
  2. 把 name TEXT NOT NULL 改成 name TEXT,插空姓名会怎样?
  3. (先想再跑)不建主键的表,SELECT * FROM students 还能正常读吗?

常见误区

  1. 把数据库当「大号 Excel」。 Excel 没有主键、没有约束、没有并发控制,多人同时改就丢数据, 这是它和数据库最本质的差别。
  2. 以为约束要靠程序写 if。 约束写在表结构上,绕过应用直接敲 SQL 也拦得住。
  3. 把 SQL 和 MySQL 混成一个词。 SQL 是语言,MySQL 是产品; 同一句 SELECT 在 PostgreSQL、SQLite 上基本一样。
自测三问(点开对答案) 1. 数据库和 DBMS 有什么区别? —— 数据库是数据的集合;DBMS 是管理这些数据的软件,负责解析 SQL、执行、加锁、写日志。 2. 主键和外键各管什么? —— 主键是**本表**每一行的唯一标识,不重复不许空;外键是**别人表**的主键,用来把两张表关联起来。 3. 为什么说「约束是表结构的一部分」? —— 因为约束定义在表上,由数据库自己检查,任何写入方式(程序、命令行、导入)都绕不过去。

知道了数据库是什么, 接下来得先把它装上。 下一章看 MySQL 装完到底装了什么,以及这台沙箱为什么装不了。

相关笔记