# 21.1 数据库概论

关系型数据库基于关系模型，以二维表格存储数据，通过表间关联关系组织、管理与查询数据。

关系型数据库管理系统（Relational Database Management System，RDBMS）通过结构化查询语言（Structured Query Language，SQL）提供数据定义、数据操作和数据控制功能，通过事务机制保证数据的一致性与完整性。

在此基础上，本部分介绍关系型数据库的基本操作与 SQL 语法。

## 在 UNIX 系统中连接数据库并执行 SQL 脚本

在 UNIX 或类 UNIX 系统中，可通过命令行连接数据库并执行 SQL 脚本。

以下命令连接 MySQL 数据库并执行 SQL 脚本：

```sh
# mysql -u root -p          # 以 root 用户登录 MySQL，-u 指定用户名，-p 表示需要密码验证
mysql> source FileName.sql  # 在 MySQL 交互式界面中执行指定的 SQL 文件，source 命令读取文件内容并逐条执行 SQL 语句
```

## 建立数据库

在关系型数据库中，数据库是存储相关数据表的容器，每个数据库具有独立的权限控制机制与存储空间。

可通过 SQL 语句创建新的数据库：

```sql
create database db_name;
```

* `db_name`：数据库名，用于标识数据库。数据库名在数据库实例中必须唯一。

## 数据库重命名

```sql
rename database old_name to new_name;
```

* `old_name`：旧数据库名，要重命名的数据库的当前名称
* `new_name`：新数据库名，重命名后的数据库名称

在 MySQL 中，`RENAME DATABASE` 存在于早期版本，但在 MySQL 5.1.23 之后已移除，原因是该操作存在安全风险。

现代 MySQL 需要重命名数据库时，应先导出数据，再创建新数据库并导入数据。

## 查看数据库

```sql
show databases;
```

## 进入数据库

```sql
use db_name;
```

* `db_name`：数据库名

## 删除数据库

```sql
drop database db_name;
```

* `db_name`：数据库名

## 更改数据库字符集

```sql
alter database xxx
default character set utf8mb4;
```

* `xxx`：数据库名
* `utf8mb4`：字符集名称，指定数据库的默认字符集

## SQL 数据类型

SQL 数据类型定义了表中列可以存储的数据种类和格式。

* `tinyint`（1B）、`smallint`（2B）、`mediumint`（3B）、`int`（4B）、`bigint`（8B）
* `float`（4B）、`double`（8B）、`decimal(整体位数, 小数点后位数)`
* `char` 是字符类型，若需存储字符串，则需使用 `char(字符数)`
* `unsigned + 数据类型` 可设置为无符号的数据类型

## 建立表

```sql
create table table_name (
  column_name data_type,
  column_name data_type,
  column_name data_type
);
```

* `table_name`：表名
* `column_name`：列名
* `data_type`：数据类型

## 表重命名

```sql
alter table old_name rename to new_name;
```

* `old_name`：旧表名
* `new_name`：新表名

## 查看数据库中有哪些表

```sql
show tables;
```

## 展示表结构

```sql
desc table_name;
```

## 显示表全部信息（select \* from 表名）

```sql
select * from table_name;
```

## SQL 语法：注释

单行注释：

```sql
-- 这是单行注释：注释不会被执行，用于说明 SQL 语句的用途或注意事项
```

多行注释：

```sql
/*
   这里是多行注释
   可以跨越多行，用于描述复杂的业务逻辑或临时禁用代码块
*/
```

## 主键、唯一约束、非空约束与自动编号

```sql
create table 表名
(
  -- 主键
  列名 数据类型 primary key,
  -- 唯一
  列名 char(20) unique,
  -- 不允许为空
  列名 数据类型 not null,
  -- 自动编号
  列名 数据类型 auto_increment
);
```

## 信息写入表

```sql
insert into table_name (col1, col2) values (123, '李明');
```

* `col1`：列名 1
* `col2`：列名 2

## 表新增列

```sql
alter table table_name
add column new_col data_type;
```

* `new_col`：新列名
* `data_type`：数据类型

## 表删除列

```sql
alter table 表名 drop column 列名;
```

## 修改表列数据类型

```sql
alter table table_name modify column col_name new_data_type;
```

* `new_data_type`：新数据类型

## 表添加主键

```sql
alter table table_name
add primary key (col_name);
```

* `col_name`：作为主键的列名（通常为已存在的列）

## 表删除主键

```sql
alter table table_name drop primary key;
```

## 唯一性约束

### 表添加唯一性约束

```sql
alter table table_name
add constraint constraint_name unique (col_name);
```

* `constraint_name`：唯一约束名
* `col_name`：列名

### 表删除唯一性约束

```sql
alter table table_name
drop index constraint_name;
```

## 外键

### 添加外键

```sql
ALTER TABLE table_name
ADD CONSTRAINT fk_column_name
FOREIGN KEY (fk_column)
REFERENCES tstu(sid);
```

