SQL必知必会(第四版)-- Learn01

基于Mysql5.6 学习此书

Lesson11 子查询

1. 利用子查询进行过滤

例如:查找购买物品数量>=10的客户

SELECT cust_name, cust_contact FROM customers 
WHERE cust_id IN (
    SELECT cust_id FROM orders 
    WHERE order_num IN (
        SELECT order_num FROM orderitems WHERE quantity >= 10)
);

-----------
mysql> SELECT cust_name, cust_contact FROM customers WHERE cust_id IN (SELECT cust_id FROM orders WHERE order_num IN (SELECT order_num FROM orderitems WHERE quantity >= 10));
+----------------+--------------+
| cust_name      | cust_contact |
+----------------+--------------+
| Coyote Inc.    | Y Lee        |
| Yosemite Place | Y Sam        |
| E Fudd         | E Fudd       |
+----------------+--------------+
3 rows in set (0.04 sec)

 使用子查询并不总是执行复杂 SELECT 操作的最有效方法,
下面是使用联结的相同查询:

SELECT 	c.cust_name,
		c.cust_contact 
	FROM customers c, orders o, orderitems oi 
	WHERE c.cust_id=o.cust_id 
		and oi.order_num=o.order_num 
		and oi.quantity >= 10;
		
+----------------+--------------+
| cust_name      | cust_contact |
+----------------+--------------+
| Coyote Inc.    | Y Lee        |
| Yosemite Place | Y Sam        |
| E Fudd         | E Fudd       |
+----------------+--------------+
3 rows in set (0.02 sec)

 或

SELECT 	c.cust_name, 
		c.cust_contact 
	FROM customers c INNER JOIN orders o INNER JOIN orderitems oi 
		ON c.cust_id=o.cust_id 
		   and oi.order_num=o.order_num 
		   and oi.quantity >= 10;

+----------------+--------------+
| cust_name      | cust_contact |
+----------------+--------------+
| Coyote Inc.    | Y Lee        |
| Yosemite Place | Y Sam        |
| E Fudd         | E Fudd       |
+----------------+--------------+
3 rows in set (0.00 sec)

2. 作为计算字段使用子查询

假如需要显示 Customers 表中
每个顾客的订单总数。订单与相应的顾客 ID存储在 Orders 表中。
执行这个操作,要遵循下面的步骤:
(1) 从 Customers 表中检索顾客列表;
(2) 对于检索出的每个顾客,统计其在 Orders 表中的订单数目。

使用子查询:

select 	cust_name, 
		cust_state, 
		(select count(*) from orders where orders.cust_id=customers.cust_id) as orders 
	from customers 
	order by cust_name;
	
+----------------+------------+--------+
| cust_name      | cust_state | orders |
+----------------+------------+--------+
| Coyote Inc.    | MI         |      2 |
| E Fudd         | IL         |      1 |
| Mouse House    | OH         |      0 |
| Wascals        | IN         |      1 |
| Yosemite Place | AZ         |      1 |
+----------------+------------+--------+
5 rows in set (0.27 sec)

提示:不止一种解决方案
正如这一课前面所述,虽然这里给出的样例代码运行良好,但它并不
是解决这种数据检索的最有效方法。在后面两课学习 JOIN 时,我们
还会遇到这个例子。

使用联结JOIN:

select 	c.cust_name, 
		c.cust_state, 
		count(o.order_num) orders 
	from customers c left join orders o 
		on c.cust_id=o.cust_id 
	group by c.cust_id 
	order by c.cust_name;
+----------------+------------+--------+
| cust_name      | cust_state | orders |
+----------------+------------+--------+
| Coyote Inc.    | MI         |      2 |
| E Fudd         | IL         |      1 |
| Mouse House    | OH         |      0 |
| Wascals        | IN         |      1 |
| Yosemite Place | AZ         |      1 |
+----------------+------------+--------+
5 rows in set (0.00 sec)

总结

子查询常用于 WHERE 子句的 IN 操作符中, 以及用来填充计算列。

Lesson12 联接表

SQL最强大的功能之一就是能在数据查询的执行中联结(join)表。

