搜尋
首頁資料庫mysql教程详细讲述MySQL中的子查询操作_MySQL

继续做以下的前期准备工作:

    新建一个测试数据库TestDB;

   

  create database TestDB;

    创建测试表table1和table2;

   CREATE TABLE table1
   (
     customer_id VARCHAR(10) NOT NULL,
     city VARCHAR(10) NOT NULL,
     PRIMARY KEY(customer_id)
   )ENGINE=INNODB DEFAULT CHARSET=UTF8;

   CREATE TABLE table2
   (
     order_id INT NOT NULL auto_increment,
     customer_id VARCHAR(10),
     PRIMARY KEY(order_id)
   )ENGINE=INNODB DEFAULT CHARSET=UTF8;

    插入测试数据;

   INSERT INTO table1(customer_id,city) VALUES('163','hangzhou');
   INSERT INTO table1(customer_id,city) VALUES('9you','shanghai');
   INSERT INTO table1(customer_id,city) VALUES('tx','hangzhou');
   INSERT INTO table1(customer_id,city) VALUES('baidu','hangzhou');

   INSERT INTO table2(customer_id) VALUES('163');
   INSERT INTO table2(customer_id) VALUES('163');
   INSERT INTO table2(customer_id) VALUES('9you');
   INSERT INTO table2(customer_id) VALUES('9you');
   INSERT INTO table2(customer_id) VALUES('9you');
   INSERT INTO table2(customer_id) VALUES('tx');

    准备工作做完以后,table1和table2看起来应该像下面这样:

   mysql> select * from table1;
   +-------------+----------+
   | customer_id | city   |
   +-------------+----------+
   | 163     | hangzhou |
   | 9you    | shanghai |
   | baidu    | hangzhou |
   | tx     | hangzhou |
   +-------------+----------+
   4 rows in set (0.00 sec)

   mysql> select * from table2;
   +----------+-------------+
   | order_id | customer_id |
   +----------+-------------+
   |    1 | 163     |
   |    2 | 163     |
   |    3 | 9you    |
   |    4 | 9you    |
   |    5 | 9you    |
   |    6 | tx     |
   +----------+-------------+
   7 rows in set (0.00 sec)

准备工作做的差不多了,开始今天的总结吧。
一个问题

现在需要查询所有杭州用户的所有订单号,这个SQL语句怎么写?首先,你可以这么写:

select table2.customer_id, table2.order_id from table2 join table1 on table1.customer_id=table2.customer_id where table1.city='hangzhou';

能实现我们需要的结果。但是,我们也可以这么写:

select customer_id, order_id from table2 where customer_id in (select customer_id from table1 where city='hangzhou');

呃?在()括号中的的select语句是什么?问题来了,这到底是什么语法,怎么也可以完成任务,那么这篇博文就围绕着这个问题开始展开。
啥是子查询

简单的说,子查询就是:

201541491747213.png (875×250)

如上图所示,子查询,有叫内部查询,相对于内部查询,包含内部查询的就称为外部查询。子查询可以包含普通select可以包括的任何子句,比如:distinct、group by、order by、limit、join和union等;但是对应的外部查询必须是以下语句之一:select、insert、update、delete、set或者do。

201541491840216.png (485×217)

我们可以在where和having子句中使用子查询,将子查询得到的结果作为判断的条件。
使用比较进行子查询

一个子查询会返回一个标量(就一个值)、一个行、一个列或一个表,这些子查询称之为标量、行、列和表子查询。

当一个子查询返回一个标量时,我们就可以在where或者having子句中使用比较符与子查询得到的结果进行直接判断。比如,我现在要得到比用户tx订单数多的customer_id、city和订单数,这个sql语句怎么写。

先来说说,我写sql的一般步骤:

  •     读懂需求;
  •     得到比用户tx订单数多的customer_id、city和对应的订单数。
  •     看看最终需要得到哪些字段信息;
  •     最终需要得到customer_id、city和订单数信息。
  •     分析这些字段信息涉及到哪几个表;
  •     涉及到表table1和表table2。
  •     这几个表是如何关联的;
  •     表table1和表table2的关联就在于customer_id字段。
  •     分解需求,得到一个个小的需求;
  •         需要得到tx用户的订单数;
  •         需要得到其它用户的订单数;
  •         比较订单数。
  •     确认每一个小需求的过滤条件;
  •     得到每个小需求的结果,进行组装,得到最终结果。

最终,我会写出一下的sql语句:

select table1.customer_id,city,count(order_id) 
from table1 join table2 
on table1.customer_id=table2.customer_id 
where table1.customer_id <> 'tx'
group by customer_id 
having count(order_id) > 
            (select count(order_id) 
             from table2 
             where customer_id='tx' 
             group by customer_id);