* `fk_column_name`：外键名
* `fk_column`：子表中的外键列名

### 删除外键

```sql
alter table table_name
drop foreign key fk_name;
```

* `table_name`：子表名
* `fk_name`：外键名

### 外键的概念

外键用于建立两个表之间的关联关系，由一列或多列组成，指向另一个表的主键或唯一键，确保数据的一致性与完整性。数据库管理系统在外键约束上维护参照完整性，确保子表中的外键值必须存在于父表的主键或唯一键中，或者为 NULL（如果允许）。

子表：包含外键的表，子表中的外键列指向父表的主键或唯一键。例如上文示例中的子表。

父表：外键引用的表，父表中的被引用列通常是主键（PRIMARY KEY）或唯一键（UNIQUE）。例如上文示例中的父表。

### 父表数据删除失败

原因：子表中存在引用该数据的记录，数据库管理系统会阻止删除操作以维护参照完整性。 解决：使用 `ON DELETE CASCADE` 自动删除子表中的相关记录，或先手动删除子表记录，再删除父表记录。 `ON DELETE CASCADE` 是外键约束的一个选项，父表记录删除时，数据库自动删除子表中所有引用该记录的行。此机制在“自动维护父表和子表之间的参照完整性”一节中有详细说明。

### 父子表和外键示例

```sql
-- 创建父表 customers，包含以下列：
CREATE TABLE customers (
    id INT AUTO_INCREMENT PRIMARY KEY, -- 主键，自动编号
    name VARCHAR(100) NOT NULL,        -- 客户名
    email VARCHAR(150) UNIQUE          -- 唯一的电子邮件
);

-- 创建子表 orders
CREATE TABLE orders (
    order_id INT AUTO_INCREMENT PRIMARY KEY, -- 主键，订单编号
    order_date DATE NOT NULL,                -- 订单日期
    customer_id INT,                         -- 外键列，关联客户 ID
    amount DECIMAL(10,2),                    -- 订单金额
    FOREIGN KEY (customer_id) REFERENCES customers(id) -- 定义外键
    ON DELETE CASCADE -- 当父表记录被删除时，级联删除子表相关记录
    ON UPDATE CASCADE -- 当父表主键更新时，子表外键自动更新
);
```

1. 创建父表 customers： 包含列： id：主键，用于唯一标识客户。 name：客户姓名。 email：唯一约束，用于防止重复的电子邮件地址。
2. 创建子表 orders： order\_id：主键，用于唯一标识订单。 order\_date：记录订单日期。 customer\_id：外键列，用于关联客户。 amount：订单金额。
3. 设置外键： `FOREIGN KEY (customer_id) REFERENCES customers(id)` 表示子表的 customer\_id 列引用父表的 id 列。 `ON DELETE CASCADE` 删除父表记录时，子表中引用该记录的行也会被删除。 `ON UPDATE CASCADE` 更新父表主键时，子表外键列会自动更新。

## 修改列的数据类型

```sql
ALTER TABLE 表名
MODIFY COLUMN 列名 新数据类型;

-- 例如（重设数据类型并不允许为空、设置默认值）
ALTER TABLE book_table
MODIFY COLUMN BookName CHAR(100) NOT NULL DEFAULT '《书名》';

-- 如果修改的列是外键，必须先删除外键约束，然后修改数据类型，最后重新添加外键约束
alter table 子表 drop FOREIGN KEY 外键名;
alter table 子表 modify column 列名 新数据类型;
alter table 子表 add constraint 外键名 FOREIGN KEY (列名) REFERENCES 父表名(父列名);
```

## 修改表信息、删除行

```sql
-- 修改表信息
update 表名
set 列名 = 新值
where 条件;
```

`SET` 子句用于指定需要修改的列。如需仅修改部分记录，应通过 `WHERE` 子句限定条件，以明确修改范围。

```sql
-- 删除行
DELETE FROM book_table
WHERE BookNumber < 200;
```

下面给出几个示例：

```sql
update book_table
set Price = Price * 1.2
where Publisher = "人民邮电出版社";
```

该示例将 `book_table` 表中 `Publisher` 值为“人民邮电出版社”的记录的 `Price` 值统一乘以 `1.2`，即在原价基础上增加 20%。

```sql
DELETE FROM book_table
WHERE BookNumber < 200;

DELETE FROM book_table
WHERE author IN ('王阳', '刘天洋');
```

### WHERE 子句

WHERE 子句是 SQL 语句中的重要组成部分，用于指定筛选条件，仅操作符合条件的记录。

```sql
...(update 或 delete)
where Price < 50;
...(update 或 delete)
WHERE author = '王阳' or author = '刘天洋';
...(update 或 delete)
WHERE name in ('张三', '李四');
```

1. 若 `Price` 值小于 50，则执行相应操作。
2. 若 `author` 值为王阳或刘天洋，则执行相应操作。
3. 若 `name` 值为张三或李四，则执行相应操作。