为什么使用联结?
如前所述,将数据分解为多个表能更有效地存储,更方便地处理,并且可伸缩性更好。但这些好处是有代价的。
如果数据存储在多个表中,怎样用一条 SELECT 语句就检索出数据呢?
答案是使用联结。简单说,联结是一种机制,用来在一条 SELECT 语句中关联表,因此称为联结。使用特殊的语法,可以联结多个表返回一组输出,联结在运行时关联表中正确的行。

联结

笛卡儿积(cartesian product)
由没有联结条件的表关系返回的结果为笛卡儿积。检索出的行的数目将是第一个表中的行数乘以第二个表中的行数。
没有 WHERE 子句,第一个表中的每一行将与第二个表中的每一行配对,而不管它们逻辑上是否能配在一起。

有时,返回笛卡儿积的联结,也称叉联结(cross join)

联结就是从两个表的笛卡尔积运算结果中选取某些行和列。换句话说,连接就是有条件的笛卡尔积。

SELECT vend_name, prod_name, prod_price FROM Vendors, Products ;

+----------------+----------------+------------+
| vend_name      | prod_name      | prod_price |
+----------------+----------------+------------+
| Anvils R Us    | .5 ton anvil   |       5.99 |
| LT Supplies    | .5 ton anvil   |       5.99 |
| ACME           | .5 ton anvil   |       5.99 |
| Furball Inc.   | .5 ton anvil   |       5.99 |
| Jet Set        | .5 ton anvil   |       5.99 |
| Jouets Et Ours | .5 ton anvil   |       5.99 |
| Anvils R Us    | 1 ton anvil    |       9.99 |
| LT Supplies    | 1 ton anvil    |       9.99 |
| ACME           | 1 ton anvil    |       9.99 |
| Furball Inc.   | 1 ton anvil    |       9.99 |
| Jet Set        | 1 ton anvil    |       9.99 |
| Jouets Et Ours | 1 ton anvil    |       9.99 |
| Anvils R Us    | 2 ton anvil    |      14.99 |
| LT Supplies    | 2 ton anvil    |      14.99 |
| ACME           | 2 ton anvil    |      14.99 |
| Furball Inc.   | 2 ton anvil    |      14.99 |
| Jet Set        | 2 ton anvil    |      14.99 |
| Jouets Et Ours | 2 ton anvil    |      14.99 |
| Anvils R Us    | Detonator      |      13.00 |
| LT Supplies    | Detonator      |      13.00 |
......

相应的笛卡儿积不是我们想要的。这里返回的数据用每个供应商匹配了每个产品,包括了供应商不正确的产品(即使
供应商根本就没有产品) 。所以,不要忘了 WHERE 子句。

SELECT vend_name, prod_name, prod_price 
FROM Vendors, Products 
WHERE Vendors.vend_id = Products.vend_id;
+-------------+----------------+------------+
| vend_name   | prod_name      | prod_price |
+-------------+----------------+------------+
| Anvils R Us | .5 ton anvil   |       5.99 |
| Anvils R Us | 1 ton anvil    |       9.99 |
| Anvils R Us | 2 ton anvil    |      14.99 |
| LT Supplies | Fuses          |       3.42 |
| LT Supplies | Oil can        |       8.99 |
| ACME        | Detonator      |      13.00 |
| ACME        | Bird seed      |      10.00 |
| ACME        | Carrots        |       2.50 |
| ACME        | Safe           |      50.00 |
| ACME        | Sling          |       4.49 |
| ACME        | TNT (1 stick)  |       2.50 |
| ACME        | TNT (5 sticks) |      10.00 |
| Jet Set     | JetPack 1000   |      35.00 |
| Jet Set     | JetPack 2000   |      55.00 |
+-------------+----------------+------------+
14 rows in set (0.00 sec)

多张表联结

SELECT prod_name, vend_name, prod_price, quantity 
FROM OrderItems, Products, Vendors 
WHERE Products.vend_id = Vendors.vend_id 
  AND OrderItems.prod_id = Products.prod_id 
  AND order_num = 20007;
  
