MySQL总结(5)

ぃ、小莉子 提交于 2019-12-04 03:38:23

视图

SELECT cust_name,cust_contact
FROM customers,orders,orderitems
WHERE customers.cust_id=orders.cust_id
    AND orderitems.order_num=orders.order_num
    AND prod_id='TNT2';

假如可以把整个查询包装成一个名为 productcustomers 的虚拟表

SELECT cust_name,cust_contact
FROM productcustomers #this is a 视图
WHERE prod_id='TNT2'

productcustomers 是一个视图,作为视图,它不包含表中应该有的任何列或数据,它包含的是一个SQL查询(与上面用以正确联结表的相同的查询)。

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

😘 与表一样,视图必须唯一命名(不能给视图取与别的视图或表相
同的名字)。
😘 对于可以创建的视图数目没有限制。
😘 为了创建视图,必须具有足够的访问权限。这些限制通常由数据
库管理人员授予。
😘 视图可以嵌套,即可以利用从其他视图中检索数据的查询来构造
一个视图。
😘 ORDER BY 可以用在视图中,但如果从该视图检索数据 SELECT 中也
含有 ORDER BY ,那么该视图中的 ORDER BY 将被覆盖。
😘 视图不能索引,也不能有关联的触发器或默认值。
😘 视图可以和表一起使用。例如,编写一条联结表和视图的 SELECT
语句。

使用视图

❤ 视图用 CREATE VIEW 语句来创建。
❤ 使用 SHOW CREATE VIEW viewname ;来查看创建视图的语句。
❤ 用 DROP 删除视图,其语法为 DROP VIEW viewname;。
❤ 更新视图时,可以先用DROP再用CREATE,也可以直接用CREATE OR
REPLACE VIEW。如果要更新的视图不存在,则第 2 条更新语句会创建一个视图;如果要更新的视图存在,则第 2 条更新语句会替换原有视图。

CREATE VIEW productcustomers AS
SELECT cust_name, cust_contact, prod_id
FROM customers,orders,orderitems
WHERE customer.cust_id = orders.cust_id
    AND orderitems.order_num = orders.order_num;

WHERE 子句与 WHERE 子句 如果从视图检索数据时使用了一条
WHERE 子句,则两组子句(一组在视图中,另一组是传递给视图的)将自动组合。

更新视图

INSERT UPDATE DELETE

如果视图具有以下操作是不能被更新的 (更新其实更新的是原来的表)
 分组(使用 GROUP BY 和 HAVING );
 联结;
 子查询;
 并;
 聚集函数( Min() 、 Count() 、 Sum() 等);
 DISTINCT;
 导出(计算)列。

存储过程

0x01 执行存储过程

call CALL 接受存储过程的名字以及需要传递给它的任意参数。

CALL productpricing(@pricelow,
                   @pricehigh,
                   @priceaverage);

其中,执行名为 productpricing 的存储过程,它计算并返回产品的最低、最高和平均价格。

0x02 创建存储过程 procedure

CREATE PROCEDURE pro()
BEGIN 
    SELECT avg(prod_price) AS priceaverage
    FROM products;
END

DELIMITER

DELIMITER // 告诉命令行实用程序使用 // 作为新的语句结束分隔符,可以看到标志存储过程结束的 END 定义为 END// 而不是 END;

主要为了防止命令行模式混淆 出现语法错误

DELIMITER //
DELIMITER ;

使用这个存储过程

CALL pro();

删除存储过程

DROP PROCEDURE IF EXISTS pro;

if exists 指定过程不存在时删除

使用参数

CREATE PROCEDURE productpricing(
    OUT p1 DECIMAL(8,2),
    OUT ph DECIMAL(8,2),
    OUT pa DECIMAL(8,2)
)
BEGIN
    SELECT min(prod_price)
    INTO p1
    FROM products;
    SELECT max(prod_price)
    INTO ph
    FROM products;
    SELECT avg(prod_price)
    INTO pa
    FROM products;
