您好,登錄后才能下訂單哦!
這篇文章主要講解了MySQL的基礎用法,內容清晰明了,對此有興趣的小伙伴可以學習一下,相信大家閱讀完之后會有幫助。
這篇文章主要梳理了 SQL 的基礎用法,會涉及到以下方面內容:
SQL 是我們用來最長和數據打交道的方式之一,如果按照功能劃分可分為如下 4 個部分:
平時在編寫 SQL 時,可能發現許多 SQL 大小寫不統一,雖然不會影響 SQL 的執行結果,但保持統一的書寫規范,是提高效率的關鍵,通常遵循如下的原則:
目前排名較前的 DBMS:
SELECT 一般是在學習 SQL 接觸的第一個關鍵字,基礎的內容就是不提了,這里整理常用的規范:
SELECT name AS n FROM student
SELECT '學生信息' as student_info, name FROM student
SELECT DISTINCT age FROM student
需要注意的是 DISTINCT 是對后面的所有列進行去重, 下面這種情況就會對 age 和 name 的組合進行去重。
SELECT DISTINCT age,name FROM student
如先按照 name 排序,name 相等的情況下按照 age 排序。
SELECT DISTINCT age FROM student ORDERY BY name,age DESC
SELECT DISTINCT age FROM student ORDERY BY name DESC LIMIT 5
了解了 SELECT 的執行順序,才能更好地寫出更有效率的 SQL。
對于 SELECT 順序有兩個原則:
SELECT ... FROM ... WHERE ... GROUP BY ... HAVING ... ORDER BY ...
FROM > WHERE > GROUP BY > HAVING > SELECT的字段 > DISTINCT > ORDER BY > LIMIT
SELECT DISTINCT student_id, name, count(*) as num #順序5 FROM student JOIN class ON student.class_id = class.class_id #順序1 WHERE age > 18 #順序2 GROUP BY student.class_id #順序3 HAVING num > 2 #順序4 ORDER BY num DESC #順序6 LIMIT 2 #順序7
在逐一分析下這個過程前,我們需要知道在上面的每一個步驟中都會產生一個虛擬表,然后將這個虛擬表作為下一個步驟中作為輸入,但這一過程對我們來說是不可見的:
如果涉及到函數的計算比如 sum() 等,會在 GROUP BY分組后,HAVING 分組前,進行聚集函數的計算。
涉及到表達式計算,如 age * 10 等,會在 HAVING 階段后,SELECT 階段前進行計算。
使用 SELECT 時指定明確的列來代替 SELECT * . 從而減少網絡的傳輸量。
使用 WHERE 篩選時,常有通過比較運算符,邏輯運算符,通配符三種方式。
對于比較運算符,常用的運算符如下表。
對于邏輯運算符來說,可以將多個比較運行符連接起來,進行多條件的篩選,常用的運算符如下:
需要注意的是,當 AND 和 OR 同時出現時,AND 的優先級更高會先被執行。當如果存在 () 的話,則括號的優先級最高。
使用通配符過濾:
like:(%)代表零個或多個字符,(_)只代表一個字符
和編程語言中的定義的函數一樣,SQL 同樣定義了一些函數方便使用,比如求和,平均值,長度等。
常見的函數主要分為如下四類,分類的原則是根據定義列時的數據類型:
需要注意的是,在使用字符串比較日期時,要使用 DATE 函數比較。
CAST 函數在轉換數據類型時,不會四舍五入,如果原數值是小數,在轉換到整數時會報錯。
在轉換時可以使用 DECIMAL(a,b) 函數來規定小數的精度,比如 DECIMAL(8,2) 表示精度為 8 位 - 小數加整數最多 8 位。小數后面最多為 2 位。
然后通過 SELECT CAST(123.123 AS DECIMAL(8,2)) 來轉換。
通常情況下,我們會使用聚集函數來匯總表的數據,輸入為一組數據,輸出為單個值。
常用的聚集函數有 5 個:
其中 COUNT 函數需要額外注意,具體的內容可以參考這篇。
在統計結果時,往往需要對數據按照一定條件進行分組,對應就是 GROUP BY 語句。
比如統計每個班級的學生人數:
SELECT class_id, COUNT(*) as student_count FROM student \ GROUP BY class_id;
GROUP BY 后也可接多個列名,進行分組,比如按照班級和性別分組:
SELECT class_id, sex, COUNT(*) as student_count FROM \ student GROUP BY class_id, sex;
和 WHERE 一樣,可以對分組后的數據進行篩選。區別在于 WHERE 適用于數據行,HAVING 用于分組。
而且 WHERE 支持的操作,HAVING 也同樣支持。
比如可以篩選大于2人的班級:
SELECT class_id, COUNT(*) as student_count FROM student \ GROUP BY class_id \ HAVING student_count > 20;
在一些更為復雜的情況中,往往會進行嵌套的查詢,比如在獲取結果后,該結果作為輸入,去獲取另外一組結果。
在 SQL 中,查詢可以分為關聯子查詢和非關聯子查詢。
假設有如下的表結構:
-- ---------------------------- DROP TABLE IF EXISTS `student`; CREATE TABLE `student` ( `id` int(11) NOT NULL AUTO_INCREMENT, `name` varchar(20) NOT NULL DEFAULT '', `age` int(3) NOT NULL, `sex` varchar(10) NOT NULL DEFAULT '', `class_id` int(11) NOT NULL COMMENT '班級ID', PRIMARY KEY (`id`) ) ENGINE=InnoDB AUTO_INCREMENT=9 DEFAULT CHARSET=utf8; -- ---------------------------- -- Records of Student -- ---------------------------- INSERT INTO `student` VALUES ('1', '胡一', 13, '男', '1'); INSERT INTO `student` VALUES ('3', '王阿', 11, '女', '1'); INSERT INTO `student` VALUES ('5', '王琦', 12, '男', '1'); INSERT INTO `student` VALUES ('7', '劉偉', 11, '女', '1'); INSERT INTO `student` VALUES ('7', '王意識', 11, '女', '2'); -- ---------------------------- DROP TABLE IF EXISTS `student_activities`; CREATE TABLE `student_activities` ( `id` int(11) NOT NULL AUTO_INCREMENT, `name` varchar(20) NOT NULL DEFAULT '', `stu_id` int(11) NOT NULL COMMENT '班級ID', PRIMARY KEY (`id`) ) ENGINE=InnoDB AUTO_INCREMENT=9 DEFAULT CHARSET=utf8; INSERT INTO `student_activities` VALUES ('1', '博物館', 1); INSERT INTO `student_activities` VALUES ('3, '春游', 3);
子查詢從數據表中查詢了數據結果,如果這個數據結果只執行一次,然后這個數據結果作為主查詢的條件接著執行。
這里想要查詢和胡一相同班級的同學名稱:
SELECT name FROM student WHERE class_id = \ (SELECT class_id FROM student WHERE name='胡一')
這里先查到胡一的班級,只有一次查詢,再根據該班級查找學生就是非關聯子查詢。
如果子查詢需要執行多次,即采用循環的方式,先從外部查詢開始,每次都傳入子查詢進行查詢,然后再將結果反饋給外部
再舉個例子, 比如查詢比每個班級中比平均年齡大的學生姓名信息:
SELECT name FROM student as s1 WHERE age > (SELECT AVG(age) FROM student as s2 where s1.class_id = s2.class_id)
這里根據每名同學的班級信息,查找出對應班級的平均年齡,然后做判斷。子查詢每次執行時,都需要根據外部的查詢然后進行計算。這樣的子查詢就是關聯子查詢。
在關聯子查詢中,常會和 EXISTS 一起使用。用來判斷條件是否滿足,滿足的話為 True,不滿足為 False。
比如查詢參加過學校活動的學生名稱:
SELECT NAME FROM student as s where \ EXISTS(SELECT stu_id FROM student_activities as sa where sa.stu_id=s.id)
同樣 NOT EXISTS 就是不存在的意思,滿足為 FALSE , 不滿足為 True.
比如查詢沒有參加過學校活動的學生名稱:
SELECT NAME FROM student as s where \ NOT EXISTS(SELECT stu_id FROM student_activities as sa where sa.stu_id=s.id)
可以在子查詢中,使用集合操作符,來比較結果。
還是上面查詢參加學校活動的學生名字的子查詢, 同樣可以使用 IN:
SELECT name FROM student WHERE id IN (SELECT stu_id FROM student_activities)
既然 EXISTS 和 IN 都能實現相同的功能,那么他們之間的區別是什么?
現在假設我們有表 A 和 表 B,其中 A,B 都有字段 cc,并對 cc 建立了 b+ 索引,其中 A 表 n 條記錄,B 表 m 條索引。
將其模式抽象為:
SELECT * FROM A WHERE cc IN (SELECT cc FROM B) SELECT * FROM A WHERE EXIST (SELECT cc FROM B WHERE B.cc=A.cc)
對于 EXISTS 來說,會先對外表進行逐條循環,每次拿到外表的結果后,帶入子查詢的內表中,去判斷該值是否存在。
偽代碼類似于下面:
for i in A
for j in B
if j.cc == i.cc:
return result
首先先看外表 A,每一條都需要遍歷到,所以需要 n 次。內表 B,在查詢時由于使用索引進而查詢效率變成 log(m) B+ 的樹高,而不是 m。
進而總效率:n * log(m)
所以對于 A 表的數量明顯小于 B 時,推薦使用 EXISTS 查詢。
再看 IN ,會先對內表 B 進行查詢,然后用外表 A 進行判斷,偽代碼如下:
for i in B
for j in A
if j.cc == i.cc:
return result
由于需要首先將內表所有數據查出,所以需要的次數就是 m. 再看外表 A ,由于使用了 cc 索引,可將 n 簡化至 log(n), 也就是 m * log(n).
所以對于 A 表的數據明顯大于 B 表時,推薦使用 IN 查詢。
總結一下對于 IN 和 EXISTS時,采用小表驅動大表的原則。
這里再擴展下 NOT EXISTS 和 NOT IN 的區別:
SELECT * FROM A WHERE cc NOT IN (SELECT cc FROM B) SELECT * FROM A WHERE NOT EXIST (SELECT cc FROM B WHERE B.cc=A.cc)
對于 NOT EXITS 來說,和 EXISTS 一樣,對于內表可以使用 cc 的索引。適用于 A 表小于 B 表的情況。
但對于 NOT IN 來說,和 IN 就有區別了,由于 cc 設置了索引 cc IN (1, 2, 3) 可以轉換成 WHERE cc=1 OR cc=2 OR cc=3 , 是可以正常走 cc 索引的。但對于 NOT IN 也就是轉化為 cc!=1 OR cc!=2 OR cc!=3 這時由于是不等號查詢,是無法走索引的,進而全表掃描。
也就是說,在設置索引的情況下 NOT EXISTS 比 NOT IN 的效率高。
但對于沒有索引的情況,IN 和 OR 是不同的:
一、操作不同
1、in:in是把父查詢表和子查詢表作hash連接。
2、or:or是對父查詢表作loop循環,每次loop循環再對子查詢表進行查詢。二、適用場景不同
1、in:in適合用于子查詢表數據比父查詢表數據多的情況。
2、or:or適合用于子查詢表數據比父查詢表數據少的情況。三、效率不同
1、in:在沒有索引的情況下,隨著in后面的數據量越多,in的執行效率不會有太大的下降。
2、or:在沒有索引的情況下,隨著or后面的數據量越多,or的執行效率會有明顯的下降
看完上述內容,是不是對MySQL的基礎用法有進一步的了解,如果還想學習更多內容,歡迎關注億速云行業資訊頻道。
免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。