# SQL基础


## 1. 创建数据库表

​	创建数据表（Table）之前需要先创建数据库（Database），或者必许在已存在的数据库上创建数据表。

1. 数据库创建语句`CREATE DATABASE 数据库名`

   ```sql
   CREATE DATABASE shop;
   ```

2. 数据表创建

   ```sql
   CREATE TABLE &lt;表名&gt;
   (
   	&lt;列名1&gt; &lt;数据类型&gt; &lt;该列所需约束&gt;,
       ......
       &lt;该表的约束1&gt;, &lt;该表的约束2&gt;, .....
   );
   
   CREATE TABLE Product
   (
   	product_id		CHAR(4)			NOT NULL,	// 商品编号
       product_name 	VARCHAR(100)	NOT NULL,	// 商品名称
       product_type	VARCHAR(32)		NOT NULL,	// 商品种类
       sale_price		INTEGER,					// 销售单价
       purchase_price 	INTEGER,					// 进货单价
       regist_date		DATE,						// 登记日期
       PRIMARY KEY (product_id));
   )
   
   ```

   ## 表的删除和更新

   **表的删除**

   ```sql
   DROP TABLE 表名;
   ```

   **表定义的更新（`ALTER TABLE`）**

   1. 添加列

      ```sql
      ALTER TABLE &lt;表名&gt; ADD COLUMN &lt;列的定义&gt;;
      ```

   2. 删除列

      ```sql
      ALTER TABLE &lt;表名&gt; DROP COLUMN &lt;列名&gt;;
      ```

   3. 向表中插入数据

      ```sql
      INSERT INTO &lt;表名&gt; VALUES(&#39;VALUE1&#39;,&#39;VALUE2&#39;,...);
      
      START TRANSACTION;
      INSERT INTO Product VALUES (&#39;0001&#39;, &#39;T恤衫&#39;, &#39;衣服&#39;, 1000, 500, &#39;2009-09-20&#39;);
      INSERT INTO Product VALUES (&#39;0002&#39;, &#39;打孔器&#39;, &#39;办公用品&#39;, 500, 320, &#39;2009-09-11&#39;);
      INSERT INTO Product VALUES (&#39;0003&#39;, &#39;运动T恤&#39;, &#39;衣服&#39;, 4000, 2800, NULL);
      INSERT INTO Product VALUES (&#39;0004&#39;, &#39;菜刀&#39;, &#39;厨房用具&#39;, 3000, 2800, &#39;2009-09-20&#39;);
      INSERT INTO Product VALUES (&#39;0005&#39;, &#39;高压锅&#39;, &#39;厨房用具&#39;, 6800, 5000, &#39;2009-01-15&#39;);
      INSERT INTO Product VALUES (&#39;0006&#39;, &#39;叉子&#39;, &#39;厨房用具&#39;, 500, NULL, &#39;2009-09-20&#39;);
      INSERT INTO Product VALUES (&#39;0007&#39;, &#39;擦菜板&#39;, &#39;厨房用具&#39;, 880, 790, &#39;2008-04-28&#39;);
      INSERT INTO Product VALUES (&#39;0008&#39;, &#39;圆珠笔&#39;, &#39;办公用品&#39;, 100, NULL,&#39;2009-11-11&#39;);
      COMMIT;
      ```

   **表的修改**

   1. 变更表名

      ```sql
      RENAME TABLE &lt;原表名&gt; TO &lt;新表名&gt;;
      ```

      

## 2. 查询基础

### 2.1 `SELECT`语句基础

#### **列的查询**

```sql
SELECT &lt;列1&gt;, &lt;列2&gt;, .... FROM &lt;表名&gt;;

SELECT product_id, product_name, purchase_price FROM Product;
```

#### **查询出表中所有的列**

```sql
SELECT * FROM &lt;表名&gt;;

SELECT * FROM Product;
```

#### **为列设定别名**

别名可以使用中文，需要双引号括起来（&#34;&#34;）

```sql
SELECT &lt;列1&gt; AS &lt;别名1&gt;, .... FROM &lt;表名&gt;;

SELECT  product_id AS id, 
       product_name AS name, 
       purchase_price AS price 
  FROM Product;
  
SELECT product_id AS &#34;商品编号&#34;, 
       product_name AS &#34;商品名称&#34;, 
       purchase_price AS &#34;进货单价&#34;
  FROM Product;
```

