《SQL必知必会》笔记9-使用视图view、存储过程procedure

1 使用视图(VIEW)

1.1 视图

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

视图的常见应用:

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

创建视图之后,可以用与表基本相同的方式使用它们。可以对视图执行SELECT操作,过滤和排序数据,将视图联结到其他视图或表,甚至添加和更新数据。

创建和使用视图的一些常见规则和限制:

  • 与表一样,视图必须唯一命名,不能给视图取与别的视图或表相同的名字。
  • 对于可以创建的视图数目没有限制。
  • 创建视图,必须有足够的访问权限。
  • 视图可以嵌套,可以利用从其他视图中检索数据的查询来构造视图。
  • 视图不能索引,也不能有关联的触发器或默认值。
  • 有些DBMS把视图作为只读的查询,这表示可以从视图检索数据,但不能将数据写回底层表。

1.2 创建视图CREATE VIEW

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

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

1.2.1 利用视图简化复杂的联结

常见的视图应用是隐藏复杂的SQL。

(1)先创建ProductCustomers视图。

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;

(2)检索订购了产品RGAN01的顾客。

SELECT cust_name, cust_contact
FROM ProductCustomers
WHERE prod_id = 'RGAN01';

利用视图,可一次性编写基础的SQL,然后根据需要多次使用。


1.2.2 用视图重新格式化检索出的数据

将供应商Vendors表中的vend_name和vend_country合并输出显示。

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

SELECT * 
FROM VendorLocations;

1.2.3 用视图过滤不想要的数据

定义CustomerEmailList视图,过滤没有Email的顾客。

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

SELECT * 
FROM CustomerEmailList;

1.2.4 使用视图与计算字段

检索某个订单的产品,计算每种产品的总价格。

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

SELECT *
FROM OrderItemsExpanded
WHERE order_num = 20008;

1.2.5 更新视图

迄今为止的所有视图都是和SELECT语句使用的。然而,视图的数据能否更新?答案视情况而定。

通常,视图是可更新的,即,可以对它们使用INSERT、UPDATE和DELETE。更新一个视图将更新其基表,如果对视图增加或者删除行,实际上是对其基表增加或删除行。

但是,并非所有视图都是可更新的。基本上可以说,如果MySQL不能正确地确定被更新的基数据,则不允许更新(包括插入和删除)。

换句话说,如果视图定义中有以下操作,则不能进行视图的更新。

  • 分组(使用GROUP BY和HAVING)。
  • 联结。
  • 子查询。
  • 并。
  • 聚集函数(MIN()、COUNT()等)。
  • DISTINCT。
  • 导出(计算)列。

换句话说,本章的许多例子的视图都是不可更新的。这听上去好像是一个严重的限制,但实际上不是,因为视图主要用于数据检索。

一般,应该将视图用于检索(SELECT语句)而不用于更新(INSERT、UPDATE和DELETE)。


2 使用存储过程

2.1 存储过程

迄今为止,使用的大多数SQL语句都是针对一个或多个表的单条语句。并非所有操作都这么简单,经常会有一个完整的操作需要多条语句才能完成。

比如,考虑以下情况:

  1. 为了处理订单,需要核对以保证库存中有相应的物品。
  2. 如果库存有物品,这些物品需要预定以便不将它们再卖给别的人,并且要减少可用的物品数量以及反映正确的库存量。
  3. 库存中没有的物品需要订购,这需要与供应商进行某种交互。
  4. 关于哪些物品入库(并且可以立即发货)和哪些物品退订,需要通知相应的客户。

执行上述的例子,需要针对许多表的多条SQL语句。此外,需要执行的具体语句及其次序也不是固定的,它们可能会(和将)根据哪些物品在库存中,哪些不在而变化。

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


2.2 为什么要使用存储过程

使用存储过程的主要理由:

  1. 通过把处理封装在容易使用的单元中,简化复杂的操作。
  2. 由于不要求反复建立一系列处理步骤,这保证了数据的完整性。如果所有开发人员和应用程序都使用同一(试验和测试)存储过程,则使用的代码都是相同的。(这一点的延伸就是防止错误,需要执行的步骤越多,出错的可能性就越大。防止错误保证了数据的一致性。)
  3. 简化对变动的管理。如果表名、列名或业务逻辑有变化,只需要更改存储过程的代码,使用它的人员甚至不需要知道这些变化。(这一点的延伸就是安全性,通过存储过程限制对基础数据的访问减少了数据讹误的机会。)
  4. 提高性能。因为使用存储过程比使用简单的SQL语句要快。
  5. 存在一些只能用在单个请求中的MySQL元素和特性,存储过程可以使用它们来编写功能更强更灵活的代码。

使用存储过程的3个主要好处就是:简单、安全、高性能。

存在的缺陷:

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

2.3 使用存储过程

2.3.1 执行存储过程

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

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

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


2.3.2 创建存储过程

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