END;
# 此存储过程接受3个参数: pl 存储产品最低价格, ph 存储产品最高价格, pa 存储产品平均价格。每个参数必须具有指定的类型,这里使用十进制值。关键字 OUT 指出相应的参数用来从存储过程传出一个值(返回给调用者)。MySQL支持 IN (传递给存储过程)、 OUT (从存储过程传出,如这里所用)和 INOUT (对存储过程传入和传出)类型的参数。存储过程的代码位于 BEGIN 和 END 语句内,如前所见,它们是一系列SELECT 语句,用来检索值,然后保存到相应的变量(通过指定 INTO 关键字)。
# 调用
CALL productpricing(@pricelow,
                   @pricehigh,
                   @priceaverage)
# mysql 变量必须是@ 开始
# 显示变量
SELECT @priceaverage,@pricelow,@pricehigh
CREATE PROCEDURE ordertotal(
    IN onumber INT,
    OUT ototal DECIMAL(8,2) # 十进制的(一共多少数,小数位数)
)
BEGIN 
    SELECT sum(item_price*quantity)
    FROM orderitems
    WHERE order_num = onumber
    INTO ototal;
END;
-- 注释 DECLARE declare 声明
CALL ordertotal(20005,@total);

智能储存

image-20191101135704897

image-20191101135753359

IF 语句 这个例子给出了MySQL的 IF 语句的基本用法。 IF 语句还支持 ELSEIF 和 ELSE 子句(前者还使用 THEN 子句,后者不使用)。在以后章节中我们将会看到 IF 的其他用法(以及其他流控制语句)。

此存储过程有很大的变动。首先,增加了注释(前面放置 -- )。在存储过程复杂性增加时,这样做特别重要。添加了另外一个参数taxable ,它是一个布尔值(如果要增加税则为真,否则为假)。在存储过程体中,用 DECLARE 语句定义了两个局部变量。DECLARE 要求指定变量名和数据类型,它也支持可选的默认值(这个例子中的 taxrate 的默认被设置为 6% )。 SELECT 语句已经改变,因此其结果存储到 total (局部变量)而不是 ototal 。 IF 语句检查 taxable 是否为真,如果为真,则用另一 SELECT 语句增加营业税到局部变量 total 。最后,用另一 SELECT 语句将total (它增加或许不增加营业税)保存到 ototal 。

COMMENT 关键字 本例子中的存储过程在 CREATE PROCEDURE 语
句中包含了一个 COMMENT 值。它不是必需的,但如果给出,将
在 SHOW PROCEDURE STATUS 的结果中显示。

检查储存过程

SHOW CREATE PROCEDURE ordertotal;

游标

只能用于存储过程 不像多数DBMS,MySQL游标只能用于存储过程(和函数)。

🙃 在能够使用游标前,必须声明(定义)它。这个过程实际上没有检索数据,它只是定义要使用的 SELECT 语句。
🙃 一旦声明后,必须打开游标以供使用。这个过程用前面定义的SELECT 语句把数据实际检索出来。
🙃 对于填有数据的游标,根据需要取出(检索)各行。
🙃 在结束游标使用时,必须关闭游标。

游标需要declare 声明

CREATE PROCEDURE processorders()
BEGIN
    DECLARE ordernumbers CURSOR # procedure程序,过程 
    FOR
    SELECT order_num FROM orders;
END;    # cursor 游标

打开和关闭游标

OPEN ordernumbers;  -- OPEN CURSOR
CLOSE ordernumbers; -- CLOSE CURSER
-- 隐含关闭 如果你不明确关闭游标,MySQL将会在到达 END 语句时自动关闭它。

在一个游标被打开后,可以使用 FETCH 语句分别访问它的每一行。
FETCH 指定检索什么数据(所需的列),检索出来的数据存储在什么地方。它还向前移动游标中的内部行指针,使下一条 FETCH 语句检索下一行(不重复读取同一行)。

CREATE PROCEDURE processorders()
BEGIN 
    DECLARE o INT;
    DECLARE ordernumbers CURSOR
    FOR
    SELECT order_num FROM orders;
    OPEN ordernumbers;
    FETCH ordernumbers INTO o;-- fetch 取来,拿
    CLOSE ordernumbers;
END;

DECLARE 语句的次序 DECLARE 语句的发布存在特定的次序。用 DECLARE 语句定义的局部变量必须在定义任意游标或句柄之前定义,而句柄必须在游标之后定义。不遵守此顺序将产生错误消息。