+----------------+-----------+------------+----------+
| prod_name      | vend_name | prod_price | quantity |
+----------------+-----------+------------+----------+
| TNT (5 sticks) | ACME      |      10.00 |      100 |
+----------------+-----------+------------+----------+
1 row in set (0.18 sec)

 应该注意,不要联结不必要的表。联结的表越多,性能下降越厉害。

内联结(INNER JOIN)

SELECT vend_name, prod_name, prod_price FROM Vendors, Products WHERE Vendors.vend_id = Products.vend_id;

之前使用的这种是简单的等值语法。上面的又可以写成 inner join,ANSI SQL 规范首选 INNER JOIN 语法,这是标准格式。但简单语法简单也很常用。

SELECT vend_name, prod_name, prod_price
FROM Vendors INNER JOIN Products
  ON Vendors.vend_id = Products.vend_id;

也可以用WHERE : SELECT vend_name, prod_name, prod_price FROM Vendors INNER JOIN Products WHERE Vendors.vend_id = Products.vend_id;

区别:where后面增加的条件是对临时表生成后,进行过滤的。而on条件是在生成临时表时使用的条件。

这句不加ON条件,也是笛卡尔积。
注意:left / right join 后不能不加on条件,否则报错。可以这么写 select * from stu1 left join stu2 on 1=1;这样也是笛卡尔积。

迄今为止,我们使用的只是内联结或等值联结的简单联结。现在来看三种
其他联结: 自联结 (self-join) 、 自然联结 (natural join) 和外联结 (outer join) 。

自联结 (self-join)

即自己与自己联结

假如要给与 Jim Jones同一公司的所有顾客发送一封信件。这个查询要求,首先找出 Jim Jones工作的公司,然后找出在该公司工作的顾客。
下面是解决此问题的一种方法

SELECT cust_id, cust_name, cust_contact
FROM Customers
WHERE cust_name = (SELECT cust_name FROM Customers WHERE cust_contact = 'Jim Jones');

这是第一种解决方案,使用了子查询。内部的 SELECT 语句做了一个简单检索,返回 Jim Jones工作公司的 cust_name 。该名字用于外部查询的 WHERE 子句中,以检索出为该公司工作的所有雇员。

SELECT c1.cust_id, c1.cust_name, c1.cust_contact
FROM Customers  c1, Customers  c2
WHERE c1.cust_name = c2.cust_name
  AND c2.cust_contact = 'Jim Jones';

 此查询中需要的两个表实际上是相同的表,因此 Customers 表在 FROM 子句中出现了两次。这就是自联结。

提示:用自联结而不用子查询,一般自联结比子查询速度快。

自然联结 (natural join)

无论何时对表进行联结,应该至少有一列不止出现在一个表中(被联结的列) 。标准的联结(前一课中介绍的内联结)返回所有数据,相同的列甚至多次出现。自然联结排除多次出现,使每一列只返回一次。
怎样完成这项工作呢?答案是,系统不完成这项工作,由你自己完成它。
自然联结要求你只能选择那些唯一的列,一般通过对一个表使用通配符
( SELECT * ) ,而对其他表的列使用明确的子集来完成。下面举一个例子:

SELECT C.*, O.order_num, O.order_date,
	OI.prod_id, OI.quantity, OI.item_price
FROM Customers  C, Orders  O, OrderItems  OI
WHERE C.cust_id = O.cust_id
	AND OI.order_num = O.order_num
	AND prod_id = 'RGAN01';

 外联结 (outer join)

许多联结将一个表中的行与另一个表中的行相关联,但有时候需要包含没有关联行的那些行。例如,可能需要使用联结完成以下工作:

  • 对每个顾客下的订单进行计数,包括那些至今尚未下订单的顾客;
  • 列出所有产品以及订购数量,包括没有人订购的产品;
  • 计算平均销售规模,包括那些至今尚未下订单的顾客。

在上述例子中,联结包含了那些在相关表中没有关联行的行。这种联结称为外联结

包括:左联结(LEFT OUTER JOIN 或 LEFT JOIN),右联结(RIGHT OUTER JOIN 或 RIGHT JOIN)