WHERE 子句指定条件表达式，该表达式计算并返回布尔值。

### SQL 运算符

运算符是 SQL 语句中用于数据计算和比较的符号，分为算术运算符、比较运算符、逻辑运算符和特殊运算符等几类。

#### 算术运算符

* `+` 加法运算符
* `-` 减法运算符
* `*` 乘法运算符
* `/` 除法运算符
* `%` 取模运算符

#### 比较运算符

* `=` 等于（在 SQL 中用于比较，而非赋值）
* `!=` 不等于
* `<` 小于
* `>` 大于
* `<=` 小于等于
* `>=` 大于等于
* `<=>` NULL 安全等于，NULL <=> NULL，返回结果是 TRUE

#### 逻辑运算符

* `and` 两条件都为真时才返回真，否则为假
* `or` 两条件有一个为真时返回真，全为假时才返回假
* `not` 反转真假
* `xor` 仅一个条件为真时才返回真，否则返回假

#### 特殊运算符

* `in`：“值在列表中”运算符，用于筛选符合条件的记录，并返回布尔结果，供 update 或 delete 语句使用。例如 `5 in (1, 3, 5)` 返回真。
* `between` 范围匹配：例如 `5 between 1 and 10` 返回真。
* `like` 模式匹配：`'abc' like 'a%'` 返回真。百分号在此处功能类似 shell 通配符。
* `IS NULL`“判断表项是否为 NULL”：`NULL IS NULL` 返回 TRUE。 示例：查看书名条目是否存在 `《xxx 设计 yyy》`。

```sql
select * from book_table
where BookName like '%设计%';

select * from book_table
where BookName like '_____';
-- 五个下划线代表五个字，返回长度为 5 个字的书名。
```

在书籍管理数据库中，使用 LIKE '%设计%' 会返回类似《MySQL 数据库设计》的书名。

## 自动维护父表和子表之间的参照完整性

参照完整性确保相关表之间数据的一致性。父表数据变化时，子表中的相关数据也需相应调整。

* **CASCADE** 级联操作：父表记录删除/更新时，子表中相关记录也一并删除/更新。
* **SET NULL** 将子表中的外键列设置为 NULL（要求外键列允许 NULL）。
* **RESTRICT** 拒绝操作：不允许删除/更新父表中被引用的记录（立即返回错误）。
* **NO ACTION** 类似 RESTRICT。在 MySQL 中，二者完全等价，均立即拒绝违反参照完整性的操作。在 SQL 标准中，NO ACTION 在语句结束时检查完整性，而非延迟到事务结束。
* **SET DEFAULT** 设置为默认值，MySQL 不支持。

## SELECT 语句

SELECT 语句是 SQL 中最常用的查询语句，用于从数据库中检索数据。SELECT 关键字后指定要查询的列名，使用 `*` 表示选择所有列。SELECT 语句的基本语法包括 SELECT 子句（指定要返回的列）、FROM 子句（指定数据源表）、WHERE 子句（指定过滤条件）、GROUP BY 子句（分组聚合）、HAVING 子句（分组过滤）和 ORDER BY 子句（结果排序）。

```sql
select * from student_table
where age = (select max(age) from student_table);
```

该查询用于显示 student\_table 中年龄等于该表最大年龄值的记录。内部子查询 `(select max(age) from student_table)` 首先执行，返回表中的最大年龄值；外部查询使用 WHERE 子句过滤出年龄等于该最大值的所有记录。

### 升序输出和降序输出

排序是查询的重要功能，通过 ORDER BY 子句可以指定查询结果的排序方式。

ASC 升序，DESC 降序。

```sql
SELECT student_name FROM student_table ORDER BY student_name DESC;
```

`LIMIT a, b` 用于限制查询结果的数量，其中 a 表示起始位置（从 0 开始），b 表示返回的记录条数。

```sql
SELECT * from `学生表`
ORDER BY `学号` desc
LIMIT 0,3;
```

### 连接查询

连接查询是关系型数据库的核心功能，可从多个相关联的表中同时获取数据。

#### 显式连接

```sql
SELECT name, score --name 来自学生表，score 来自分数表
FROM stu_table -- 表一
JOIN score_table -- 表二
ON stu_table.stu_id = score_table.s_id; -- 连接条件
WHERE name = 'Jack' -- 筛选条件，后面还可以接上 AND 或 OR。
```

`name` 取自 `stu_table` `score` 取自 `score_table`

#### 隐式连接

```sql
SELECT name, score
FROM stu_table, score_table
WHERE stu_table.stu_id = score_table.s_id
AND name = 'Jack';
```

虽然隐式连接语法较为简洁，但实际开发中推荐使用显式连接，可提高可读性和可维护性。


---

# Agent Instructions: Querying This Documentation

If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter:

```
GET https://book.bsdcn.org/di-21-zhang-shu-ju-ku-guan-li/di-21.1-jie-shu-ju-ku-gai-lun.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