由于默认的MySQL语句分隔符为;,如果命令行实用程序要解释存储过程自身内的;字符,则它们最终不会成为存储过程的成分,这会导致存储过程中的SQL出现句法错误,正如上图所示。

解决办法就是使用更改命令行实用程序的语法分隔符,如下所示:

DELIMITER //          # 临时改成//分隔符

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

DELIMITER ;     # 改成默认;分隔符,不然以后写的SQL语句都要以//作为结尾,才不会报错。

调用存储过程:

CALL productpricing();

CALL productpricing();执行刚创建的存储过程并显示返回的结果。因为存储过程实际上是一种函数,所以存储过程名后需要有()符号,即使不传递参数也需要。

可以看作调用一个定义好的函数,一定要带()符号。


2.3.3 删除存储过程

删除刚创建的存储过程:

DROP PROCEDURE productpricing;

删除存储过程的时候,只需给出存储过程名即可,不需要带上()符号。

** 建议:如果指定的存储过程不存在的话,则DROP PROCEDURE将会产生错误,为防止出现错误,可使用DROP PROCEDURE IF EXISTS。**

DROP PROCEDURE IF EXISTS productpricing;

2.3.4 使用带参数的存储过程

productpricing只是一个简单的存储过程,它简单地显示SELECT语句的结果。一般,存储过程并不显示结果,而是把结果返回给你指定的变量。

注意:创建下面的存储过程的时候,记得先删除以前创建的productpricing。

DELIMITER //

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 //

DELIMITER ;

此存储过程接受3个参数:pl存储产品最低价格,ph存储产品最高价格,pa存储产品平均价格。

MySQL支持IN(传递给存储过程)、OUT(从存储过程传出)和INOUT(对存储过程传入和传出)类型的参数。

存储过程的代码位于BEGIN和END语句内,它们是一系列SELECT语句,用来检索值,然后保存到相应的变量(通过指定INTO关键字)。

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

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

所有MySQL变量都必须以@开始。

为了显示检索出的产品平均价格:

SELECT @priceaverage;

为获得3个值,可使用以下语句:

SELECT @pricehigh, @pricelow, @priceaverage;

创建另一个存储过程,这次使用IN和OUT参数。ordertotal接受订单号并返回该订单的合计:

DELIMITER //

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 //

DELIMITER ;

调用该存储过程时,必须给ordertotal传递两个参数:第一个参数为订单号,第二个参数为包含计算出来的合计的变量名。

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

CALL ordertotal(20009, @total);
SELECT @total;

2.3.5 建立智能存储过程

上述的所有存储过程基本上都是封装MySQL简单的SELECT语句,只有在存储过程内包含业务规则和智能处理时,它们的作用才能真正显现出来。

考虑如下场景,你需要获得与以前一样的订单合并,但需要对合计增加营业税,不过只针对某些顾客。那么,你需要做下面几件事情:

  • 获得合计。
  • 把营业税有条件地添加到合计。
  • 返回合计(带税或者不带税)。

存储过程的完整工作如下(记得先删除以前创建的ordertotal存储过程):

DELIMITER //

CREATE PROCEDURE ordertotal(
    IN    onumber   INT,
    IN    taxable   BOOLEAN,
    OUT   ototal    DECIMAL(8,2)
)
BEGIN
    # 声明局部变量
    DECLARE total    DECIMAL(8,2);
    DECLARE taxrate  INT   DEFAULT  6;

    SELECT SUM(item_price * quantity)
    FROM OrderItems
    WHERE order_num = onumber
    INTO total;

    IF taxable THEN
        SELECT total + (total/100 * taxrate) INTO total;
    END IF;

    # 结果保存到ototal中
    SELECT total INTO ototal;
END //

DELIMITER ;

DECLARE语句定义了两个局部变量,DECLARE要求指定变量名和数据类型,它支持可选的默认值。IF语句检查taxable是否为真,如果为真,则用另一个SELECT语句增加营业税到局部变量total。最后,将局部变量total保存到ototal。

调用该存储过程:

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

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

2.3.6 检查存储过程

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

SHOW CREATE PROCEDURE ordertotal;

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

SHOW PROCEDURE STATUS;

可看出,SHOW PROCEDURE STATUS;显示了太多无关紧要的信息,为限制其输出,可以使用LIKE指定一个过滤模式。例如:

SHOW PROCEDURE STATUS LIKE 'ordertotal';

如果您发现文中有不清楚或者有问题的地方,请在下方评论区留言,我会根据您的评论,更新文中相关内容,谢谢!

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 216,651评论 6 501
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 92,468评论 3 392
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 162,931评论 0 353
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,218评论 1 292
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,234评论 6 388
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,198评论 1 299
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,084评论 3 418
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,926评论 0 274
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,341评论 1 311
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,563评论 2 333
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,731评论 1 348
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,430评论 5 343
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,036评论 3 326
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,676评论 0 22
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,829评论 1 269
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,743评论 2 368
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,629评论 2 354

推荐阅读更多精彩内容