例如:

SELECT Customers.cust_id, Orders.order_num 
FROM Customers LEFT OUTER JOIN Orders 
  ON Customers.cust_id = Orders.cust_id;

+---------+-----------+
| cust_id | order_num |
+---------+-----------+
|   10001 |     20005 |
|   10001 |     20009 |
|   10002 |      NULL |
|   10003 |     20006 |
|   10004 |     20007 |
|   10005 |     20008 |
+---------+-----------+
6 rows in set (0.00 sec)
  • INNER JOIN(内连接,或等值连接):获取两个表中字段匹配关系的记录。
  • LEFT JOIN(左连接):获取左表所有记录,即使右表没有对应匹配的记录。
  • RIGHT JOIN(右连接): 与 LEFT JOIN 相反,用于获取右表所有记录,即使左表没有对应匹配的记录

右联结

SELECT Customers.cust_id, Orders.order_num 
FROM Customers RIGHT OUTER JOIN Orders 
  ON Customers.cust_id = Orders.cust_id;

+---------+-----------+
| cust_id | order_num |
+---------+-----------+
|   10001 |     20005 |
|   10001 |     20009 |
|   10003 |     20006 |
|   10004 |     20007 |
|   10005 |     20008 |
+---------+-----------+
5 rows in set (0.00 sec)

 还存在另一种外联结,就是全外联结(full outer join) ,它检索两个表中的所有行并关联那些可以关联的行。与左外联结或右外联结包含一个表不关联的行不同,全外联结包含两个表的不关联的行。全外联结的语法如下:

SELECT Customers.cust_id, Orders.order_num 
FROM Orders FULL OUTER JOIN Customers 
  ON Orders.cust_id = Customers.cust_id;

-- 但是 mysql 报错了
mysql> SELECT Customers.cust_id, Orders.order_num FROM Orders FULL OUTER JOIN Customers ON Orders.cust_id = Customers.cust_id;
ERROR 1064 (42000): You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near 'OUTER JOIN Customers ON Orders.cust_id = Customers.cust_id' at line 1
mysql>

 注意: FULL OUTER JOIN 的支持
Access、MariaDB、MySQL、Open Office Base和 SQLite不支持 FULL OUTER JOIN 语法。

使用带聚集函数的联结

 看个例子,要检索所有顾客及每个顾客所下的订单数,下面的代码使用 COUNT() 函数完成此工作:

SELECT     Customers.cust_id,
        COUNT(Orders.order_num) AS num_ord
FROM Customers INNER JOIN Orders
  ON Customers.cust_id = Orders.cust_id
GROUP BY Customers.cust_id;

+---------+---------+
| cust_id | num_ord |
+---------+---------+
|   10001 |       2 |
|   10003 |       1 |
|   10004 |       1 |
|   10005 |       1 |
+---------+---------+
4 rows in set (0.04 sec)
SELECT Customers.cust_id, 
       COUNT(Orders.order_num) AS num_ord 
FROM Customers LEFT OUTER JOIN Orders 
  ON Customers.cust_id = Orders.cust_id 
GROUP BY Customers.cust_id;

+---------+---------+
| cust_id | num_ord |
+---------+---------+
|   10001 |       2 |
|   10002 |       0 |
|   10003 |       1 |
|   10004 |       1 |
|   10005 |       1 |
+---------+---------+
5 rows in set (0.00 sec)

有必要汇总一下联结及其使用的要点。

  • 注意所使用的联结类型。 一般我们使用内联结, 但使用外联结也有效。
  • 关于确切的联结语法,应该查看具体的文档,看相应的 DBMS支持何种语法(大多数 DBMS使用这两课中描述的某种语法) 。
  • 保证使用正确的联结条件(不管采用哪种语法) ,否则会返回不正确的数据。
  • 应该总是提供联结条件,否则会得出笛卡儿积。
  • 在一个联结中可以包含多个表,甚至可以对每个联结采用不同的联结类型。虽然这样做是合法的,一般也很有用,但应该在一起测试它们前分别测试每个联结。这会使故障排除更为简单。

