1.什么是视图
视图(View)是一种虚拟存在的表。其内容与真实的表相似,包含一系列带有名称的列和行数据。但是视图并不在数据库中以存储的数据的形式存在。行和列的数据来自定义视图时查询所引用的基本表,并且在具体引用视图时动态生成。
通俗的讲,视图就是一条SELECT语句执行后返回的结果集。所以我们在创建视图的时候,主要的工作就落在创建这条SQL查询语句上。
2.视图的作用
- 简单:使用视图的用户完全不需要关心视图中的数据是通过什么查询得到的,视图中的数据对用户来说已经是过滤好的符合条件的结果集;
- 安全:使用视图的用户只能访问他们被允许查询的结果集,对表的权限管理并不能限制到某个行或某个列,但是通过视图就可以简答地实现;
- 数据独立:一旦视图的结构确定了,可以屏蔽表结构变化对用户的影响,原表增加列对视图没有影响;源表修改列名,则可以通过修改视图来解决,不会造成对访问者的影响。
3.视图操作
创建视图
CREATE
[OR REPLACE]
[ALGORITHM = {UNDEFINED | MERGE | TEMPTABLE}]
[DEFINER = { user | CURRENT_USER }]
[SQL SECURITY { DEFINER | INVOKER }]
VIEW view_name [(column_list)]
AS select_statement
[WITH [CASCADED | LOCAL] CHECK OPTION]
ALGORITHM:
MySQL有三种视图执行策略, 分别是MERGE, TEMPTABLE, UNDEFINED
.
- 使用
MERGE
策略,MySQL会先将输入的查询语句和视图的声明语句进行合并,然后执行合并后的语句并返回。但是如果输入的查询语句中不允许包含一些聚合函数如:MIN, MAX, SUM, COUNT, AVG, etc., or DISTINCT, GROUP BY, HAVING, LIMIT, UNION, UNION ALL, subquery
。同样如果视图声明没有指向任何数据表,也是不允许的。如果出现以上任意情况, MySQL默认会使用UNDEFINED
策略。 - 使用
TEMPTABLE
策略,MySQL先基于视图的声明创建一张temporary table
,当输入查询语句时会直接查询这张temporary table
。由于需要创建temporary table
来存储视图的结果集,TEMPTABLE
的效率要比MERGE
策略低,另外使用temporary table
策略的视图是无法更新的。 - 使用
UNDEFINED
策略,如果创建视图的时候不指定策略,MySQL默认使用此策略。UNDEFINED
策略会自动选择使用上述两种策略中的一个,优先选择MERGE
策略,无法使用则转为TEMPTABLE
策略。
DEFINER
指定视图创建者。
SQL SECURITY
指定视图查询数据时的安全验证方式。
View Name
视图的名称,在MySQL中视图名和表名使用同一命名空间,这意味这视图名不能和表名重复并且要符合表名的命名规范。
Select Statement
在视图的查询声明中你可以查询所有数据库中已存在的表的数据,有以下集中规则:
- 查询声明可以在
WHERE
条件中使用子查询但是不允许在FROM
的来源中使用子查询。 - 查询声明中不允许引用任何变量,包括
local variables, user variables, and session variables
。 - 查询声明中不允许引用
prepare statement
的参数。
补充: 查询声明的From
中可以引用其他视图
实例解析,现有三张表:用户(user)、课程(course)、用户课程中间表(user_course),表结构及数据如下:
-- ----------------------------
-- Table structure for `course`
-- ----------------------------
DROP TABLE IF EXISTS `course`;
CREATE TABLE `course` (
`id` bigint(20) NOT NULL AUTO_INCREMENT,
`name` varchar(200) NOT NULL,
`description` varchar(500) NOT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=4 DEFAULT CHARSET=utf8;
-- ----------------------------
-- Records of course
-- ----------------------------
INSERT INTO `course` VALUES ('1', 'JAVA', 'JAVA课程');
INSERT INTO `course` VALUES ('2', 'C++', 'C++课程');
INSERT INTO `course` VALUES ('3', 'C语言', 'C语言课程');
-- ----------------------------
-- Table structure for `user`
-- ----------------------------
DROP TABLE IF EXISTS `user`;
CREATE TABLE `user` (
`id` bigint(20) NOT NULL AUTO_INCREMENT,
`account` varchar(255) NOT NULL,
`name` varchar(255) NOT NULL,
`address` varchar(255) DEFAULT NULL,
`others` varchar(200) DEFAULT NULL,
`others2` varchar(200) DEFAULT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=4 DEFAULT CHARSET=utf8;
-- ----------------------------
-- Records of user
-- ----------------------------
INSERT INTO `user` VALUES ('1', 'user1', '小陈', '美国', '1', '1');
INSERT INTO `user` VALUES ('2', 'user2', '小张', '日本', '2', '2');
INSERT INTO `user` VALUES ('3', 'user3', '小王', '中国', '3', '3');
-- ----------------------------
-- Table structure for `user_course`
-- ----------------------------
DROP TABLE IF EXISTS `user_course`;
CREATE TABLE `user_course` (
`id` bigint(20) NOT NULL AUTO_INCREMENT,
`userid` bigint(20) NOT NULL,
`courseid` bigint(20) NOT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=7 DEFAULT CHARSET=utf8;
-- ----------------------------
-- Records of user_course
-- ----------------------------
INSERT INTO `user_course` VALUES ('1', '1', '2');
INSERT INTO `user_course` VALUES ('2', '1', '3');
INSERT INTO `user_course` VALUES ('3', '2', '1');
INSERT INTO `user_course` VALUES ('4', '2', '2');
INSERT INTO `user_course` VALUES ('5', '2', '3');
INSERT INTO `user_course` VALUES ('6', '3', '2');
View Code
这时,当我们想要查询小张上的所以课程相关信息的时候,需要这样写一条长长的SQL语句,如下:
SELECT
`uc`.`id` AS `id`,
`u`.`name` AS `username`,
`c`.`name` AS `coursename`
FROM
`user` `u`
LEFT JOIN `user_course` `uc` ON ((`u`.`id` = `uc`.`userid`))
LEFT JOIN `course` `c` ON ((`uc`.`courseid` = `c`.`id`))
WHERE
u.`name` = '小张'
但是我们可以通过视图简化操作,例如我们创建视图view_user_course如下:
-- ----------------------------
-- View structure for `view_user_course`
-- ----------------------------
DROP VIEW
IF EXISTS `view_user_course`;
CREATE ALGORITHM = UNDEFINED
DEFINER = `root`@`localhost`
SQL SECURITY DEFINER
VIEW `view_user_course` AS (
SELECT
`uc`.`id` AS `id`,
`u`.`name` AS `username`,
`c`.`name` AS `coursename`
FROM
(
(
`user` `u`
LEFT JOIN `user_course` `uc` ON ((`u`.`id` = `uc`.`userid`))
)
LEFT JOIN `course` `c` ON ((`uc`.`courseid` = `c`.`id`))
)
);
创建好视图之后,我们可以直接用以下SQL语句在视图上查询小张上的所以课程相关信息,同样可以得到所需结果:
SELECT
vuc.username,
vuc.coursename
FROM
view_user_course vuc
WHERE
vuc.username = '小张'
增删改数据操作
继续,我们可以尝试在视图view_user_course上做增删改数据操作,如下:
update view_user_course set username='test',coursename='JAVASCRIPT' where id=3
遗憾的是操作失败,提示错误信息如下:
[SQL] update view_user_course set username='test',coursename='JAVASCRIPT' where id=3
[Err] 1393 - Can not modify more than one base table through a join view 'demo.view_user_course'
因为不能在一张由多张关联表连接而成的视图上做同时修改两张表的操作;
那么哪些操作可以在视图上进行呢?
视图与表是一对一关系情况:如果没有其它约束(如视图中没有的字段,在基本表中是必填字段情况),是可以进行增删改数据操作;
如我们创建用户关键信息视图view_user_keyinfo,如下:
-- ----------------------------
-- View structure for `view_user_keyinfo`
-- ----------------------------
DROP VIEW
IF EXISTS `view_user_keyinfo`;
CREATE ALGORITHM = UNDEFINED DEFINER = `root`@`localhost` SQL SECURITY DEFINER VIEW `view_user_keyinfo` AS SELECT
`u`.`id` AS `id`,
`u`.`account` AS `account`,
`u`.`name` AS `username`
FROM
`user` `u`;
进行增删改操作如下,操作成功(注意user表中的其它字段要允许为空,否则操作失败):
INSERT INTO view_user_keyinfo (account, username)
VALUES
('test1', 'test1');
DELETE
FROM
view_user_keyinfo
WHERE
username = 'test1';
UPDATE view_user_keyinfo
SET username = 'updateuser'
WHERE
id = 1
视图与表是一对多关系情况:如果只修改一张表的数据,且没有其它约束(如视图中没有的字段,在基本表中是必填字段情况),是可以进行改数据操作,如以下语句,操作成功;
update view_user_course set coursename='JAVA' where id=1;
update view_user_course set username='test2' where id=3;
以下操作失败:
delete from view_user_course where id=3;
insert into view_user_course(username, coursename) VALUES('2','3');