#### **常数的查询**

```sql
SELECT &#39;商品&#39; AS string, 38 AS number, &#39;2009-02-24&#39; AS date,
       product_id, product_name
  FROM Product;
```

#### **从结果中删除重复行**

​	在使用 DISTINCT 时，NULL 也被视为一类数据。NULL 存在于多行中时，也会被合并为一条 NULL 数据。

```sql
SELECT DISTINCT &lt;列1&gt;, &lt;列2&gt; FROM &lt;表名&gt;;

SELECT DISTINCT product_type 
  FROM Product;
  
SELECT DISTINCT product_type, regist_date 
  FROM Product;
```

#### **条件查询（`WHERE`）**

`WHERE`字句要紧跟在`FROM`字句之后。

```sql
SELECT &lt;列名&gt;,... FROM &lt;表名&gt; WHERE &lt;条件表达式&gt;;

SELECT product_name, product_type 
  FROM Product
 WHERE product_type = &#39;衣服&#39;;
```

**注释**

- 单行注释，`--`
- 多行注释，`/*`和`*/`之间

### 2.2 算术运算符和比较运算符

- #### 算术运算符

  ```sql
  SELECT product_name, sale_price,
         sale_price * 2 AS &#34;sale_price_x2&#34; 
    FROM Product;
  ```

  四则运算：

  | 含义     | 运算符 |
  | -------- | ------ |
  | 加法运算 | &#43;      |
  | 减法运算 | -      |
  | 乘法运算 | *      |
  | 除法运算 | /      |

  SELECT子句中可以使用常数或者表达式。所有包含 NULL 的计算，结果肯定是 NULL。

- #### 比较运算符

  ```sql
  SELECT product_name, product_type 
    FROM Product
   WHERE sale_price &lt;&gt; 500;
  ```

  | 运算符 | 含义      |
  | ------ | --------- |
  | =      | 和~相等   |
  | &lt;&gt;     | 和~不相等 |
  | &gt;=     | 大于等于~ |
  | &gt;      | 大于~     |
  | &lt;=     | 小于等于~ |
  | &lt;      | 小于~     |

  ```sql
  SELECT product_name, product_type, sale_price 
    FROM Product
   WHERE sale_price &gt;= 1000;
   
  SELECT product_name, product_type, regist_date 
    FROM Product
   WHERE regist_date &lt; &#39;2009-09-27&#39;;
   
  SELECT product_name, sale_price, purchase_price 
    FROM Product
   WHERE sale_price - purchase_price &gt;= 500;
  ```

  **注意：一定要让不等号在左，等号在右。**

  字符串类型的数据原则上按照字典顺序进行排序，不能与数字的大小顺序混淆。

  **不能对NULL使用比较运算符**：SQL 提供了专门用来判断是否为 NULL 的 `IS (NOT) NULL` 运算符。

### 2.3 逻辑运算符

- `NOT`运算符

  NOT 不能单独使用，必须和其他查询条件组合起来使用。

  ```sql
  SELECT product_name, product_type, sale_price 
    FROM Product
   WHERE NOT sale_price &gt;= 1000;
  ```

  NOT运算符用来否定某一条件，但是不能滥用。

- `AND`运算符和`OR`运算符

  在 WHERE 子句中使用 `AND` 运算符或者 `OR` 运算符，可以对多个查询条件进行组合。

  - `AND` 运算符在其两侧的查询条件都成立时整个查询条件才成立，其意思相当于“并且”。

  - `OR` 运算符在其两侧的查询条件有一个成立时整个查询条件都成立，其意思相当于“或者”。

    ```sql
    SELECT  product_name, purchase_price 
      FROM Product
     WHERE  product_type = &#39;厨房用具&#39;
       AND sale_price &gt;= 3000;
    ```

  多个查询条件进行组合时，需要使用AND运算符或者OR运算符。

- 通过括号强化处理

  `AND` 运算符优先于 `OR` 运算符。

  ```sql
  SELECT product_name, product_type, regist_date 
    FROM Product
   WHERE product_type = &#39;办公用品&#39;
     AND (   regist_date = &#39;2009-09-11&#39;
          OR regist_date = &#39;2009-09-20&#39;);
  ```