上面的查询中使用了子查询,外部查询与子查询得到的结果进行了比较判断。如果子查询返回一个标量值(就一个值),那么外部查询就可以使用:=、>、=、符号进行比较判断;如果子查询返回的不是一个标量值,而外部查询使用了比较符和子查询的结果进行了比较,那么就会抛出异常。
使用ANY进行子查询

上面使用比较符进行子查询,规定了子查询只能返回一个标量值;但是,如果子查询返回的是一个集合,怎么办?

没问题,我们可以使用:any、in、some或者all来和子查询的返回结果进行条件判断。这里先总结使用any进行子查询。

any关键词必须与上面总结的比较操作符一起使用;any关键词的意思是“对于子查询返回的列中的任何一个数值,如果比较结果为TRUE,就返回TRUE”。

好比“10 >any(11, 20, 2, 30)”,由于10>2,所以,该该判断会返回TRUE;只要10与集合中的任意一个进行比较,得到TRUE时,就会返回TRUE。

比如,我现在要查询比customer_id为tx或者9you的订单数量多的用户的id、城市和订单数量。

我可以得到以下的sql语句来完成需求。

select table1.customer_id,city,count(order_id)
from table1 join table2
on table1.customer_id=table2.customer_id
where table1.customer_id<>'tx' and table1.customer_id<>'9you'
group by customer_id
having count(order_id) >
any (
select count(order_id)
from table2
where customer_id='tx' or customer_id='9you'
group by customer_id);

any的意思比较好明白,直译就是任意一个,只要条件满足任意的一个,就返回TRUE。
使用IN进行子查询

使用in进行子查询,这个我们在日常写sql的时候是经常遇到的。in的意思就是指定的一个值是否在这个集合中,如何在就返回TRUE;否则就返回FALSE了。

in是“=any”的别名,在使用“=any”的地方,我们都可以使用“in”来进行替换。这里就不举例了,尽情的发挥想象,自行发挥吧。

有了in,肯定就有了not in;not in并不是和any是同样的意思,not in和all是一个意思,关于all,下面马上就要总结了。
使用SOME进行子查询

some是any的别名,用的比较少。只需要理解any的意思就好了,这里就不做过多的总结。具体请参考上面的any部分的总结。
使用ALL进行子查询

all必须与比较操作符一起使用。all的意思是“对于子查询返回的列中的所有值,如果比较结果为TRUE,则返回TRUE”。

好比“10 >all(2, 4, 5, 1)”,由于10大于集合中的所有值,所以这条判断就返回TRUE;而如果为“10 >all(20, 3, 2, 1, 4)”,这样的话,由于10小于20,所以该判断就会返回FALSE。

all的同义词是not in,表示不等于集合中的所有值,这个很容易和any搞混,平时多留点心就好了。
标量子查询

根据子查询返回值的数量,将子查询可以分为标量子查询和多值子查询。在使用比较符进行子查询时,就要求必须是标量子查询;如果是多值子查询时,使用比较符,就会抛出异常。
多值子查询

与标量子查询对应的就是多值子查询了,多值子查询会返回一列、一行或者一个表,它们组成一个集合。我们一般使用的any、in、all和some等词,将外部查询与子查询的结果进行判断。如果将any、in、all和some等词与标量子查询,就会得到空的结果。
独立子查询

独立子查询是不依赖外部查询而运行的子查询。什么叫依赖外部查询?先看下面两个sql语句。

sql语句1:获得所有hangzhou顾客的订单号。

select order_id 
from table2 
where customer_id in 
          (select customer_id 
          from table1 
          where city='hangzhou');

sql语句2:获得城市为hangzhou,并且存在订单的用户。

select * 
from table1 
where city='hangzhou' and exists
                (select * 
                from table2 
                where table1.customer_id=table2.customer_id);

上面的两条sql语句,虽然例子举的有点不是很恰当,但是足以说明这里的问题了。

对于sql语句1,我们将子查询单独复制出来,也是可以单独执行的,就是子查询与外部查询没有任何关系。

对于sql语句2,我们将子查询单独复制出来,就无法单独执行了,由于sql语句2的子查询依赖外部查询的某些字段,这就导致子查询就依赖外部查询,就产生了相关性。

对于子查询,很多时候都会考虑到效率的问题。当我们执行一个select语句时,可以加上explain关键字,用来查看查询类型,查询时使用的索引以及其它等等信息。比如这么用:

explain select order_id 
  from table2 
  where customer_id in 
            (select customer_id 
            from table1 
            where city='hangzhou');

使用独立子查询,如果子查询部分对集合的最大遍历次数为n,外部查询的最大遍历次数为m时,我们可以记为:O(m+n)。而如果使用相关子查询,它的遍历次数可能会达到O(m+m*n)。可以看到,效率就会成倍的下降;所以,大伙在使用子查询时,一定要考虑到子查询的相关性。

