基本 SQL 之增刪改查(二)

上一篇文章中,咱們介紹了 SQL 中最基本的 DML 語法,包括 insert 的插入數據、update 的更新數據、delete 的刪除數據以及基本的查詢語法,但大多比較簡單不能解決咱們平常項目中複雜的需求。java

那麼,本篇就來看一看較爲複雜繁瑣的查詢語法,例如多表的鏈接查詢、嵌套的子查詢以及不少預約的功能函數。git

鏈接查詢

關係型數據庫的一個特色就是,多張表之間存在關係,以至於咱們能夠鏈接多張表進行查詢操做,因此鏈接查詢會是關係型數據庫中最多見的操做。程序員

鏈接查詢主要分爲三種,交叉鏈接、內鏈接和外鏈接,咱們一個個說。github

一、交叉鏈接數據庫

交叉鏈接其實鏈接查詢的第一個階段,它簡單表現爲兩張表的笛卡爾積形式,具體例子:bash

image

若是你沒學過數學中的笛卡爾積概念,你能夠這樣簡單的理解這裏的交叉鏈接:微信

兩張表的交叉鏈接就是一個鏈接合併的過程,T1 表中的每一行會分別與 T2 表的每一行作一個組合鏈接並單獨生成一行。例如 T1 的第一行會與 T2 的第一行合併生成一行,會與 T2 的第二行再合併生成一行,T2 的第三行合併生成一行,接着再以 T1 的第二行爲基礎重複上述動做。函數

應該不難理解,咱們看實現交叉鏈接的語法是什麼樣的。學習

交叉鏈接使用關鍵字 CROSS JOIN 進行鏈接,例如:ui

select * from table1 cross join table2
複製代碼

也能夠按照 ANSI SQL:1989 規範中指定的,使用逗號進行交叉鏈接,例如:

select * from table1,table2
複製代碼

經過交叉鏈接,咱們能夠兩張表的數據進行一個結合,可是你會發現同時也產生了不少冗餘的垃圾數據行,因此咱們每每也會結合 where 子句對結果集進行一個條件篩選。

例如咱們有這麼兩張表:

Students表:

+----+--------+------+----------+
| id | name   | age  | departId |
+----+--------+------+----------+
|  1 | 小明   |   12 |        1 |
|  2 | 胖虎   |   13 |        4 |
|  3 | 小新   |   15 |        2 |
|  4 | 曹操   | 1000 |        3 |
|  5 | 王安石 | 2000 |        5 |
|  6 | 杜甫   | 2000 |        2 |
+----+--------+------+----------+
複製代碼

departs 表:

+----+----------------+
| id | depart         |
+----+----------------+
|  1 | 信息工程學院   |
|  2 | 文學院         |
|  3 | 化工學院       |
|  4 | 外國語學院     |
|  5 | 數學與統計學院 |
+----+----------------+
複製代碼

若是咱們如今須要查詢一個學生的學院信息,咱們就須要鏈接兩個表,而咱們的交叉鏈接會爲咱們產生太多冗餘數據行,咱們可使用 where 子句對笛卡爾積後的結果集進行一個條件篩選。

select * from students,departs
where students.departId = departs.id
複製代碼

這樣就過濾了那些冗餘的垃圾數據行,獲得咱們須要的有效數據。

+----+--------+------+----------+----+----------------+
| id | name   | age  | departId | id | depart         |
+----+--------+------+----------+----+----------------+
|  1 | 小明   |   12 |        1 |  1 | 信息工程學院   |
|  3 | 小新   |   15 |        2 |  2 | 文學院         |
|  6 | 杜甫   | 2000 |        2 |  2 | 文學院         |
|  4 | 曹操   | 1000 |        3 |  3 | 化工學院       |
|  2 | 胖虎   |   13 |        4 |  4 | 外國語學院     |
|  5 | 王安石 | 2000 |        5 |  5 | 數學與統計學院 |
+----+--------+------+----------+----+----------------+
複製代碼

可是咱們仍然須要說一下,雖然交叉鏈接容許結合 where 子句過濾冗餘數據,可是笛卡爾積自己就形成了很大的資源消耗,對於這種狀況,內鏈接會有更好效率體現,一樣能實現該需求。

總歸一句,笛卡爾積式的交叉鏈接謹慎使用。

二、內鏈接

內鏈接也能夠理解爲條件鏈接,它使用關鍵字 INNER JOIN 鏈接兩張表並使用 ON 篩選器篩選組合合適的數據行。基本語法以下:

select * from table1 inner join table2 on [條件]

咱們一樣以上述的 students 和 departs 表舉例子,看這條 SQL:

select * from students
 inner join departs
 on students.departId = departs.id;
複製代碼

會獲得一樣的結果:

+----+--------+------+----------+----+----------------+
| id | name   | age  | departId | id | depart         |
+----+--------+------+----------+----+----------------+
|  1 | 小明   |   12 |        1 |  1 | 信息工程學院   |
|  3 | 小新   |   15 |        2 |  2 | 文學院         |
|  6 | 杜甫   | 2000 |        2 |  2 | 文學院         |
|  4 | 曹操   | 1000 |        3 |  3 | 化工學院       |
|  2 | 胖虎   |   13 |        4 |  4 | 外國語學院     |
|  5 | 王安石 | 2000 |        5 |  5 | 數學與統計學院 |
+----+--------+------+----------+----+----------------+
複製代碼