- 逻辑运算符和真值

​	`NOT`、`AND` 和 `OR` 称为逻辑运算符。真值就是`TRUE`或者`FALSE`。

​	使用 `AND` 运算符进行的逻辑运算称为**逻辑积**，使用 `OR` 运算符进行的逻辑运算称为**逻辑和**。

## 3. 聚合与排序

### 3.1 对表进行聚合查询

#### 聚合函数

**常用函数：**

| 函数名 | 作用                               |
| ------ | ---------------------------------- |
| COUNT  | 计 算 表 中 的 记 录 数（ 行 数 ） |
| SUM    | 计算表中数值列中数据的合计值       |
| AVG    | 计算表中数值列中数据的平均值       |
| MAX    | 求出表中任意列中数据的最大值       |
| MIN    | 求出表中任意列中数据的最小值       |

用于汇总的函数称为聚合函数或者聚集函数。

```sql
SELECT COUNT(参数) FROM &lt;表名&gt;;
```

**计算NULL之外的数据的行数**

```sql
SELECT COUNT(purchase_price) // 为NULL不会计算进去
  FROM Product;
```

对于 `COUNT` 函数来说，参数列不同计算的结果也会发生变化。

`COUNT`函数的结果根据参数的不同而不同。`COUNT(*)`会得到包含`NULL`的数据行数，而`COUNT(&lt;列名&gt;)`会得到`NULL`之外的数据行数。

#### 计算合计值

使用`SUM()`函数。

```sql
SELECT SUM(sale_price), SUM(purchase_price) 
  FROM Product;
```

所有的聚合函数，如果以列名为参数，那么在计算之前就已经把`NULL` 排除在外了。因此，无论有多少个 `NULL` 都会被无视。

#### 计算平均值

使用`AVG ()`函数，语法与`SUM()`函数相同

```sql
SELECT AVG(sale_price) 
  FROM Product;
```

#### 计算最大值和最小值

最大值使用`MAX()`函数，最小值使用`MIN()`函数。

```sql
SELECT MAX(sale_price), MIN(purchase_price) 
  FROM Product;
  
SELECT MAX(regist_date), MIN(regist_date) 
  FROM Product;
```

#### 使用聚合函数删除重复值（关键字DISTINCT）

```sql
SELECT COUNT(DISTINCT product_type) 
  FROM Product;
```

想要计算值的种类时，可以在`COUNT`函数的参数中使用`DISTINCT`。

不仅限于 `COUNT` 函数，所有的聚合函数都可以使用 `DISTINCT`。

在聚合函数的参数中使用DISTINCT，可以删除重复数据。

### 3.2 对表进行分组

#### GROUP BY子句

**使用聚合函数和GROUP BY子句时需要注意以下 4点:**

1. 只能写在`SELECT`子句之中
2. `GROUP BY`子句中不能使用`SELECT`子句中列的别名
3. `GROUP BY`子句的聚合结果是无序的
4. `WHERE`子句中不能使用聚合函数

```sql
SELECT &lt;列名1&gt;, &lt;列名2&gt;, &lt;列名3&gt;, ……
  FROM &lt;表名&gt;
 GROUP BY &lt;列名1&gt;, &lt;列名2&gt;, &lt;列名3&gt;, ……;
 
SELECT product_type, COUNT(*) 
  FROM Product
 GROUP BY product_type;
```

在 `GROUP BY` 子句中指定的列称为**聚合键**或者**分组列**。`GROUP BY` 子句也和 `SELECT` 子句一样，可以通过逗号分隔指定多列。

`GROUP BY` 子句的书写位置也有严格要求，一定要写在`FROM` 语句之后（如果有 `WHERE` 子句的话需要写在 `WHERE` 子 句 之 后 ）。

#### 聚合键中包含NULL的情况

当聚合键中包含 `NULL` 时，也会将`NULL` 作为一组特定的数据。聚合键中包含`NULL`时，在结果中会以“不确定”行（空行）的形式表现出来。

#### 使用WHERE子句时GROUP BY的执行结果

```sql
SELECT &lt;列名1&gt;, &lt;列名2&gt;, &lt;列名3&gt;, ……
  FROM &lt;表名&gt;
 WHERE 
 GROUP BY &lt;列名1&gt;, &lt;列名2&gt;, &lt;列名3&gt;, ……;
```

