sql嵌套查询很慢——优化⽅案
⼯作需要写⼀个查询:
最开始写了最原始的嵌套查询:
select * from realservice where realservname in (select realservname from groupmember where groupname in (select groupname from v_group where vn 发现很慢,查了⼀下⽤时4min 22.74sec。
然后考虑如何优化,⾃然想到了连接,然后优化后代码如下:
select * from realservice as a join groupmember as b alservname upname in (upname from v_group as a ⽤时0.03sec,已经很快了,但是出于好奇,想知道把in和=的区别,继续改进:
select * from realservice as a join groupmember as b alservname upname=(upname from v_group as a jo ⽤时0.00sec
所以先来了解下in和=的区别:
等号:确切知道所要查的内容,且为单⼀值时,可以使⽤等号运算符来进⾏数据⽐较。等号运算符中可以使⽤字符串、⽇期或数字。
in:当确切知道所要查的内容,且为多个值时,可以使⽤ IN ⼦句来进⾏数据⽐较。IN ⼦句中也可以使⽤数字、字符串或⽇期。
这⾥⼦查询出来的groupname只有⼀个,是单⼀值,所以可以使⽤等号,但如果groupname查询出来的结果是多个,再使⽤等号就会出错,此时只能使⽤in来查询了。
由于groupname有可能会出现有多个值的情况,所以为了保证查询的正确,这⾥应该使⽤in⽽不是等号。
然后看⼀下为什么直接使⽤多层嵌套查询会很慢?我们先看⼀下select查询的本质:
分析器会先看语句的第⼀个词,当它发现第⼀个词是SELECT关键字的时候,它会跳到FROM关键字,然后通过FROM关键字到表名并把表装⼊内存。接着是WHERE关键字,如果不到则返回到SELECT字段解析,如果到WHERE,则分析其中的条件,完成后再回到SELECT分析字段。最后形成⼀张我们要的虚表。
WHERE关键字后⾯的是条件表达式。如果学过C语⾔等编程语⾔就会知道,条件表达式计算完成后,
会有⼀个返回值,即⾮0或0,⾮0即
为真(true),0即为假(false)。同理WHERE后⾯的条件也有⼀个返回值,真或假,来确定接下来执不执⾏SELECT。
分析器先到关键字SELECT,然后跳到FROM关键字将STUDENT表导⼊内存,并通过指针p1到第⼀条记录,接着到WHERE关键字计算它的条件表达式,如果为真那么把这条记录装到⼀个虚表当中,p1再指向下⼀条记录。如果为假那么p1直接指向下⼀条记录,⽽不进
⾏其它操作。⼀直检索完整个表,并把虚表返回给⽤户。
这是⼀个SQL语句的嵌套使⽤,但和上⾯说的SQL语句的执⾏过程也是相同的。嵌套的意思也就是说当分析主SQL语句(外⾯的那个SELECT)到WHERE关键字的时候,⼜进⼊了另⼀个SQL语句中。那么也就是说,分析器先到表Student并装⼊内存,⼀个指针(例如p1)指向Student表中的第⼀条记录。然后进⼊WHERE⾥分析⾥⾯的SQL语句,再把SC表装⼊内存,另⼀个指针(例如p2)指向SC表中的第⼀
条记录,分析WHERE后⾯的条件表达式,依次进⾏分析,最后分析出⼀个虚表2。sql语句优化方式
如果虚表为空表,EXISTS 虚表2 也就为false,不返回到SELECT,⽽p1指向下⼀条记录。如果虚表2不为空也就是有记录,那么EXISTS 虚表2 为true同,返回到SELECT并把p1指向的记录添加到主SQL
语句的虚表1当中。(这也是为什么嵌套的SQL语句SELECT 后⾯为⼀
般为*的原因,因为它EXISTS返回的只是真或假,字段的名没有意义,⽤*就⾏,当然⽤别的也不会错。 )
注意,这⾥虽然嵌套的SQL语句分析完了,但主SQL语句只执⾏了⼀遍,也就是说p1指向Student的第⼀条记录,p1还要再指向Student 表的下⼀条记录并分析,这样⼜进⼊了嵌套中的SQL语句,同上⾯说的⼀样分析。当p1也到了Student表的结尾,整个SQL语句结束。返回虚表1Sname这⼀列。
所以,才说嵌套查询的复杂度是M*N次查询,因为每⼀条数据都要和⼀次⼦查询的查询结果进⾏⽐对,同时,每次查询分析到from的时候都会把表装进⼀次内存,创建⼀次临时表,M*N次的存⼊内存也是很⼤的消耗。
参考链接: