• 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 ...];
    




    ----------

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

    昨夜雨疏风骤,浓睡不消残酒。试问卷帘人,却道海棠依旧。知否?知否?应是绿肥红瘦。
  • 相关阅读:
    candy——动态规划
    HTTP协议(转)
    linked-list-cycle-ii——链表,找出开始循环节点
    linked-list-cycle——链表、判断是否循环链表、快慢指针
    转: utf16编码格式(unicode与utf16联系)
    【转】Nginx模块开发入门
    【转】依赖注入那些事儿
    转: OpenResty最佳实践
    转:linux的源码查看, c++语法 查看网站
    【转】所需即所获:像 IDE 一样使用 vim
  • 原文地址:https://www.cnblogs.com/htj10/p/15221779.html
Copyright © 2020-2023  润新知