像这样使用 `WHERE` 子句进行汇总处理时，会先根据 `WHERE` 子句指定的条件进行过滤，然后再进行汇总处理。

```sql
SELECT purchase_price, COUNT(*) 
  FROM Product
 WHERE product_type = &#39;衣服&#39;
 GROUP BY purchase_price;
```

`GROUP BY` 和` WHERE` 并用时 `SELECT` 语句的执行顺序:

```sql
FROM → WHERE → GROUP BY → SELECT
```

#### 与聚合函数和GROUP BY子句有关的常见错误

1. 在`SELECT`子句中书写了多余的列

   在使用 `COUNT` 这样的聚合函数时，`SELECT` 子句中的元素有严格的限制。实际上，使用聚合函数时，`SELECT` 子句中只能存在以下三种元素。

   - 常数
   - 聚合函数
   - `GROUP BY`子句中指定的列名（也就是聚合键）

   使用`GROUP BY`子句时，`SELECT`子句中不能出现聚合键之外的列名。

2. 在`GROUP BY`子句中写了列的别名

   在 `GROUP BY` 子句中是不能使用别名的。

3. `GROUP BY`子句的结果能排序吗

   不能排序，结果是随机的。

4. 在`WHERE`子句中使用聚合函数

   只有`SELECT`子句和`HAVING`子句（以及`ORDER BY`子句）中能够使用聚合函数。

### 3.3 为聚合结果指定条件

#### HAVING子句

对集合指定条件就需要使用`HAVING` 子句,`HAVING` 子句必须写在 `GROUP BY` 子句之后

```sql
SELECT &lt;列名1&gt;, &lt;列名2&gt;, &lt;列名3&gt;, …… 
  FROM &lt;表名&gt;
 GROUP BY &lt;列名1&gt;, &lt;列名2&gt;, &lt;列名3&gt;, …… 
HAVING &lt;分组结果对应的条件&gt;
```

使用`HAVING`子句时`SELECT`语句的顺序。

```sql
SELECT → FROM → WHERE → GROUP BY → HAVING
```

```sql
SELECT product_type, AVG(sale_price) 
  FROM Product
 GROUP BY product_type
HAVING AVG(sale_price) &gt;= 2500;
```

#### HAVING子句的构成要素

`HAVING` 子句中能够使用的 3 种要素:

- 常数
- 聚合函数
- `GROUP BY`子句中指定的列名（即聚合键）

`HAVING` 子句是用来指定“组”的条件的。因此，“行”所对应的条件还是应该写在 `WHERE` 子句当中。

`WHERE` 子句 = 指定行所对应的条件
`HAVING` 子句 = 指定组所对应的条件

聚合键所对应的条件不应该书写在`HAVING`子句当中，而应该书写在`WHERE`子句当中。

### 3.4 对查询结果进行排序

#### ORDER BY子句

```sql
SELECT &lt;列名1&gt;, &lt;列名2&gt;, &lt;列名3&gt;, …… 
  FROM &lt;表名&gt;
 ORDER BY &lt;排序基准列1&gt;, &lt;排序基准列2&gt;, ……
 
SELECT product_id, product_name, sale_price, purchase_price 
  FROM Product
ORDER BY sale_price;
```

子句的书写顺序:

```sql
 1. SELECT 子句 → 2. FROM 子句 → 3. WHERE 子句 → 4. GROUP BY 子句 → 5. HAVING 子句 → 6. ORDER BY 子句
```

#### 指定升序或降序

`ASC` 和 `DESC` 是 ascendent（上升的）和descendent（下降的）这两个单词的缩写。

```sql
SELECT product_id, product_name, sale_price, purchase_price 
  FROM Product
ORDER BY sale_price DESC;
```

未指定ORDER BY子句中排列顺序时会默认使用升序进行排列。

#### NULL的顺序

使用含有 `NULL` 的列作为排序键时，`NULL` 会在结果的开头或末尾汇总显示。

#### 在排序键中使用显示用的别名

在 `GROUP BY` 子句中**不能**使用`SELECT` 子句中定义的**别名**，但是在 `ORDER BY` 子句中却是**允许使用别名**的。

```sql
SELECT product_id AS id, product_name, sale_price AS sp, purchase_price
  FROM Product
ORDER BY sp, id;
```