CREATE PROCEDURE processorders()
BEGIN 
    DECLARE done BOOLEAN DEFAULT 0; -- 默认值为0
    DECLARE o INT;
    DECLARE ordernumbers CURSOR
    FOR
    SELECT order_num FROM orders;
    DECLARE CONTINUE HANDLER FOR 
    SQLSTATE '02000' SET done=1;
    OPEN ordernumbers;
    -- repeat 重复
    REPEAT
        FETCH ordernumbers INTO o;-- fetch 取来,拿
    UNTIL done END REPEAT;
    CLOSE ordernumbers;
END;
-- 与前一个例子一样,这个例子使用 FETCH 检索当前 order_num到声明的名为 o 的变量中。但与前一个例子不一样的是,这个例子中的 FETCH 是在 REPEAT 内,因此它反复执行直到 done 为真(由 UNTILdone END REPEAT; 规定)。为使它起作用,用一个 DEFAULT 0 (假,不结束)定义变量 done 。那么, done 怎样才能在结束时被设置为真呢?答案是用以下语句:DECLARE CONTINUE HANDLER FOR SQLSTATE '02000' SET done=1; 这条语句定义了一个 CONTINUE HANDLER ,它是在条件出现时被执行的代码。这里,它指出当 SQLSTATE '02000' 出现时, SET done=1。SQLSTATE'02000' 是一个未找到条件,当 REPEAT 由于没有更多的行供循环而不能继续时,出现这个条件。
CREATE PROCEDURE processorders()
BEGIN 
    DECLARE done BOOLEAN DEFAULT 0; -- 默认值为0
    DECLARE o INT;
    DECLARE t DECIMAL(8,2);
    DECLARE ordernumbers CURSOR
    FOR
    SELECT order_num FROM orders;
    DECLARE CONTINUE HANDLER FOR 
    SQLSTATE '02000' SET done=1;
    CREATE TABLE IF NOT EXISTS ordertotals
    (order_num INT,total DECIMAL(8,2));
    OPEN ordernumbers;
    -- repeat 重复
    REPEAT
        FETCH ordernumbers INTO o;-- fetch 取来,拿
        CALL ordertotal(o,1,t);
        INSERT IOTO ordertotals(order_num,total)
        VALUES(o,t);
    UNTIL done END REPEAT;
    CLOSE ordernumbers;
END;

使用触发器

 每当增加一个顾客到某个数据库表时,都检查其电话号码格式是
否正确,州的缩写是否为大写;
 每当订购一个产品时,都从库存数量中减去订购的数量;
 无论何时删除一行,都在某个存档表中保留一个副本。

 DELETE ;
 INSERT ;
 UPDATE 。

其他语句不支持触发器

创建触发器

保持每个数据库的触发器名唯一 在MySQL 5中,触发器名必须在每个表中唯一,但不是在每个数据库中唯一。这表示同一数据库中的两个表可具有相同名字的触发器。这在其他每个数据库触发器名必须唯一的DBMS中是不允许的,而且以后的MySQL版本很可能会使命名规则更为严格。因此,现在最好是在数据库范围内使用唯一的触发器名。

trigger 触发

CREATE TRIGGER newproduct AFTER INSERT ON products
-- 触发器在insert之后执行 
FOR EACH ROW SELECT 'Product added'
-- 对每一个插入行执行

触发器按每个表每个事件每次地定义,每个表每个事件每次只允许一个触发器。因此,每个表最多支持6个触发器(每条 INSERT 、 UPDATE和 DELETE 的之前和之后)。单一触发器不能与多个事件或多个表关联,所以,如果你需要一个对 INSERT 和 UPDATE 操作执行的触发器,则应该定义
两个触发器。

如果 BEFORE 触发器失败,则MySQL将不执行请求的操作。此外,如果 BEFORE 触发器或语句本身失败,MySQL将不执行 AFTER 触发器(如果有的话)。

删除触发器

DROP TRIGGER newproduct;

INSERT 触发器

 在 INSERT 触发器代码内,可引用一个名为 NEW 的虚拟表,访问被
插入的行;
 在 BEFORE INSERT 触发器中, NEW 中的值也可以被更新(允许更改
被插入的值);
 对于 AUTO_INCREMENT 列, NEW 在 INSERT 执行之前包含 0 ,在 INSERT执行之后包含新的自动生成值。