咱們也說了,雖然交叉鏈接也實現一樣的效果,但實現原理是不一樣的,效率也是不同的,交叉鏈接經過笛卡爾積返回結果集再結合 where 子句剔除冗餘數據行,而內鏈接的 ON 篩選器工做在笛卡爾積過程當中,只有符合條件才能合併生成新的數據行。

這二者的效率是不一樣的,內鏈接的效率顯然是大於等於交叉鏈接的,因此咱們也建議了儘可能使用內鏈接取代交叉鏈接的使用。

三、外鏈接

咱們說內鏈接主要是兩個步驟的結合,笛卡爾積加 ON 篩選器,通常狀況下也是內鏈接使用的最爲頻繁。外鏈接實際上是基於內鏈接的兩個步驟,額外新增了另外一個步驟,進一步苛刻化查詢操做,咱們依然以上述的學生表和學院表營造這麼一個場景:

咱們的學生表中保存了學生的我的信息以及所屬的學院外鍵編號,假設其中有部分學生的學院還未分配,即爲NULL。

那麼我如今須要查詢出全部的學生及其所屬部門信息,包括那些未知學院信息的學生,請問你怎麼作?

這個問題的核心點在於,我不只要知足鏈接條件成功合併的數據行,還要那些未成功匹配的行,也就是說學生表的全部行都得出現。

不賣關子了,使用左外鏈接便可實現:

咱們該一下 students 表中數據,departs 表中數據不變:

+----+------+------+----------+
| id | name | age  | departId |
+----+------+------+----------+
|  1 | 小明 |   12 |        1 |
|  2 | 胖虎 |   13 |     NULL |
|  3 | 小新 |   15 |        2 |
|  4 | 李白 |  200 |     NULL |
+----+------+------+----------+
複製代碼

咱們執行 SQL :

select * from
 students left outer join departs
 on students.departId = departs.id;
複製代碼

獲得結果:

+----+------+------+----------+------+--------------+
| id | name | age  | departId | id   | depart       |
+----+------+------+----------+------+--------------+
|  1 | 小明 |   12 |        1 |    1 | 信息工程學院 |
|  2 | 胖虎 |   13 |     NULL | NULL | NULL         |
|  3 | 小新 |   15 |        2 |    2 | 文學院       |
|  4 | 李白 |  200 |     NULL | NULL | NULL         |
+----+------+------+----------+------+--------------+
複製代碼

你看,左鏈接至關於以左表爲基準,成功鏈接匹配的就列出其對應的學院信息,不能匹配的就填充爲 NULL。

固然,若是你想以右表爲基準,你可使用右鏈接,關鍵詞 right outer join/on。

除此以外,還有一種全外鏈接,這種模式下沒有以誰爲基準,兩邊表的全部行都得出現。咱們舉個例子:

image

兩邊的表都在看,本身哪些行成功的條件匹配了,哪些沒有,沒有成功匹配的行會在最後強制出現,未匹配的字段賦值爲 NULL。

這就是外鏈接的本質,但願你理解了。

子查詢

子查詢,顧名思義就是嵌套的別的查詢語句中的查詢,由於不少時候查詢不是一蹴而就的,每每是須要一箇中間結果集做一個過渡的,而咱們的子查詢就是用於這種中間結果集過渡。

一、出如今選擇列表中

這種狀況並很少見,但也是一種使用場景,能夠應用在 insert,update,delete 和 select 語句中,咱們分別來看。

insert:

insert into students
 values(5,'yang',(select avg(id) from departs),1);
複製代碼

咱們向 students 表中插入一條數據,age 字段的值爲 departs 表 id 字段的平均值,這個例子自己沒多大意義,可是我爲你演示的是語法。

update:

update students set age=(select avg(id) from departs)
  where students.id = 1;
複製代碼

記住,子查詢必定要使用小括號括起來,強調一種優先級,否則會與外查詢產生衝突報錯。

delete:

delete from students
 where id = (select avg(id) from departs)
複製代碼

select 語句的子查詢相似,再也不贅述了。再次強調下,這種模式下使用子查詢並很少見,不要刻意爲了裝逼寫成這樣,你會被打的!

二、含有in和exists的子查詢

in 這個關鍵字相信你們也不陌生,咱們以前介紹 select 基本查詢的時候介紹過,它強調的是一種集合的概念,能夠視做一種邏輯運算符。

好比:

select * from students
  where id in(2,3,4,5,6)
複製代碼

咱們換成子查詢就變成:

select * from students
  where id in(select id from departs)
複製代碼

這時的子查詢返回的是一個集合,而再也不是一個常量。

exists 帶來的子查詢主要思路:將主查詢的數據,放到子查詢中作條件驗證,根據驗證結果(TRUE 或 FALSE)來決定主查詢的數據結果是否得以保留