使用 HAVING 子句时 SELECT 语句的顺序:

```sql
FROM → WHERE → GROUP BY → HAVING → SELECT → ORDER BY
```

`SELECT` 子句的执行顺序在 `GROUP BY` 子句之后，`ORDER BY` 子句之前。

#### ORDER BY子句中可以使用的列

`ORDER BY` 子句中也可以使用存在于表中、但并不包含在 `SELECT`子句之中的列

```sql
SELECT product_name, sale_price, purchase_price 
  FROM Product
ORDER BY product_id;
```

在`ORDER BY`子句中可以使用`SELECT`子句中未使用的列和聚合函数。

#### 不要使用列编号

## 4. 数据更新

### 4.1 数据的插入（INSERT语句的使用方法）

#### INSERT语句

```sql
INSERT INTO &lt;表名&gt; (列1, 列2, 列3, ……) VALUES (值1, 值2, 值3, ……);
```

```sql
INSERT INTO Product (product_id, product_name, product_type, sale_price, purchase_price, regist_date) VALUES (&#39;0001&#39;, &#39;T恤衫&#39;, &#39;衣服&#39;, 1000, 500, &#39;2009-09-20&#39;);
```

##### 多行INSERT

```sql
INSERT INTO ProductIns VALUES (&#39;0002&#39;, &#39;打孔器&#39;, &#39;办公用品&#39;, 500, 320, &#39;2009-09-11&#39;),
   (&#39;0003&#39;, &#39;运动T恤&#39;, &#39;衣服&#39;, 4000, 2800, NULL),
   (&#39;0004&#39;, &#39;菜刀&#39;, &#39;厨房用具&#39;, 3000, 2800, &#39;2009-09-20&#39;);
```

##### 列清单的省略

```sql
-- 包含列清单
INSERT INTO ProductIns (product_id, product_name, product_type, sale_price, purchase_price, regist_date) VALUES (&#39;0005&#39;, &#39;高压锅&#39;, &#39;厨房用具&#39;, 6800, 5000, &#39;2009-01-15&#39;);
-- 省略列清单
INSERT INTO ProductIns VALUES (&#39;0005&#39;, &#39;高压锅&#39;, &#39;厨房用具&#39;, 6800, 5000, &#39;2009-01-15&#39;);
```

##### 插入NULL

`INSERT` 语句中想给某一列赋予 `NULL` 值时，可以直接在 `VALUES`子句的值清单中写入 `NULL`。

要插入 `NULL` 的列一定不能设置 `NOT NULL` 约束。向设置了 `NOT NULL` 约束的列中插入 `NULL` 时，`INSERT` 语句会出错，导致数据插入失败。

##### 插入默认值

- 通过显式方法插入默认值

  在 `VALUES` 子句中指定 `DEFAULT` 关键字

  ```sql
  INSERT INTO ProductIns (product_id, product_name, product_type, sale_price, purchase_price, regist_date) VALUES (&#39;0007&#39;, &#39;擦菜板&#39;, &#39;厨房用具&#39;, DEFAULT, 790, &#39;2009-04-28&#39;);
  ```

  