CREATE TRIGGER neworder AFTER INSERT ON orders
FOR EACH ROW SELECT NEW.order_num

此代码创建一个名为 neworder 的触发器,它按照 AFTER INSERT ON orders 执行。在插入一个新订单到 orders 表时,MySQL生成一个新订单号并保存到order_num 中。触发器从 NEW. order_num 取得这个值并返回它。此触发器必须按照 AFTER INSERT 执行,因为在 BEFORE INSERT 语句执行之前,新 order_num 还没有生成。对于 orders 的每次插入使用这个触发器将总是返回新的订单号。

DELECT 触发器

 在 DELETE 触发器代码内,你可以引用一个名为 OLD 的虚拟表,访
问被删除的行。
 OLD 中的值全都是只读的,不能更新。

create trigger delectorder before delete on orders
FOR each row
begin 
    insert into archive_orders(order_num,order_date,cust_id)
    values(OLD.order_num,OLD.order_date,OLD.cust_id);
end;

使用 BEGIN END 块的好处是触发器能容纳多条SQL语句(在 BEGIN END块 中一条挨着一条)。

UPDATA 触发器

 在 UPDATE 触发器代码中,你可以引用一个名为 OLD 的虚拟表访问以前( UPDATE 语句前)的值,引用一个名为 NEW 的虚拟表访问新更新的值;
 在 BEFORE UPDATE 触发器中, NEW 中的值可能也被更新(允许更改将要用于 UPDATE 语句中的值);
 OLD 中的值全都是只读的,不能更新。

CREATE TRIGGER updataorder BEFORE UPDATE ON vendors
FOR EACH ROW SET NEW.vend_state = Upper(NEW.vend_state);

事务处理

并非所有引擎都支持事务处理 正如第21章所述,MySQL支持几种基本的数据库引擎。正如本章所述,并非所有引擎都支持明确的事务处理管理。 MyISAM 和 InnoDB 是两种最常使用的引擎。前者不支持明确的事务处理管理,而后者支持。这就是为什么本书中使用的样例表被创建来使用 InnoDB 而不是更经常使用的 MyISAM 的原因。如果你的应用中需要事务处理功能,则一定要使用正确的引擎类型。

事务处理是一种机制,用来管理必须成批执行的MySQL操作,以保证数据库不包含不完整的操作结果。利用事务处理,可以保证一组操作不会中途停止,它们或者作为整体执行,或者完全不执行(除非明确指示)。如果没有错误发生,整组语句提交给(写到)数据库表。如果发生错误,则进行回退(撤销)以恢复数据库到某个已知且安全的状态。

事务开始

START TRANSACTION

使用ROLLBACK

START TRANSACTION
​```
ROLLBACK;

哪些语句可以回退? 事务处理用来管理 INSERT 、 UPDATE 和
DELETE 语句。你不能回退 SELECT 语句。(这样做也没有什么意义。)你不能回退 CREATE 或 DROP 操作。事务处理块中可以使用
这两条语句,但如果你执行回退,它们不会被撤销。

使用COMMIT

START TRANSACTION;
DELETE FROM orderitems WHERE order_num = 20010;
DELETE FROM orders WHERE order_num = 20010;
COMMIT;
-- 在这个例子中,从系统中完全删除订单 20010 。因为涉及更新两个数据库表 orders 和 orderItems ,所以使用事务处理块来保证订单不被部分删除。最后的 COMMIT 语句仅在不出错时写出更改。如果第一条 DELETE 起作用,但第二条失败,则 DELETE 不会提交(实际上,它是被自动撤销的)。

隐含事务关闭 当 COMMIT 或 ROLLBACK 语句执行后,事务会自动关闭(将来的更改会隐含提交)。

每个保留点都取标识它的唯一名字,以便在回退时,MySQL知道要回退到何处。为了回退到本例给出的保留点,可如下进行:

savepoint delete1;

​```

ROLLBACK TO delete1;

autocommit 标志决定是否自动提交更改,不管有没有 COMMIT语句。设置 autocommit 为 0 (假)指示MySQL不自动提交更改(直到 autocommit 被设置为真为止)。

SET autocommit = 0;
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!