关于explain的更多解释,请参考这里。
相关子查询

相关子查询是指引用了外部查询列的子查询,即子查询会对外部查询的每行进行一次计算。但是在MySQL的内部,会进行动态优化,会随着情况的不同会有所不同。使用相关子查询是最容易出现性能的地方。而关于sql语句的优化,这又是一个非常大的话题了,只能通过实际的经验积累,才能更好的去理解如何进行优化。

关于sql的性能,我这里不能说什么,如果只是阅读其它人的文章来考虑性能问题,其实是没有任何感觉的,我们需要实际的项目中才能更好的理解。
EXISTS谓词

EXISTS是一个非常牛叉的谓词,它允许数据库高效地检查指定查询是否产生某些行。根据子查询是否返回行,该谓词返回TRUE或FALSE。与其它谓词和逻辑表达式不同的是,无论输入子查询是否返回行,EXISTS都不会返回UNKNOWN,对于EXISTS来说,UNKNOWN就是FALSE。还是上面的语句,获得城市为hangzhou,并且存在订单的用户。

select * 
from table1 
where city='hangzhou' and exists
                (select * 
                from table2 
                where table1.customer_id=table2.customer_id);

使用explain查看一下,就会得到以下内容:

201541491929351.png (1082×140)

我们可以很明显的看到,存在一个相关的子查询(DEPENDENT SUBQUERY)。可以看到EXISTS和IN是非常相似的,那么它们之间的区别是什么呢?

关于IN和EXISTS的主要区别在于三值逻辑的判断上。EXISTS总是返回TRUE或FALSE,而对于IN,除了TRUE、FALSE值外,还有可能对NULL值返回UNKNOWN。但是在过滤器中,UNKNOWN的处理方式与FALSE相同,因此使用IN与使用EXISTS一样,SQL优化器会选择相同的执行计划。

说到了IN和EXISTS几乎是一样的,但是,就不得不说到NOT IN和NOT EXISTS,对于输入列表中包含NULL值时,NOT EXISTS和NOT IN之间的差异就表现的非常大了。输入列表包含NULL值时,IN总是返回TRUE和UNKNOWN,因此NOT IN就会得到NOT TRUE和NOT UNKNOWN,即FALSE和UNKNOWN。

mysql> select 'c' NOT IN ('a', 'b', NULL)\G;

执行一下上述代码,看看结果。你就会感到惊讶。
派生表

上面也说到了,在子查询返回的值中,也可能返回一个表,如果将子查询返回的虚拟表再次作为FROM子句的输入时,这就子查询的虚拟表就成为了一个派生表。语法结构如下:

FROM (subquery expression) AS derived_table_alias

由于派生表是完全的虚拟表,并没有也不可能被物理地具体化。
总结

总算总结的差不多了,当然了子查询的东西还是有很多的,不可能一篇文章就能总结的完的,这里只是把一些基本的概念,常用的知识点进行了总结,关于将子查询使用到update、delete和insert语句中的用法,我这里并没有涉及,大体上都是大同小异的。知识这个东西,展开了,就没有头了,还是需要适可而止,适当的进行深度的挖掘,但是深度最好不要超过2,关于这个2如何定义,自行把握。好了,这篇文章就到此为止了,我们下一篇见。

陳述
本文內容由網友自願投稿,版權歸原作者所有。本站不承擔相應的法律責任。如發現涉嫌抄襲或侵權的內容,請聯絡admin@php.cn
MySQL的角色:Web應用程序中的數據庫MySQL的角色:Web應用程序中的數據庫Apr 17, 2025 am 12:23 AM

MySQL在Web應用中的主要作用是存儲和管理數據。 1.MySQL高效處理用戶信息、產品目錄和交易記錄等數據。 2.通過SQL查詢,開發者能從數據庫提取信息生成動態內容。 3.MySQL基於客戶端-服務器模型工作,確保查詢速度可接受。

mysql:構建您的第一個數據庫mysql:構建您的第一個數據庫Apr 17, 2025 am 12:22 AM

構建MySQL數據庫的步驟包括:1.創建數據庫和表,2.插入數據,3.進行查詢。首先,使用CREATEDATABASE和CREATETABLE語句創建數據庫和表,然後用INSERTINTO語句插入數據,最後用SELECT語句查詢數據。

MySQL:一種對數據存儲的初學者友好方法MySQL:一種對數據存儲的初學者友好方法Apr 17, 2025 am 12:21 AM

MySQL適合初學者,因為它易用且功能強大。 1.MySQL是關係型數據庫,使用SQL進行CRUD操作。 2.安裝簡單,需配置root用戶密碼。 3.使用INSERT、UPDATE、DELETE、SELECT進行數據操作。 4.複雜查詢可使用ORDERBY、WHERE和JOIN。 5.調試需檢查語法,使用EXPLAIN分析查詢。 6.優化建議包括使用索引、選擇合適數據類型和良好編程習慣。