- 通过隐式方法插入默认值

  插入默认值时也可以不使用 `DEFAULT` 关键字，只要在列清单和 `VALUES` 中省略设定了默认值的列就可以了。

  - 通过隐式方法设定默认值

    ```sql
    INSERT INTO Product (product_id, product_name, product_type, purchase_price, regist_date) VALUES (&#39;0007&#39;, &#39;擦菜板&#39;, &#39;厨房用具&#39;, 790, &#39;2009-04-28&#39;); // 省略了sale_price列及对应的值
    ```

  - 未设定默认值的情况

    ```sql
    -- 省略purchase_price列（无约束）：会赋予“NULL”
    INSERT INTO ProductIns (product_id, product_name, product_type, sale_price, regist_date) VALUES (&#39;0008&#39;, &#39;圆珠笔&#39;, &#39;办公用品&#39;, 100, &#39;2009-11-11&#39;);
    -- 省略product_name列（设置了NOT NULL约 束 ）：错 误 ！
    INSERT INTO ProductIns (product_id, product_type, sale_price, purchase_price, regist_date) VALUES (&#39;0009&#39;, &#39;办公用品&#39;, 1000, 500, &#39;2009-12-12&#39;);
    ```

    省略 `INSERT` 语句中的列名，就会自动设定为该列的默认值（没有默认值时会设定为 `NULL`）。

  ##### 从其他表中复制数据

  创建ProductCopy表(表结构与Product完全一致，只是表名不一样而已)：

  ```sql
  CREATE TABLE ProductCopy
  (
      product_id   CHAR(4)      NOT NULL, 
      product_name  VARCHAR(100) NOT NULL, 
      product_type VARCHAR(32)  NOT NULL, 
      sale_price  INTEGER,
      purchase_price INTEGER,
      regist_date   DATE,
      PRIMARY KEY (product_id)
  );
  ```

  ###### INSERT ... SELECT语句

  ```sql
  -- 将商品表中的数据复制到商品复制表中
  INSERT INTO ProductCopy (product_id, product_name, product_type, sale_price, purchase_price, regist_date) SELECT product_id, product_name, product_type, sale_price, purchase_price, regist_date FROM Product;
  ```

  **多种多样的SELECT语句**

  ProductType表:

  ```sql
  CREATE TABLE ProductType
  (
      product_type       VARCHAR(32) NOT NULL,
      sum_sale_price     INTEGER,
      sum_purchase_price INTEGER,
      PRIMARY KEY (product_type)
  );
  
  INSERT INTO ProductType (product_type, sum_sale_price, sum_purchase_price)
  SELECT product_type, SUM(sale_price), SUM(purchase_price)
  FROM Product
  GROUP BY product_type;
  ```

  ### 4.2 数据的删除（DELETE语句的使用方法）

  #### DROP TABLE语句和DELETE语句

  1.  `DROP TABLE` 语句可以将表完全删除
  2.  `DELETE` 语句会留下表（容器），而删除表中的全部数据

  ##### DELETE语句的基本语法

  ```sql
  DELETE FROM &lt;表名&gt;;
  ```

  DELETE语句的删除对象并不是表或者列，而是记录（行）。

  #### 指定删除对象的DELETE语句（搜索型DELETE）

  ```sql
  DELETE FROM &lt;表名&gt; WHERE &lt;条件&gt;;
  ```

  可以通过WHERE子句指定对象条件来删除部分数据。与 `SELECT` 语句不同的是，`DELETE` 语句中不能使用 `GROUP BY`、`HAVING` 和 `ORDER BY` 三类子句，而只能使用`WHERE` 子 句 。

  ##### 删除和舍弃

  - 只能删除表中全部数据的TRUNCATE语句

    ```sql
    TRUNCATE &lt;表名&gt;;
    ```

    与 `DELETE` 不同的是，`TRUNCATE` 只能删除表中的全部数据，而不能通过`WHERE` 子句指定条件来删除部分数据。也正是因为它不能具体地控制删除对象，
    所以其处理速度比 `DELETE` 要快得多。

  ### 4.3 数据的更新（UPDATE语句的使用方法）

  #### UPDATE语句的基本语法

  ```sql
  UPDATE &lt;表名&gt; 
  SET &lt;列名&gt; = &lt;表达式&gt;;
  ```

  #### 指定条件的UPDATE语句（搜索型UPDATE）

  ```sql
  UPDATE &lt;表名&gt;
  	SET &lt;列名&gt; = &lt;表达式&gt; 
  WHERE &lt;条件&gt;;
  ```

  #### 使用NULL进行更新

  使用 `UPDATE` 也可以将列更新为 `NULL`（该更新俗称为 `NULL` 清空）。

  ```sql
  UPDATE Product
     SET regist_date = NULL 
   WHERE product_id = &#39;0008&#39;;
  ```

  使用UPDATE语句可以将值清空为NULL（但只限于未设置NOT NULL约束的列）。

  #### 多列更新

  - 使用逗号对列进行分隔排列

    ```sql
    UPDATE Product
       SET sale_price = sale_price * 10,
           purchase_price = purchase_price / 2
     WHERE product_type = &#39;厨房用具&#39;;
    ```

  - 将列用()括起来的清单形式

    ```sql
    UPDATE Product
       SET (sale_price, purchase_price) = (sale_price * 10, purchase_price / 2)
     WHERE product_type = &#39;厨房用具&#39;;
    ```

  ### 4.4 事务

  #### 什么是事务

  事务就是需要在同一个处理单元中执行的一系列更新处理的集合 。

  #### 创建事务

  ```sql
  事务开始语句;
  	DML语句①;
  	DML语句②;
  	DML语句③;
  	......
  事务结束语句（COMMIT或者ROLLBACK）;
  ```

  使用事务开始语句和事务结束语句，将一系列 DML 语句（`INSERT`/`UPDATE`/`DELETE` 语句）括起来，就实现了一个事务处理。

  各个DBMS的事务开始语句：

  - SQL Server、PostgreSQL

    ```sql
    BEGIN TRANSACTION
    ```

  - MySQL

    ```sql
    START TRANSACTION
    ```

  - Oracle、DB2

    无

  **更新商品信息的事务**

  ```sql
  START TRANSACTION;
      -- 将运动T恤的销售单价降低1000日元
      UPDATE Product
         SET sale_price = sale_price - 1000
       WHERE product_name = &#39;运动T恤&#39;;
      -- 将T恤衫的销售单价上浮1000日元
      UPDATE Product
         SET sale_price = sale_price &#43; 1000
       WHERE product_name = &#39;T恤衫&#39;;
  COMMIT;
  ```

  #### COMMIT——提交处理

  `COMMIT` 是提交事务包含的全部更新处理的结束指令，相当于文件处理中的覆盖保存。一旦提交，就无法恢复到事务开始前的状态了。

  ##### COMMIT的流程 =直线进行

  ```sql
  1.事务开始语句 ------&gt; 2.执行更新语句（DML） ------&gt; 3.执行COMMIT
  ```

  结束后的状态：②中的所有更新都被反映到了数据库中

  虽然我们可以不清楚事务开始的时间点，但是在事务结束时一定要仔细进行确认。

  #### ROLLBACK——取消处理

  `ROLLBACK` 是取消事务包含的全部更新处理的结束指令，相当于文件处理中的放弃保存。一旦回滚，数据库就会恢复到事务开始之前的状态。

  ##### ROLLBACK的流程 =掉头回到起点

  ```sql
  1.事务开始语句 ------&gt; 2.执行更新语句(DML) ------&gt; 3.执行ROLLBACK
  ```

  结束后的状态：和①执行前相同

  ```sql
  START TRANSACTION;
      -- 将运动T恤的销售单价降低1000日元
      UPDATE Product
         SET sale_price = sale_price - 1000
       WHERE product_name = &#39;运动T恤&#39;;
      -- 将T恤衫的销售单价上浮1000日元
      UPDATE Product
         SET sale_price = sale_price &#43; 1000
       WHERE product_name = &#39;T恤衫&#39;;
  ROLLBACK;
  ```

  ##### 事务处理何时开始

  实际上，几乎所有的数据库产品的事务都无需开始指令。这是因为大部分情况下，事务在数据库连接建立时就已经悄悄开始了，并不需要用户再明确发出开始指令。

  **事务类别：**

  - 每条 SQL语句就是一个事务（自动提交模式）
  - 直到用户执行COMMIT或者ROLLBACK为止算作一个事务

  默认使用自动提交模式的 `DBMS` 有 `SQL Server`、`PostgreSQL` 和 `MySQL` 等

#### ACID特性

`DBMS` 的事务都遵循四种特性，将这四种特性的首字母结合起来统称为 ACID 特性。这是所有 `DBMS` 都必须遵守的规则。

- 原子性（Atomicity）

  原子性是指在事务结束时，其中所包含的更新处理要么全部执行，要么完全不执行，也就是要么占有一切要么一无所有。

- 一致性（Consistency）

  一致性指的是事务中包含的处理要满足数据库提前设置的约束，如主键约束或者 `NOT NULL` 约束等。

- 隔离性（Isolation）

  隔离性指的是保证不同事务之间互不干扰的特性。

- 持久性（Durability）

  持久性也可以称为耐久性，指的是在事务（不论是提交还是回滚）结束后，`DBMS` 能够保证该时间点的数据状态会被保存的特性。即使由于系统故障导致数据丢失，数据库也一定能通过某种手段进行恢复。

## 复杂查询

### 视图

#### 视图和表


---

> 作者: hiifong  
> URL: https://f.style/sql-basics/  