Lesson14 组合查询

本课讲述如何利用 UNION 操作符将多条 SELECT 语句组合成一个结果集。
多数 SQL查询只包含从一个或多个表中返回数据的单条 SELECT 语句。但是,SQL也允许执行多个查询(多条 SELECT 语句) ,并将结果作为一个查询结果集返回。这些组合查询通常称为并(union)或复合查询(compound query) 。
主要有两种情况需要使用组合查询:

  • 在一个查询中从不同的表返回结构数据;
  • 对一个表执行多个查询,按一个查询返回数据。
SELECT cust_name, cust_contact, cust_email 
FROM Customers 
WHERE cust_state IN ('IL','IN','MI') 
UNION 
SELECT cust_name, cust_contact, cust_email 
FROM Customers 
WHERE cust_name = 'Fun4All';

-- 相当于
SELECT cust_name, cust_contact, cust_email 
FROM Customers 
WHERE cust_state IN ('IL','IN','MI') 
  OR cust_name = 'Fun4All';

 UNION 从查询结果集中自动去除了重复的行;换句话说,它的行为与一条 SELECT 语句中使用多个 WHERE 子句条件一样。
这是 UNION 的默认行为,如果愿意也可以改变它。事实上,如果想返回所有的匹配行,可使用 UNION ALL 而不是 UNION 。

SELECT 语句的输出用 ORDER BY 子句排序。在用 UNION 组合查询时,只能使用一条 ORDER BY 子句,它必须位于最后一条 SELECT 语句之后。用它来排序所有 SELECT 语句返回的所有结果。

Lesson18 使用视图

视图是虚拟的表。与包含数据的表不一样,视图只包含使用时动态检索数据的查询。

为什么使用视图
下面是视图的一些常见应用。

  • 重用 SQL语句
  • 简化复杂的 SQL操作。在编写查询后,可以方便地重用它而不必知道其基本查询细节。
  • 使用表的一部分而不是整个表。
  • 保护数据。可以授予用户访问表的特定部分的权限,而不是整个表的访问权限。
  • 更改数据格式和表示。视图可返回与底层表的表示和格式不同的数据。

创建视图

CREATE VIEW ProductCustomers AS 
	SELECT cust_name, cust_contact, prod_id 
	FROM Customers, Orders, OrderItems 
	WHERE Customers.cust_id = Orders.cust_id 
		AND OrderItems.order_num = Orders.order_num;

 删除视图: DROP VIEW viewname; 。覆盖(或更新)视图,必须先删除它,然后再重新创建。

这条语句创建一个名为 ProductCustomers 的视图,它联结三个表,返回已订购了任意产品的所有顾客的列表。如果执行 SELECT * FROM ProductCustomers ,将列出订购了任意产品的顾客。
检索订购了产品 TNT2 的顾客,可如下进行:
SELECT cust_name, cust_contact FROM ProductCustomers WHERE prod_id = 'TNT2';

简化了sql,重用了sql。

又例如,有一个sql

SELECT RTRIM(vend_name) + ' (' + RTRIM(vend_country) + ')'
	AS vend_title
FROM Vendors
ORDER BY vend_name;
# 或者
SELECT RTRIM(vend_name) || ' (' || RTRIM(vend_country) || ')'
	AS vend_title
FROM Vendors
ORDER BY vend_name;
#Mysql 使用 CONCAT 连接字符串
SELECT CONCAT(RTRIM(vend_name) ,' (', RTRIM(vend_country),')') 
	AS vend_title 
FROM Vendors 
ORDER BY vend_name;

现在, 假设经常需要这个格式的结果。 我们不必在每次需要时执行这种拼接,
而是创建一个视图,使用它即可。把此语句转换为视图,可按如下进行:

CREATE VIEW VendorLocations AS 
	SELECT RTRIM(vend_name) + ' (' + RTRIM(vend_country) + ')'
		AS vend_title
	FROM Vendors;

# 或者
CREATE VIEW VendorLocations AS 
	SELECT RTRIM(vend_name) || ' (' || RTRIM(vend_country) || ')'
		AS vend_title
	FROM Vendors;