只要子查詢有返回行,即返回 true,不然返回 false。

基本語法以下:

select [select_list] from [table]
where [not] exists [子查詢]
複製代碼

看起來挺複雜,咱們舉個例子你就明白了:

以咱們的學生表來講,每一個學生都存儲了一個學院字段信息,經過這個字段的值能夠具體查到學院的名字,那麼假如現學生表中有大量陳腐數據,無效或爲空的學院 id 對應的數據都是有些有問題的數據,現須要查出這些數據

departs 表以下:

+----+----------------+
| id | depart         |
+----+----------------+
|  1 | 信息工程學院   |
|  2 | 文學院         |
|  3 | 化工學院       |
|  4 | 外國語學院     |
|  5 | 數學與統計學院 |
+----+----------------+
複製代碼

students 表以下:

+----+------+------+----------+
| id | name | age  | departId |
+----+------+------+----------+
|  1 | 小明 |   12 |        1 |
|  2 | 小紅 |   13 |        2 |
|  3 | 王菲 |   14 |       10 |
|  4 | 張三 |   23 |     NULL |
+----+------+------+----------+
複製代碼

顯然,學生表中的第三第四行數據有問題,他們的學院 id 非法,咱們須要使用 SQL 找到這兩條數據。

select * from students
where not exists
(select * from departs where students.departId = departs.id)
複製代碼

執行 SQL,看看結果:

+----+------+------+----------+
| id | name | age  | departId |
+----+------+------+----------+
|  3 | 王菲 |   14 |       10 |
|  4 | 張三 |   23 |     NULL |
+----+------+------+----------+
複製代碼

首先外查詢拿到 students 表的全部數據,where 子句會遍歷每一行,執行子查詢過濾篩選,若是整個 where 子句返回爲 true,表明該行記錄有效應該被查詢出來,不然將拋棄該行,繼續遍歷。

關於子查詢暫時句介紹到這,基本核心的內容都已經附帶例子的介紹了,什麼嵌套子查詢不過是多套了一層而已,相信你可以理解,這裏再也不贅述了。

經常使用函數

SQL 規範中定義了不少的函數方法,它們按照應用場景不一樣能夠劃分爲如下類別,聚合函數、日期時間函數、字符串函數,邏輯函數等等,咱們抽取幾個較爲頻繁使用的方法介紹下,其他的留待你們自行研究。

聚合函數:

  • AVG(column_name):求取結果集某一列的平均值,若是某條記錄行該字段NULL,將不參與平均值計算
  • COUNT(column_name):統計結果集中數據記錄行數,即統計有多少條數據
  • MAX(column_name):返回結果集中某列的最大值
  • SUM(colunmn_name):求和結果集某列
  • MIN(column_name):返回結果集中某列的最小值
  • 除此以外,還有一些求標準差,方差

聚合函數的一個特色是,它每每是對整個結果集進行了某種數學運算並返回一個常量數值而非集合。

日期時間函數:

因爲日期函數在不一樣的數據實現來講,不少相同功能的函數具備不一樣的函數名稱,咱們這裏僅以 MySql 來介紹這些函數,給你一個宏觀印象,不一樣的數據庫之間只不過語法差別,搜索引擎就能夠解決。

  • NOW():返回當前日期時間,精確到時分秒
  • CURDATE():返回當前的日期
  • CURTIME():返回當前的時間
  • DATA(日期時間/日期表達式):該函數用於提取參數中的日期部分,參數可爲一個時分秒的日期對象,或字符串表達式
  • EXTRACT(unit FROM date):取出日期時間中指定部分,unit 可取值年、月、日、時分秒等等
  • DATE_ADD(date,INTERVAL expr type):爲指定參數的時間日期添加一個時間間隔
  • DATEDIFF(date1,date2):返回兩個日期時間之間相隔的天數
  • DATE_FORMAT(date,format):按照指定格式輸出指定的日期時間

咱們詳細說一下最後一個函數,這個函數用於格式化輸出一個日期時間對象,format 爲指定的格式,取值以下:

image

image

舉個例子:

select date_format(now(),'年:%Y-月:%m-日:%d');

執行 SQL,將輸出:

年:2019-月:02-日:25

雖然很奇怪的輸出格式,但我要說明的是這種可定製化的日期格式輸出函數。

數學函數:

  • ABS(column_name):求絕對值
  • PI():返回常量 3.141593
  • 還有一些正弦、餘弦,正切,平方根等函數

除此以外,還有不少函數,例如一些操做字符串、文本等等函數,這裏不一一贅述了,用到的時候嘗試性的搜一搜,看有沒有預約義的函數可以解決你的當下需求。

那麼,繁雜的一篇又結束了,自認爲乾貨仍是很足的,着重講了比較重要的鏈接查詢和子查詢,不知道你有啥想法呢,歡迎你和我討論!

關注公衆不迷路,一個愛分享的程序員。
公衆號回覆「1024」加做者微信一塊兒探討學習!
每篇文章用到的全部案例代碼素材都會上傳我我的 github
github.com/SingleYam/o…
歡迎來踩!

YangAM 公衆號
相關文章
相關標籤/搜索