目录
6. 子查询
子查询也被称作内查询或者嵌套查询,是指在一个查询语句里面还嵌套着另一个查询语 句。子查询语句是先于主查询语句被执行的,其结果作为外层的条件返回给主查询进行下一 步的查询过滤。
注意:子语句可以与主语句所查询的表相同,也可以是不同表
在关系型数据库管理系统(如MySQL)中,当你试图在一个DELETE语句中使用子查询,并且该子查询涉及到的是要删除记录所在的同一张表时,SQL语法并不允许这样做。这是因为根据SQL的标准规定,在一个DELETE语句的WHERE子句中的子查询不能直接引用要删除记录的目标表作为FROM或JOIN的一部分。
这个问题背后的逻辑在于避免循环引用和不确定的行为。当你尝试在一个操作中同时读取和修改同一张表的数据时,尤其是在没有恰当并发控制的情况下,可能导致数据不一致性和死锁等问题。例如,子查询可能在DELETE过程中动态变化,从而导致预期以外的结果。
具体到MySQL,当你尝试这样做的时候,MySQL会抛出一个错误,类似于
1093 - You can't specify target table 'table_name' for update in FROM clause.
这个错误明确指出,你不能在更新(包括删除操作也是一种更新)操作的FROM子句中指定目标表。为了绕过这个问题,你可以采取以下几种策略之一:
- 先将子查询的结果保存到临时表或变量中,然后根据临时表或变量中的数据进行删除操作。
- 使用JOIN操作与其他表一起进行删除,但在这种情况下,子查询不会直接引用待删除记录的表。
- 如果数据库系统支持(例如MariaDB 10.3之后版本解决了这个问题),则可以直接在子查询中引用目标表,但这仅限于那些已经解决了此限制的特定版本或实现。
总之,确保在多表操作尤其是删除操作中遵循数据库系统的语法规则和最佳实践,以避免数据丢失或不一致的情况发生。
子查询不仅可以在 SELECT 语句中使用,在 INERT、UPDATE、DELETE 中也同样适用。在嵌套的时候,子查询内部还可以再次嵌套新的子查询,也就是说可以多层嵌套。
语法:
IN 用来判断某个值是否在给定的结果集中,通常结合子查询来使用
语法:
<表达式> [NOT] IN <子查询>
当表达式与子查询返回的结果集中的某个值相等时,返回 TRUE,否则返回 FALSE。
若启用了 NOT 关键字,则返回值相反。
需要注意的是,子查询只能返回一列数据,如果需求比较复杂,一列解决不了问题,
可以使用多层嵌套的方式来应对。多数情况下,子查询都是与 SELECT 语句一起使用的
操作:
现在准备三张表 class1 class2 class3
查询分数大于80的记录
三表一起查
子查询还可以用在 INSERT 语句中。子查询的结果集可以通过 INSERT 语句插入到其 他的表中
UPDATE 语句也可以使用子查询。UPDATE 内的子查询,在 set 更新内容时,可以是单独的一列,也可以是多列。
将 lilei 的分数改为60
mysql> update class3 set score=60 where id in (select id from class1 where id=7);
DELETE 也适用于子查询
删除分数大于80的记录
mysql> delete from class3 where id in (select id from class1 where score > 80);
在 IN 前面还可以添加 NOT,其作用与IN相反,表示否定(即不在子查询的结果集里面)
删除分数不是大于等于80的记录
mysql> delete from class3 where id not in (select id from class1 where score > 80);
EXISTS 关键字
- 用来测试内查询有没有产生任何结果,类似布尔值是否为真。
- 如果内查询有结果的话,系统就会执行外查询中的SQL语句。若是没有结果的话,那整个SQL语句就不会产生任何结果。
格式:
SELECT "字段1" FROM "表格1" WHERE EXISTS (SELECT * FROM "表格2" WHERE "条件");
这个关键字在子查询时,主要用于判断子查询的结果集是否为空。如果不为空, 则返回 TRUE;反之,则返回 FALSE
查询如果存在分数等于80的记录则计算class3的字段数
应用:学校里面 (人员信息统计,只有当所有人全部签到之后,在人员信息统计表录入完成侯,我才需要进行统计)
查询如果存在分数小于5的记录则计算class3的字段数,class3表没有小于5的,所以返回0
别名 as
将结果集做为一张表进行查询的时候,我们也需要用到别名
格式:
select 表.字段,字段 from 表;
select * from 表名 此为标准格式,而以上的查询语句,"表名"的位置其实是一个完整结果集,mysql并不能直接识别,而此时给与结果集设置一个别名 s 以”select s.id from s“的方式查询将此结果集视为一张"表",就可以正常查询数据了.
7. 视图
理论:
视图:优化操作+安全方案
数据库中的虚拟表,这张虚拟表中不包含真实数据,只是做了真实数据的映射
作用场景[图]:
针对不同的人(权限身份),提供不同结果集的“表”(以表格的形式展示)
作用范围:
select * from class1; #展示的部分是class1表
select * from view_name; #展示的一张或多张表
功能:
简化查询结果集、灵活查询、可以针对不同用户呈现不同结果集、相对有更高的安全性
本质而言视图是一种 select (结果集的呈现)
注意:
视图适合于多表连接浏览时使用,不适合增、删、改
而存储过程适合于使用较频繁的SQL语句,这样可以提高执行效率!
视图和表的区别和联系
- 区别:
- 视图是已经编译好的sql语句。而表不是
- 视图没有实际的物理记录。而表有
- 表只用物理空间而视图不占用物理空间,视图只是逻辑概念的存在,表可以及时对它进行修改,但视图只能有创建的语句来修改
- 视图是查看数据表的一种方法,可以查询数据表中某些字段构成的数据,只是一些SQL语句的集合。从安全的角度说,视图可以不给用户接触数据表,从而不知道表结构
- 表属于全局模式中的表,是实表;视图属于局部模式的表,是虚表
- 视图的建立和删除只影响视图本身,不影响对应的基本表。(但是更新视图数据,是会影响到基本表的)
- 联系:
- 视图(view)是在基本表之上建立的表,它的结构(即所定义的列)和内容(即所有数据行)都来自基本表,它依据基本表存在而存在。一个视图可以对应一个基本表,也可以对应多个基本表。视图是基本表的抽象和在逻辑意义上建立的新关系。
操作:
创建视图(单表)
mysql> create view v_score as select * from class1 where score>=80;
查看视图
mysql> select * from v_score;
查看表状态
show table status\G
查看视图与源表结构
mysql> desc v_score;
mysql> desc class1;
创建多表视图
准备两张表
mysql> create view v_cs(id,name,score,age) as select c.id,c.name,c.score,t.age from class1 c,test1 t where c.name=t.name;
mysql> select * from v_cs;
修改视图数据
此时,表里面的数据也会跟着改变
如果我们直接修改表里数据,那么视图数据也会跟着修改
修改表不能修改以函数、复合函数方式计算出来的字段
查询方便、安全性
查询方便:索引速度快、同时可以多表查询更为迅速(视图不保存真实数据,视图本质类似select)
安全性:我们实现登陆的账户是root ——》所拥有权限 ,视图无法显示完整的约束
视图的优缺点
数据库视图(Database Views)是一种虚拟表,它是从一个或多个基表(或其它视图)中根据特定的查询语句创建的,提供了一种抽象的方式来查看和操作存储在数据库中的数据。视图的优点和缺点如下:
优点:
-
安全性:通过视图可以隐藏敏感数据,只暴露必要的列给用户,起到数据安全的作用。例如,可以创建一个视图,只显示除密码外的用户信息。
-
简化复杂性:视图可以把复杂的查询逻辑封装起来,使用户只需要面对一个简单的接口,简化了客户端应用程序对复杂数据查询的需求。
-
数据整合:视图可以从多个表中抽取数据形成一个逻辑上的统一视图,使得用户不必关心数据的具体分布情况。
-
维护方便:当底层表结构发生变化时,可以通过修改视图定义来适应变化,而不必更改所有使用这些表的应用程序代码。
-
数据一致性:通过视图可以强制实施业务规则,确保只有满足特定条件的数据才能被访问或修改。
缺点:
-
性能开销:视图的操作实际上是对基表的查询操作,如果视图的定义包含了复杂的联接和筛选条件,可能会导致查询性能下降。
-
间接访问:因为视图是虚拟表,不是物理存储的数据,所以对视图进行增删改操作时,实际上是对其基表进行操作,如果基表数据量大或者关联复杂,可能会导致操作效率不高。
-
依赖于基表:一旦基表结构发生改变,可能会影响到视图的正常使用。如果基表被删除或其结构被修改,那么基于这些表创建的视图可能无法继续工作。
-
索引局限:数据库系统一般不会在视图上直接创建索引,因此通过视图进行查询时可能无法利用索引优化性能。
-
更新限制:并非所有的视图都可以进行插入、更新或删除操作,特别是包含GROUP BY、UNION、JOIN等复杂查询构造的视图往往不允许进行修改操作。
8. NULL 值
在 SQL 语句使用过程中,经常会碰到 NULL 这几个字符。通常使用 NULL 来表示缺失 的值,也就是在表中该字段是没有值的。如果在创建表时,限制某些字段不为空,则可以使用 NOT NULL 关键字,不使用则默认可以为空。在向表内插入记录或者更新记录时,如果该字段没有 NOT NULL 并且没有值,这时候新记录的该字段将被保存为 NULL。需要注意 的是,NULL 值与数字 0 或者空白(spaces)的字段是不同的,值为 NULL 的字段是没有 值的。在 SQL 语句中,使用 IS NULL 可以判断表内的某个字段是不是 NULL 值,相反的用 IS NOT NULL 可以判断不是 NULL 值。
null值与空值的区别((空气与真空))
- 空值长度为0,不占空间,NULL值的长度为null,占用空间
- is null 无法判断空值
- 空值使用 "=“ 或者 ”<>" 来处理(!=)
- count()计算时,NULL会忽略,空值会加入计算
验证:count()计算时,NULL会忽略,空值会加入计算
查询不为空的值
9. union 联级
UNION联集:将两个SQL语句的结果合并起来,两个SQL语句所产生的字段需要是同样的数据记录种类。
9.1 union (合并后去重)
生成结果的数据记录值将没有重复,且按照字段的顺序进行排序。#合并后去重
格式:[select 语句1] UNION [select 语句2];
操作:
mysql> select name from jk union select name from class3;
9.2 union all (合并后不去重)
mysql> select name from class3 union all select name from class1;
9.3 取非交集值
(1)联级方法中 count(*)<=1
(2)左右内连接 将is not null 改为 is null
(3)子查询 外连接查询 not in (内连接查询)
10. case 条件选择查询语句
SELECT CASE ("字段名")
WHEN "条件1" THEN "结果1"
WHEN "条件2" THEN "结果2"
[ELSE "结果N"]
END
FROM "表名";
# "条件"可以是一个数值或是公式。ELSE子句则并不是必须的。
操作:
mysql> select case address
-> when 'nanjing' then score +10
-> end
-> from class1;
也可以这么写
二. 连接查询
MySQL 的连接查询,通常都是将来自两个或多个表的记录行结合起来,基于这些表之间的 共同字段,进行数据的拼接。首先,要确定一个主表作为结果集,然后将其他表的行有选择 性的连接到选定的主表结果集上。使用较多的连接查询包括:内连接、左连接和右连接
准备表数据
create table test1 (
a_id int(11) default null,
a_name varchar(32) default null,
a_level int(11) default null);
create table test2 (
b_id int(11) default null,
b_name varchar(32) default null,
b_level int(11) default null);
insert into test1 values (1,'aaaa',10);
insert into test1 values (2,'bbbb',20);
insert into test1 values (3,'cccc',30);
insert into test1 values (4,'dddd',40);
insert into test2 values (2,'bbbb',20);
insert into test2 values (3,'cccc',30);
insert into test2 values (5,'eeee',50);
insert into test2 values (6,'ffff',60);
2.1 内连接
MySQL 中的内连接就是两张或多张表中同时符合某种条件的数据记录的组合。通常在 FROM 子句中使用关键字 INNER JOIN 来连接多张表,并使用 ON 子句设置连接条件,内连接是系统默认的表连接,所以在 FROM 子句后可以省略 INNER 关键字,只使用 关键字 JOIN。同时有多个表时,也可以连续使用 INNER JOIN 来实现多表的内连接,不过为了更好的性能,建议最好不要超过三个表
#语法:
SELECT column_name(s)FROM table1 INNER JOIN table2 ON table1.column_name = table2.column_name;
操作:
mysql> select a.a_id,a.a_name from test1 a inner join test2 b on a.a_name=b.b_name;
2.2 左连接
左连接也可以被称为左外连接,在 FROM 子句中使用 LEFT JOIN 或者 LEFT OUTER JOIN 关键字来表示。左连接以左侧表为基础表,接收左表的所有行,并用这些行与右侧参 考表中的记录进行匹配,也就是说匹配左表中的所有行以及右表中符合条件的行。
mysql> select a.a_id,a.a_name from test1 a left join test2 b on a.a_name=b.b_name;
左连接中左表的记录将会全部表示出来,而右表只会显示符合搜索条件的记录,右表记录不足的地方均为 NULL。
2.3 右连接
右连接也被称为右外连接,在 FROM 子句中使用 RIGHT JOIN 或者 RIGHT OUTER JOIN 关键字来表示。右连接跟左连接正好相反,它是以右表为基础表,用于接收右表中的所有行,并用这些记录与左表中的行进行匹配
mysql> select a.a_id,a.a_name from test1 a right join test2 b on a.a_name=b.b_name;
在右连接的查询结果集中,除了符合匹配规则的行外,还包括右表中有但是左表中不匹 配的行,这些记录在左表中以 NULL 补足
三. 存储过程
1. 介绍
数据库存储过程(Stored Procedure)是一种预编译的数据库对象,它包含了一系列用于执行特定任务的SQL语句集合。存储过程不仅包含查询语句,还可以包括流程控制语句(如IF...THEN...ELSE、WHILE、LOOP等)、变量声明、条件判断、循环结构以及其他数据库系统提供的编程元素。存储过程在数据库内部被编译和优化,存放在数据库服务器中,用户不需要每次都发送整个SQL脚本到数据库服务器执行,只需调用存储过程的名称,并传入相应的参数(如果存储过程带有参数的话),就可以执行存储过程内定义的复杂操作。
- 存储过程是一组为了完成特定功能的SQL语句集合。 两个点:第一 触发器(定时任务) 第二个判断
- 存储过程这个功能是从5.0版本才开始支持的,它可以加快数据库的处理速度,增强数据库在实际应用中的灵活性。存储过程在使用过程中是将常用或者复杂的工作预先使用SQL语句写好并用一个指定的名称存储起来,这个过程经编译和优化后存储在数据库服务器中。当需要使用该存储过程时,只需要调用它即可。操作数据库的传统 SQL 语句在执行时需要先编译,然后再去执行,跟存储过程一对比,明显存储过程在执行上速度更快,效率更高
存储过程的主要优点和特性包括:
-
代码复用:存储过程可以被多个应用程序或用户反复调用,提高了代码的重用性和模块化程度。
-
性能优化:由于存储过程在数据库内部执行,减少了网络传输成本,尤其对于大批量数据处理和复杂的业务逻辑,可以显著提高执行效率。
-
安全性增强:通过权限管理和角色分配,可以控制谁有权执行存储过程,间接实现了对数据的安全保护,无需向用户暴露底层表结构。
-
事务控制:存储过程可以包含事务处理,确保一组操作要么全部成功,要么全部回滚,保证数据完整性。
-
参数化:存储过程可以定义输入参数、输出参数和输入输出参数,使其更加灵活,能够适应不同的应用场景。
-
减少网络流量:只需发送存储过程名称和参数,降低了网络传输的数据量。
-
模块化与封装:存储过程可以封装复杂的业务逻辑,便于维护和升级。
存储过程在许多大型数据库系统中都有广泛应用,如Oracle、SQL Server、MySQL、PostgreSQL等。开发者可以根据需求编写适合特定场景的存储过程,以提高数据库操作的效率和安全性。
语法:
CREATE PROCEDURE <过程名> ( [过程参数[,…] ] ) <过程体>
[过程参数[,…] ] 格式
<过程名>:尽量避免与内置的函数或字段重名
<过程体>:语句
[ IN | OUT | INOUT ] <参数名><类型>
2. 操作:
创建存储过程:
mysql> delimiter $$
mysql> create procedure pj()
-> begin
-> create table jk(id int,name varchar(20),score int(20));
-> insert into jk values(1,'xyl',90);
-> insert into jk values(2,'cp',80);
-> select * from jk;
-> end $$
Query OK, 0 rows affected (0.00 sec)
mysql> delimiter ;
调用存储过程:
mysql> call pj();
查看存储过程:
SHOW CREATE PROCEDURE [数据库.]存储过程名;
查看指定存储过程信息:
#查看存储过程
SHOW PROCEDURE STATUS
#查看指定存储过程信息
mysql> show procedure status like '%pj%'\G
修改存储过程:
删除存储过程:
存储过程内容的修改方法是通过删除原有存储过程,之后再以相同的名称创建新的存储过程。
DROP PROCEDURE IF EXISTS Proc;
3. 存储过程的参数
IN | 输入参数:表示调用者向过程传入值(传入值可以是字面量或变量) |
OUT | 输出参数:表示过程向调用者传出值(可以返回多个值)(传出值只能是变量) |
INOUT | 输入输出参数:既表示调用者向过程传入值,又表示过程向调用者传出值(值只能是变量) 即表示调用者向过程传入值,又表示过程向调用者传出值(只能是变量) |
操作:
3.1 传入参数的存储过程
创建传参的存储过程 :
mysql> delimiter ;
mysql> delimiter $$
mysql> create procedure pj3(in inname varchar(30))
-> begin
-> select * from class1 where name=inname;
-> end $$
Query OK, 0 rows affected (0.00 sec)
mysql> delimiter ;
3.2 传出参数的存储过程
只接受变量的调用,不接受直接传入的值
mysql> delimiter $$
mysql> create procedure pj4(out num int)
-> begin
-> set num=100;
-> end $$
Query OK, 0 rows affected (0.00 sec)
mysql> delimiter ;
mysql> call pj4(@newnums);
Query OK, 0 rows affected (0.00 sec)
mysql> select @newnums;
+----------+
| @newnums |
+----------+
| 100 |
+----------+
1 row in set (0.00 sec)
3.3 输入输出参数
mysql> delimiter @@
mysql> create procedure pj6(inout a int)
-> begin
-> select a;
-> set a=a+10;
-> select a;
-> end @@
Query OK, 0 rows affected (0.00 sec)
mysql> delimiter ;
mysql> set @b=10;
Query OK, 0 rows affected (0.00 sec)
mysql> call pj6(@b);
+------+
| a |
+------+
| 10 |
+------+
1 row in set (0.00 sec)
+------+
| a |
+------+
| 20 |
+------+
1 row in set (0.00 sec)
Query OK, 0 rows affected (0.00 sec)
3.4 存储过程的控制语句
3.4.1 if 条件判断语句
mysql> create procedure pj7(in num int)
-> begin
-> if num >=10 then
-> set num=num-5;
-> else
-> set num=num*2;
-> end if;
-> select num;
-> end $$
Query OK, 0 rows affected (0.00 sec)
mysql> delimiter ;
3.4.2 while 循环条件语句
注意:
decalre -i var
声明变量var为整数型( 即增加整型属性)。
decalre +i var
删除变量var的整型属性。
mysql> delimiter $$
mysql> create procedure test3 ()
-> begin
-> declare a int;
-> declare i int;
-> set a=10;
-> set i=1;
-> while i<=10 do
-> set a=a+10;
-> set i=i+1;
-> end while ;
-> select a;
-> end $$
mysql> delimiter ;
总结
1.子查询
- In:查询已知的值数据记录
- Not In:表示否定,不存在子查询结果集里(相当于取反)
- Exists:判断查询结果集是否为空(空返回False,非空返回TRUE)
2.视图——View
- 无论修改视图还是原表数据,都会随之一起更改
3.缺失值——Null
- Null:占用空间Null值
- 空值:占用空间为0
4.连接查询——内 左 右
- 内连接:Inner Join
- 左连接:Left Join
- 右连接:Right Join
delimiter $$
#将语句的结束符号从分号;临时改为两个$$(可以自定义)
mysql> create procedure class()
#创建存储过程,过程名为class,不带参数
-> begin
#过程体以关键字 BEGIN 开始
-> create table class3(id int,name varchar(8),score decimal(5,2));
-> insert into class3 values(1,'wsc',98),(2,'ljc',95);
-> select * from class3;
#过程体语句
-> END $$
#过程体以关键字 END 结束
Query OK, 0 rows affected (0.12 sec)
mysql> DELIMITER ;
#将语句的结束符号恢复为分号
5.2查看存储过程
show create procedure Procedure_name
call Procedure_name();
6.参数
- IN 输入参数:表示调用者向过程传入值(传入值可以是字面量或变量)
- OUT 输出参数:表示过程向调用者传出值(可以返回多个值)(传出值只能是变量)
- INOUT 输入输出参数:既表示调用者向过程传入值,又表示过程向调用者传出值(值只能是变量)