#Mysql 使用 CONCAT 连接字符串
CREATE VIEW VendorLocations AS 
	SELECT CONCAT(RTRIM(vend_name) ,' (', RTRIM(vend_country),')') 
		AS vend_title 
	FROM Vendors;

 

用视图过滤不想要的数据

视图对于应用普通的 WHERE 子句也很有用。例如,可以定义CustomerEMailList 视图,过滤没有电子邮件地址的顾客。为此,可使用下面的语句:

CREATE VIEW CustomerEMailList AS
SELECT cust_id, cust_name, cust_email
FROM Customers
WHERE cust_email IS NOT NULL;

使用视图与计算字段

在简化计算字段的使用上,视图也特别有用。下面是第 7 课中介绍的一条 SELECT 语句,它检索某个订单中的物品,计算每种物品的总价格:

SELECT prod_id,
	quantity,
	item_price,
	quantity*item_price AS expanded_price
FROM OrderItems
WHERE order_num = 20008;

 要将其转换为一个视图,如下进行:

CREATE VIEW OrderItemsExpanded AS
SELECT order_num, prod_id,
	quantity,
	item_price,
	quantity*item_price AS expanded_price
FROM OrderItems

 检索订单 20008 的详细内容(上面的输出) ,如下进行

SELECT * FROM OrderItemsExpanded WHERE order_num = 20008;


小结

视图为虚拟的表。它们包含的不是数据而是根据需要检索数据的查询。
视图提供了一种封装 SELECT 语句的层次,可用来简化数据处理,重新格式化或保护基础数据。

 Lesson19 使用存储过程

迄今为止,我们使用的大多数 SQL语句都是针对一个或多个表的单条语句。并非所有操作都这么简单,经常会有一些复杂的操作需要多条语句才能完成,这就需要存储过程了。

存储过程

简单来说,存储过程就是为以后使用而保存的一条或多条 SQL语句。 可将其视为批处理文件, 虽然它们的作用不仅限于批处理。

 使用存储过程有三个主要的好处,即简单、安全、高性能。显然,它们都很重要。不过,不同 DBMS中的存储过程语法有所不同。事实上,编写真正的可移植存储过程几乎是不可能的。不过,存储过程的自我调用(名字以及数据如何传递) 可以相对保持可移植。 因此, 如果需要移植到别的DBMS,至少客户端应用代码不需要变动。

创建存储过程

我们来看一个简单的存储过程例子,它对邮件发送清单中具有邮件地址的顾客进行计数。
下面是该过程的 Oracle版本:

CREATE PROCEDURE MailingListCount (
    ListCount OUT INTEGER
)
IS
v_rows INTEGER;
BEGIN
	SELECT COUNT(*) INTO v_rows
	FROM Customers
	WHERE NOT cust_email IS NULL;
	ListCount := v_rows;
END;

 这个存储过程有一个名为 ListCount 的参数。 此参数从存储过程返回一个值而不是传递一个值给存储过程。关键字 OUT 用来指示这种行为。
Oracle支持 IN (传递值给存储过程) 、 OUT (从存储过程返回值, 如这里) 、INOUT (既传递值给存储过程也从存储过程传回值)类型的参数。存储过程的代码括在 BEGIN 和 END 语句中,这里执行一条简单的 SELECT 语句,它检索具有邮件地址的顾客。然后用检索出的行数设置 ListCount(要传递的输出参数) 。

 调用 Oracle例子可以像下面这样:

var ReturnValue NUMBER
EXEC MailingListCount(:ReturnValue);
SELECT ReturnValue;

下面是该过程的 SQL Server 版本。

CREATE PROCEDURE MailingListCount
AS
DECLARE @cnt INTEGER
SELECT @cnt = COUNT(*)
FROM Customers
WHERE NOT cust_email IS NULL;
RETURN @cnt;

 此存储过程没有参数。调用程序检索 SQL Server的返回代码提供的值。其中用 DECLARE 语句声明了一个名为 @cnt 的局部变量 (SQL Server中所有局部变量名都以 @ 起头) ;然后在 SELECT 语句中使用这个变量,让它包含 COUNT() 函数返回的值;最后,用 RETURN @cnt 语句将计数返回给调用程序。