MySQL初學者友好嗎?評估學習曲線MySQL初學者友好嗎?評估學習曲線Apr 17, 2025 am 12:19 AM

MySQL適合初學者,因為:1)易於安裝和配置,2)有豐富的學習資源,3)SQL語法直觀,4)工具支持強大。儘管如此,初學者需克服數據庫設計、查詢優化、安全管理和數據備份等挑戰。

SQL是一種編程語言嗎?澄清術語SQL是一種編程語言嗎?澄清術語Apr 17, 2025 am 12:17 AM

是的,sqlisaprogramminglanguges pecialized fordatamanage.1)它具有焦點,focusingonwhattoachieveratherthanhow.2)sqlisessential forquerying forquerying,插入,更新,更新,和detletingdatainrelationalDatabases.3)

解釋酸的特性(原子,一致性,隔離,耐用性)。解釋酸的特性(原子,一致性,隔離,耐用性)。Apr 16, 2025 am 12:20 AM

ACID屬性包括原子性、一致性、隔離性和持久性,是數據庫設計的基石。 1.原子性確保事務要么完全成功,要么完全失敗。 2.一致性保證數據庫在事務前後保持一致狀態。 3.隔離性確保事務之間互不干擾。 4.持久性確保事務提交後數據永久保存。

MySQL:數據庫管理系統與編程語言MySQL:數據庫管理系統與編程語言Apr 16, 2025 am 12:19 AM

MySQL既是數據庫管理系統(DBMS),也與編程語言緊密相關。 1)作為DBMS,MySQL用於存儲、組織和檢索數據,優化索引可提高查詢性能。 2)通過SQL與編程語言結合,嵌入在如Python中,使用ORM工具如SQLAlchemy可簡化操作。 3)性能優化包括索引、查詢、緩存、分庫分錶和事務管理。

mySQL:使用SQL命令管理數據mySQL:使用SQL命令管理數據Apr 16, 2025 am 12:19 AM

MySQL使用SQL命令管理數據。 1.基本命令包括SELECT、INSERT、UPDATE和DELETE。 2.高級用法涉及JOIN、子查詢和聚合函數。 3.常見錯誤有語法、邏輯和性能問題。 4.優化技巧包括使用索引、避免SELECT*和使用LIMIT。

See all articles

熱AI工具

Undresser.AI Undress

Undresser.AI Undress

人工智慧驅動的應用程序,用於創建逼真的裸體照片

AI Clothes Remover

AI Clothes Remover

用於從照片中去除衣服的線上人工智慧工具。

Undress AI Tool

Undress AI Tool

免費脫衣圖片

Clothoff.io

Clothoff.io

AI脫衣器

AI Hentai Generator

AI Hentai Generator

免費產生 AI 無盡。

熱門文章

R.E.P.O.能量晶體解釋及其做什麼(黃色晶體)
1 個月前By尊渡假赌尊渡假赌尊渡假赌
R.E.P.O.最佳圖形設置
1 個月前By尊渡假赌尊渡假赌尊渡假赌
R.E.P.O.如果您聽不到任何人,如何修復音頻
1 個月前By尊渡假赌尊渡假赌尊渡假赌
R.E.P.O.聊天命令以及如何使用它們
1 個月前By尊渡假赌尊渡假赌尊渡假赌

熱工具

記事本++7.3.1

記事本++7.3.1

好用且免費的程式碼編輯器

SecLists

SecLists

SecLists是最終安全測試人員的伙伴。它是一個包含各種類型清單的集合,這些清單在安全評估過程中經常使用,而且都在一個地方。 SecLists透過方便地提供安全測試人員可能需要的所有列表,幫助提高安全測試的效率和生產力。清單類型包括使用者名稱、密碼、URL、模糊測試有效載荷、敏感資料模式、Web shell等等。測試人員只需將此儲存庫拉到新的測試機上,他就可以存取所需的每種類型的清單。

禪工作室 13.0.1

禪工作室 13.0.1

強大的PHP整合開發環境

DVWA

DVWA

Damn Vulnerable Web App (DVWA) 是一個PHP/MySQL的Web應用程序,非常容易受到攻擊。它的主要目標是成為安全專業人員在合法環境中測試自己的技能和工具的輔助工具,幫助Web開發人員更好地理解保護網路應用程式的過程,並幫助教師/學生在課堂環境中教授/學習Web應用程式安全性。 DVWA的目標是透過簡單直接的介面練習一些最常見的Web漏洞,難度各不相同。請注意,該軟體中

ZendStudio 13.5.1 Mac

ZendStudio 13.5.1 Mac

強大的PHP整合開發環境