MySQL视图、存储、游标、触发器

文章目录

  • 一、使用视图
    • 1. 视图
    • 2. 使用视图
  • 二、使用存储
    • 1. 存储过程
    • 2. 为什么要使用存储过程
    • 3. 使用存储过程
      • 3.1 执行存储过程
      • 3.2 创建存储过程
      • 3.3 删除存储过程
      • 3.4 使用参数
      • 3.5 建立智能存储过程
      • 3.6 检查存储过程
  • 三、使用游标
    • 1. 游标
    • 2. 使用游标
    • 2.1 创建游标
    • 2.2 打开和关闭游标
    • 2.3 使用游标数据
  • 四、使用触发器
    • 1. 触发器
    • 2. 创建触发器
      • Triggers can not be created on system tables
    • 3. 删除触发器
    • 4. 使用触发器
      • 4.1 INSERT触发器
      • 4.2 DELETE触发器
      • 4.3 UPDATE触发器
      • 4.4 触发器额外重点

一、使用视图

1. 视图

视图是虚拟的表。与包含数据的表不一样,视图只包含使用时动态检索数据的查询。
视图提供了一种MySQL的SELECT语句层次的封装,可用来简化数据处理以及重新格式化基础数据或保护基础数据。

  1. 为什么使用视图
  • 重用SQL语句
  • 简化复杂的SQL操作。在编写查询后,可以方便地重用它而不必知道它的基本查询细节
  • 使用表的组成部分而不是整个表
  • 保护数据。可以给用户授予表的特定部分的访问权限而不是整个表的访问权限
  • 更改数据格式和表示。视图可返回与底层表的表示和格式不同的数据
  1. 视图的规则和限制
  • 与表一样,视图必须唯一命名(不能给视图取与别的视图或表相同的名字)
  • 对于可以创建的视图数目没有限制
  • 为了创建视图,必须具有足够的访问权限。这些限制通常由数据库管理人员授予
  • 视图可以嵌套,即可以利用从其他视图中检索数据的查询来构造一个视图
  • ORDER BY可以用在视图中,但如果从该视图检索数据SELECT中也含有ORDER BY,那么该视图中的ORDER BY将被覆盖
  • 视图不能索引,也不能有关联的触发器或默认值
  • 视图可以和表一起使用。例如,编写一条联结表和视图的SELECT语句

2. 使用视图

  • 视图用CREATE VIEW语句来创建
  • 使用SHOW CREATE VIEW viewname;来查看创建视图的语句
  • 用DROP删除视图,其语法为DROP VIEW viewname
  • 更新视图时,可以先用DROP再用CREATE,也可以直接用CREATE OR REPLACE VIEW。如果要更新的视图不存在,则第2条更新语句会创建一个视图;如果要更新的视图存在,则第2条更新语句会替换原有视图
  1. 利用视图简化复杂的联结
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

创建一个名为productcustomers的视图,如果执行
SELECT * FROM productcustomers,将列出订购了任意产品的客户。

SELECT cust_name, cust_contact 
FROM productcustomers 
WHERE prod_id = 'TNT2'

MySQL视图、存储、游标、触发器_第1张图片

视图极大地简化了复杂SQL语句的使用。利用视图,可一次性编写基础的SQL,然后根据需要多次使用。

  1. 用视图重新格式化检索出的数据
    视图的另一常见用途是重新格式化检索出的数据。
CREATE VIEW vendeorlocations AS
SELECT CONCAT(RTRIM(vend_name),'(',RTRIM(vend_country),')')
			 AS vend_title
FROM vendors
ORDER BY vend_name

MySQL视图、存储、游标、触发器_第2张图片

  1. 用视图过滤不想要的数据
    视图对于应用普通的WHERE子句也很有用。
CREATE VIEW customeremaillist AS
SELECT cust_id, cust_name, cust_email
FROM customers
WHERE cust_email IS NOT NULL

MySQL视图、存储、游标、触发器_第3张图片

  1. 使用视图与计算字段
    视图对于简化计算字段的使用特别有用。
CREATE VIEW orderitemsexpanded AS
SELECT order_num,
			 prod_id,
			 quantity,
			 item_price,
			 quantity*item_price AS expanded_price
FROM orderitems;