调用 SQL Server例子可以像下面这样:

DECLARE @ReturnValue INT
EXECUTE @ReturnValue=MailingListCount;
SELECT @ReturnValue;

Lesson20 事务

Mysql中查看自动提交状态:

select @@autocommit;

 默认是1. 可以设置 set autocommit = 0; set autocommit = 1;

若是1,表示自动提交。每一个操作,都是一个事务,自动提交。若要开启事务,显示的写 start transaction; 然后写操作语句,最后写 commit 或 rollback。
若是0,表示不自动提交。每个操作,都有写个 commit; 才能提交,若没写,系统默认rollback。

Mysql查看隔离级别:有四种,serializable(序列化)、repeatable read (可重复读)、read committed(提交后读)、read uncommitted(未提交读)

select @@tx_isolation;

事务:一组业务操作ABCD,要么全部成功,要么全部不成功。
特性:ACID

  • 原子性:整体
  • 一致性:完成
  • 隔离性:并发
  • 持久性:结果

隔离问题:

  • 脏读:一个事务读到另一个事务没有提交的数据
  • 不可重复读:一个事务读到另一个事务已提交的数据(update)
  • 虚读(幻读):一个事务读到另一个事务已提交的数据(insert)

隔离级别:

  • read uncommitted:读未提交。存在3个问题
  • read committed:读已提交。解决脏读,存在2个问题
  • repeatable read:可重复读。解决:脏读、不可重复读,存在1个问题。
  • serializable :串行化。都解决,单事务。(速度慢)

附录:

语法

ALTER TABLE 用来更新已存在表的结构。
ALTER TABLE tablename
(
	ADD|DROP column datatype [NULL|NOT NULL] [CONSTRAINTS],
	ADD|DROP column datatype [NULL|NOT NULL] [CONSTRAINTS],
	...
);

CREATE INDEX 用于在一个或多个列上创建索引
CREATE INDEX indexname
ON tablename (column, ...);

CREATE PROCEDURE 用于创建存储过程。
CREATE PROCEDURE procedurename [parameters] [options]
AS
SQL statement;

CREATE TABLE 用于创建新数据库表。
CREATE TABLE tablename
(
column datatype [NULL|NOT NULL] [CONSTRAINTS],
column datatype [NULL|NOT NULL] [CONSTRAINTS],
...
);

CREATE VIEW 用来创建一个或多个表上的新视图。
CREATE VIEW viewname AS
SELECT columns, ...
FROM tables, ...
[WHERE ...]
[GROUP BY ...]
[HAVING ...];

DELETE 从表中删除一行或多行。
DELETE FROM tablename
[WHERE ...];

DROP 永久地删除数据库对象 (表、 视图、 索引等) 。 
DROP INDEX|PROCEDURE|TABLE|VIEW
indexname|procedurename|tablename|viewname;

INSERT 为表添加一行。
INSERT INTO tablename [(columns, ...)]
VALUES(values, ...);

INSERT SELECT 将 SELECT 的结果插入到一个表
INSERT INTO tablename [(columns, ...)]
SELECT columns, ... FROM tablename, ...
[WHERE ...];

SELECT 用于从一个或多个表(视图)中检索数据。
SELECT columnname, ...
FROM tablename, ...
[WHERE ...]
[UNION ...]
[GROUP BY ...]
[HAVING ...]
[ORDER BY ...];

UPDATE 更新表中的一行或多行。
UPDATE tablename
SET columname = value, ...
[WHERE ...];




----------

常记溪亭日暮,沉醉不知归路。兴尽晚回舟,误入藕花深处。争渡,争渡,惊起一滩鸥鹭。

昨夜雨疏风骤,浓睡不消残酒。试问卷帘人,却道海棠依旧。知否?知否?应是绿肥红瘦。
原文地址:https://www.cnblogs.com/htj10/p/15221779.html