MySQL视图、存储、游标、触发器_第4张图片

  1. 更新视图
    通常,视图是可更新的(即,可以对它们使用INSERT、UPDATE和DELETE)。更新一个视图将更新其基表(可以回忆一下,视图本身没数据)。如果你对视图增加或删除行,实际上是对其基表增加或删除行。
    但是,并非所有视图都是可更新的。基本上可以说,如果MySQL不能正确地确定被更新的基数据,则不允许更新(包括插入和删除)。这实际上意味着,如果视图定义中有以下操作,则不能进行视图的更新:
  • 分组(使用GROUP BY 和 HAVING)
  • 联结
  • 子查询
  • 聚集函数(Min()、Count()、Sum()等)
  • DISTINCT
  • 导出(计算)列

二、使用存储

1. 存储过程

迄今为止,使用的大多数SQL语句都是针对一个或多个表的单条语句。并非所有操作都这么简单,经常会有一个完整的操作需要多条语句才能完成。
存储过程简单来说,就是为以后的使用而保存的一条或多条MySQL语句的集合。可将其视为批文件,虽然它们的作用不仅限于批处理。

2. 为什么要使用存储过程

使用存储的好处

  • 通过把处理封装在容易使用的单元中,简化复杂的操作。
  • 由于不要求反复建立一系列处理步骤,这保证了数据的完整性。如果所有开发人员和应用程序都使用同一(试验和测试)存储过程,则所使用的代码都是相同的。

这一点的延伸就是防止错误。需要执行的步骤越多,出错的可能性就越大。防止错误保证了数据的一致性。

  • 简化对变动的管理。如果表名、列名或业务逻辑(或别的内容)有变化,只需要更改存储过程的代码。使用它的人员甚至不需要知道这些变化。

这一点的延伸就是安全性。通过存储过程限制对基础数据的访问减少了数据讹误(无意识的或别的原因所导致的数据讹误)的机会。

  • 提高性能。因为使用存储过程比使用单独的SQL语句要快。
  • 存在一些只能用在单个请求中的MySQL元素和特性,存储过程可以使用它们来编写功能更强更灵活的代码。

换句话说,使用存储过程有3个主要的好处,即简单、安全、高性能。

使用存储的弊端

  • 一般来说,存储过程的编写比基本SQL语句复杂,编写存储过程
    需要更高的技能,更丰富的经验。
  • 你可能没有创建存储过程的安全访问权限。许多数据库管理员限制存储过程的创建权限,允许用户使用存储过程,但不允许他们创建存储过程。

3. 使用存储过程

3.1 执行存储过程

MySQL称存储过程的执行为调用,因此MySQL执行存储过程的语句
为CALL。CALL接受存储过程的名字以及需要传递给它的任意参数。

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

3.2 创建存储过程

一个返回产品平均价格的存储过程。

CREATE PROCEDURE productpricing()
BEGIN 
		SELECT AVG(prod_price) AS priceaverage 
		FROM products;
END;

此存储过程名为 productpricing,用CREATE PROCEDURE productpricing()语句定义。如果存储过程接受参数,它们将在()中列举出来。此存储过程没有参数,但后跟的()仍然需要。BEGIN和END语句用来限定存储过程体,过程体本身仅是一个简单的SELECT语句.
使用:

CALL productpricing();

MySQL视图、存储、游标、触发器_第5张图片

3.3 删除存储过程

存储过程在创建之后,被保存在服务器上以供使用,直至被删除。删除命令从服务器中删除存储过程。

DROP PROCEDURE productpricing;

仅当存在时删除 如果指定的过程不存在,则DROP PROCEDURE将产生一个错误。当过程存在想删除它时(如果过程不存在也不产生错误)可使用DROP PROCEDURE IF EXISTS。

3.4 使用参数

一般,存储过程并不显示结果,而是把结果返回给你指定的变量。

变量:(variable)内存中一个特定的位置,用来临时存储数据。

CREATE PROCEDURE productpricing(
	OUT pl DECIMAL(8,2),
	OUT ph DECIMAL(8,2),
	OUT pa DECIMAL(8,2)
)
BEGIN 
	SELECT MIN(prod_price)
	INTO pl
	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关键字)

为调用此修改过的存储过程,必须指定3个变量名:

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

在调用时,这条语句并不显示任何数据。为了获得3个值,可使用以下语句:

SELECT @pricehigh,@pricelow,@priceaverage;

MySQL视图、存储、游标、触发器_第6张图片

3.5 建立智能存储过程

-- NAME: ordertotal 
-- Parameters: onumber = order number 
--             taxable = 0 if not taxable. 1 if taxable 
--             ototal  = order total variable 

CREATE PROCEDURE ordertotal(
	IN onumber INT,
	IN taxable BOOLEAN,
	OUT ototal DECIMAL(8,2)
) COMMENT 'Obtain order total, ooptionally adding tax'
BEGIN
	-- Declare variable for total 
	DECLARE total DECIMAL(8,2);
	-- Declare tax percentage
	DECLARE taxrate INT DEFAULT 6;
	
	-- Get the order total 
	SELECT SUM(item_price*quantity)
	FROM orderitems
	WHERE order_num = onumber 
	INTO total;
	
	-- Is this taxable?
	IF taxable THEN
		-- Yes, so add taxrate to the total 
		SELECT total+(total/100*taxrate) INTO total;
	END IF;
	
	-- And finally, save to out variable 
	SELECT total INTO ototal;
END;

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

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

使用:

CALL ordertotal(20005, 0, @total);
SELECT @total;

MySQL视图、存储、游标、触发器_第7张图片

3.6 检查存储过程

为显示用来创建一个存储过程的CREATE语句,使用SHOW CREATE PROCEDURE语句:

SHOW CREATE PROCEDURE ordertotal;

为了获得包括何时、由谁创建等详细信息的存储过程列表,使用SHOW PROCEDURE STATUS。

三、使用游标

1. 游标

有时,需要在检索出来的行中前进或后退一行或多行。这就是使用游标的原因。游标(cursor)是一个存储在MySQL服务器上的数据库查询,它不是一条SELECT语句,而是被该语句检索出来的结果集。在存储了游标之后,应用程序可以根据需要滚动或浏览其中的数据。
游标主要用于交互式应用,其中用户需要滚动屏幕上的数据,并对数据进行浏览或做出更改。

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

2. 使用游标

使用游标几个步骤:

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

2.1 创建游标

游标用DECLARE语句创建。DECLARE命名游标,并定义相应的SELECT语句,根据需要带WHERE和其他子句。

CREATE PROCEDURE processorders()
BEGIN 
	DECLARE ordernumbers CURSOR
	FOR
	SELECT order_num FROM orders;
END;

2.2 打开和关闭游标

游标用OPEN CURSOR语句来打开:

OPEN ordernumbers;

关闭游标

CLOSE ordernumbers;

修改版本

CREATE PROCEDURE processorders()
BEGIN 
	-- Declare the cursor
	DECLARE ordernumbers CURSOR
	FOR
	SELECT order_num FROM orders;
	
	-- Open the cursor
	OPEN ordernumbers;
	
	-- Close the cursor 
	CLOSE ordernumbers;
END;

2.3 使用游标数据

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

CREATE PROCEDURE processorders()
BEGIN
	-- Declare local variables 
	DECLARE o INT;
	
	-- Declare the cursor 
	DECLARE ordernumbers CURSOR
	FOR
	SELECT order_num FROM orders;
	
	-- open the cursor 
	OPEN ordernumbers;
	
	-- Get order number 
	FETCH ordernumbers INTO o;
	
	-- close the cursor 
	CLOSE ordernumbers;
END;

循环检索数据,从第一行到最后一行:

CREATE PROCEDURE processorders()
BEGIN
	-- Declare local variables 
	DECLARE done BOOLEAN DEFAULT 0;
	DECLARE o INT;
	
	-- Declare the cursor 
	DECLARE ordernumbers CURSOR
	FOR
	SELECT order_num FROM orders;
	-- Declasre continue handler 
	DECLARE CONTINUE HANDLER FOR SQLSTATE '02000' SET done = 1;
	
	-- open the cursor 
	OPEN ordernumbers;
	
	-- LOOP through all rows 
	REPEAT
	
		-- Get order number 
		FETCH ordernumbers INTO o;
		
	-- End of loop
	UNTIL done END REPEAT;
	
	-- close the cursor 
	CLOSE ordernumbers;
END;

游标存储过程样例的更进一步修改的版本,这次对取出的数据进行某种实际的处理:

CREATE PROCEDURE processorders()
BEGIN
	-- Declare local variables 
	DECLARE done BOOLEAN DEFAULT 0;
	DECLARE o INT;
	DECLARE t DECIMAL(8,2);
	
	-- Declare the cursor 
	DECLARE ordernumbers CURSOR
	FOR
	SELECT order_num FROM orders;
	-- Declasre continue handler 
	DECLARE CONTINUE HANDLER FOR SQLSTATE '02000' SET done = 1;
	
	-- create a table to store the results 
	CREATE TABLE IF NOT EXISTS ordertotals(order_num INT, total DECIMAL(8,2));
	
	-- open the cursor 
	OPEN ordernumbers;
	
	-- LOOP through all rows 
	REPEAT
	
		-- Get order number 
		FETCH ordernumbers INTO o;
		
		-- Get the total for this order 
		CALL ordertotal(o, 1, t);
		
		-- Insert order and total into ordertotals 
		INSERT INTO ordertotals(order_num, total) VALUES(o, t);
		
	-- End of loop
	UNTIL done END REPEAT;
	
	-- close the cursor 
	CLOSE ordernumbers;
END;
SELECT * FROM ordertotals;

MySQL视图、存储、游标、触发器_第8张图片

四、使用触发器

1. 触发器

触发器是MySQL响应以下任意语句而自动执行的一条MySQL语句(或位于BEGIN和END语句之间的一组语句):
DELETE、INSERET、UPDATE
其他MySQL语句不支持触发器。

2. 创建触发器

在创建触发器时,需要给出4条信息:

  • 唯一的触发器名
  • 触发器关联的表
  • 触发器应该响应的活动(DELETE、INSERET、UPDATE)
  • 触发器何时执行(处理之前或之后)

触发器用CREATE TRIGGER语句创建。

CREATE TRIGGER newproduct AFTER INSERT ON products
FOR EACH ROW SELECT 'Product added' INTO @add;

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

Triggers can not be created on system tables

“无法在系统表上创建触发器”
猜测原因:
就是当我们在Navicat中建立自己的连接并打开后你会发现,它自带了几个数据库,而且数据库中含有很多表,那些就是系统表,而且当你在那些自带的数据库中建表的时候,我们的系统也会将那些表认为是系统表。因此系统不允许你在那些表上建立TRIGGER。
解决:只需要重新建立一个自己的数据库然后再尝试创建TRIGGER即可。

3. 删除触发器

为了删除一个触发器,可使用DROP TRIGGER语句

DROP TRIGGER newproduct;

4. 使用触发器

4.1 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 INTO @num;

插入一行,触发这个触发器:

INSERT INTO orders(order_date, cust_id) VALUES (NOW(), 10001);

4.2 DELETE触发器

  • 在DELETE触发器代码内,你可以引用一个名为OLD的虚拟表,访问被删除的行;
  • OLD中的值全都是只读的,不能更新。
CREATE TRIGGER deleteorder 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;

4.3 UPDATE触发器

  • 在UPDATE触发器代码中,你可以引用一个名为OLD的虚拟表访问以前(UPDATE语句前)的值,引用一个名为NEW的虚拟表访问新更新的值;
  • 在BEFORE UPDATE触发器中,NEW中的值可能也被更新(允许更改将要用于UPDATE语句中的值);
  • OLD中的值全都是只读的,不能更新。
CREATE TRIGGER updatevendor BEFORE UPDATE ON vendors
FOR EACH ROW SET NEW.vend_state = upper(NEW.vend_state);

4.4 触发器额外重点

  • 创建触发器可能需要特殊的安全访问权限,但是,触发器的执行是自动的。如果INSERT、UPDATE或DELETE语句能够执行,则相关的触发器也能执行。
  • 应该用触发器来保证数据的一致性(大小写、格式等)。在触发器中执行这种类型的处理的优点是它总是进行这种处理,而且是透明地进行,与客户机应用无关。
  • 触发器的一种非常有意义的使用是创建审计跟踪。使用触发器,把更改(如果需要,甚至还有之前和之后的状态)记录到另一个表非常容易。
  • 遗憾的是,MySQL触发器中不支持CALL语句。这表示不能从触发器内调用存储过程。所需的存储过程代码需要复制到触发器内。

你可能感兴趣的:(#,数据库,mysql,数据库)