1. 程式人生 > >3.偏頭痛楊的mysql教學系列之查詢篇

3.偏頭痛楊的mysql教學系列之查詢篇

前戲

查詢是MYSQL中最精彩的一章,資料儲存在資料庫中,如果一直存著也沒有意義, 我們需要使用SQL語句將資料查詢出來,然後把這些資料返回給需要使用的地方。 使用select語句來進行資料表的查詢功能, select語句用於從一個或多個數據表中選定特定行、特定列的交集。

單表查詢

每次查詢只查一張表,以及相關的語法,查詢的基礎。 查詢語句主要以這個句式為基準:“SELECT * FROM 表名 WHERE 查詢條件”

SELECT後面用於確定選擇哪些列顯示,WHERE後面用於確定選擇哪些行顯示。 沒有WHERE條件預設顯示所有行,使用*表示顯示所有列。

#查詢所有列所有行
SELECT
* FROM t_user; #查詢符合條件的行 SELECT * FROM t_user WHERE user_id > 2; #使用算術運算子,形成算術表示式,可以用在數值、日期、列之間的運算。 SELECT user_id+10 AS user_id FROM t_user WHERE user_id + 1 > 2; #字串連線使用concat()函式。 SELECT CONCAT(user_name ,'good') AS user_name FROM t_user; SELECT CONCAT(user_name ,user_id) AS user_name FROM
t_user; #去掉重複值使用DISTINCT關鍵字,要緊接SELECT關鍵字後面。 #如果後面是多列組合,則去掉多列組合的重複項,而不是單列的重複項。 SELECT DISTINCT user_name,user_age FROM t_user; #查詢範圍,user_id>=1並且user_id<=3。 SELECT * FROM t_user WHERE user_id BETWEEN 1 AND 3; #查詢範圍,括號裡的每個值符合都會查詢出來。 SELECT * FROM t_user WHERE user_id IN (1,2,3); #模糊查詢,萬用字元_代表一個字元,%代表多個字元,如果不希望_與%代表萬用字元就需要使用\轉義。
#查詢包含f的名字 SELECT * FROM t_user WHERE user_name LIKE '%f%'; #查詢f開頭的名字 SELECT * FROM t_user WHERE user_name LIKE 'f%'; #查詢f結尾的名字 SELECT * FROM t_user WHERE user_name LIKE '%f'; #查詢前三個字母是fre,第五個字母是k的名字 SELECT * FROM t_user WHERE user_name LIKE 'fre_k'; #查詢只有4個字母的名字 SELECT * FROM t_user WHERE user_name LIKE '____'; #查詢包含_的名字 SELECT * FROM t_user WHERE user_name LIKE '%\_%'; #查詢日期為2018-01-13 14:32的資料,秒隨意。 SELECT * FROM t_user WHERE create_date LIKE '2018-01-13 14:32%' #查詢姓名以m及p開頭 SELECT * FROM t_user WHERE user_name LIKE 'm%' OR user_name LIKE 'p%'; #查詢為null的資料,注意null不是空字串。 SELECT * FROM t_user WHERE user_age IS NULL; #查詢條件為"或"、"且"、"非"邏輯運算子。注意邏輯運算子與算術運算子的優先順序,括號>比較運算子>not>and>or SELECT * FROM t_user WHERE user_age = 1 AND user_name = 'alex1'; SELECT * FROM t_user WHERE user_age = 1 OR user_name = 'alex2'; SELECT * FROM t_user WHERE NOT user_age = 1; SELECT * FROM t_user WHERE user_age <> 1; SELECT * FROM t_user WHERE user_name = 'alex2' OR (user_age <> 1 AND user_name LIKE '%f%'); #按某列大小進行排序,如果沒有排序則預設按照插入順序顯示,排序後預設按照升序顯示 #允許對多列進行排序,當第一排序列全部滿足後,第一排序列有重複的資料才會使用第二排序列進行排序。 SELECT * FROM t_user ORDER BY user_age DESC; SELECT * FROM t_user ORDER BY user_age ASC; SELECT * FROM t_user ORDER BY user_age; SELECT * FROM t_user ORDER BY user_age ASC,user_name DESC; #分頁,從2條資料開始後面的三條,不包括第2條 SELECT * FROM t_user ORDER BY user_age LIMIT 2,3; #分頁,查詢0-2條資料,包括第2條 SELECT * FROM t_user ORDER BY user_age LIMIT 2; #正則表示式: SELECT * FROM t_user WHERE user_name REGEXP '^alex'; SELECT * FROM t_user WHERE user_name REGEXP '^[0-9]'; SELECT * FROM t_user WHERE user_name REGEXP '[0-9]$';

函式

函式用於進行資料處理或複雜運算,通過對一組資料進行計算,得到最終需要輸出的結果。 函式一般會有一個或多個引數輸入,最終只有一個返回值輸出。 函式可以出現在SQL語句的各個位置。函式可以巢狀使用, 一個函式的返回值可以當作另一個函式的引數。

函式主要分為單行函式與多行函式兩大類。 單行函式對每行輸入值單獨計算,每行得到一個計算結果返回給使用者。 多行函式對多行輸入值整體計算,最後只會得到一個結果。 多行函式又稱為分組函式、聚合&聚集函式,主要用於完成一些統計功能。 主要的單行函式分類:日期函式、字元函式、數值函式、轉換函式等等 。

常見函式如下:

#日期函式,注意應用伺服器時間與資料庫時間是兩個時間。

#增加7天
SELECT DATE_ADD(NOW(), INTERVAL 7 DAY); 
SELECT ADDDATE('1998-01-02', INTERVAL 7 DAY);
SELECT ADDDATE('1998-01-02', 7);

#增加2個月
SELECT DATE_ADD(NOW(), INTERVAL 2 MONTH); 
SELECT DATE_ADD('1988-02-02', INTERVAL 2 MONTH); 

#減少7天
SELECT DATE_SUB(NOW(), INTERVAL 7 DAY); 

#增加12小時
SELECT DATE_ADD(NOW(), INTERVAL 12 HOUR); 

#減少12小時
SELECT DATE_SUB(NOW(), INTERVAL 12 HOUR); 

#增加10分鐘
SELECT DATE_ADD(NOW(), INTERVAL 10 MINUTE);

#減少10分鐘
SELECT DATE_SUB(NOW(), INTERVAL 10 MINUTE);

#兩個日期相減,返回兩個日期相差的秒數,引數2減引數1
SELECT TIMESTAMPDIFF(SECOND,'2018-01-13 14:30:37','2018-01-13 14:30:38' );

#得到指定日期的年月日
SELECT DATE('2015-08-05 13:00:00');

#得到指定日期的時分秒
SELECT TIME('2015-08-05 01:28:41');

#得到當前的年月日
SELECT CURDATE();

#得到當前的時分秒
SELECT CURTIME();

#今天是星期幾
SELECT (WEEKDAY(NOW())+1);

#得到當前的年月日時分秒
SELECT CURRENT_TIMESTAMP();
SELECT NOW();


#字串函式

#計算字元長度
SELECT CHAR_LENGTH(user_name) FROM t_user;
SELECT LENGTH(user_name) FROM t_user;

#從一個字串中取出一個字串,從第3個字元開始擷取,包含第3個字元
SELECT SUBSTR('alexyang',3);

#字串大小寫
SELECT UPPER('alex');
SELECT LOWER('ALEX');

#連線字串,如有任何一個引數為NULL ,則返回值為 NULL
SELECT CONCAT('a','b');

#刪除前後多餘的空格
SELECT TRIM('   alex   ');


#NULL函式

#如果引數列是null,則返回第二個引數
SELECT IFNULL(user_age,'沒有年齡') FROM t_user;

#如果引數1與引數2相等則返回null,否則返回引數1
SELECT NULLIF(user_name,'alex2') FROM t_user;

#判斷引數是否為null,如果是則返回1,後則返回0
SELECT ISNULL(user_age) FROM t_user;

#類似三目運算子,當引數1為true時返回引數2,否則返回引數3
SELECT IF(ISNULL(user_age),'沒有年齡','有年齡') FROM t_user;
SELECT IF(IS_NULLABLE='NO','√','') AS NULLABLE FROM t_xxx;

#流程控制函式

#第一種case
SELECT user_name,
CASE user_name
WHEN 'alex1' THEN '金牌使用者'
WHEN 'alex2' THEN '銀牌使用者'
ELSE '其他使用者'
END
FROM t_user;

#第二種case
SELECT user_age ,
CASE
WHEN user_age<18 THEN '未成年人'
WHEN user_age>=18 THEN '成年人'
ELSE '中年人'
END
FROM t_user;


#數值函式

#計算sin值
SELECT sin(1.57);


#加密函式

#MD5加密
SELECT MD5('111111');

分組函式&聚集函式&聚合函式

分組函式將一組記錄作為整體計算,每組記錄返回一個結果,而不是每條記錄返回一個結果。 分組函式也稱為聚集函式、聚合函式。

常見的聚合函式

函 數 名 描 述
sum 計算多行的總和,必須是數值型別,可以使用distinct表示不重複, NULL不參加計算。
avg 計算多行的平均值,必須是數值型別,可以使用distinct表示不重複, NULL不參加計算。
max 計算多行的最大值。
min 計算多行的最小值。
count 計算多行總條數,可以使用distinct表示不重複,count(*)計算NULL值,count(列名)不計算NULL值。
#計算多行總數,不計算NULL值
SELECT COUNT(*) FROM t_user;

#計算多行總數,計算NULL值
SELECT COUNT(user_age) FROM t_user;

#計算多行總數,計算NULL值,去掉重複值
SELECT COUNT(DISTINCT user_age) FROM t_user;

#計算多行的總和
SELECT SUM(user_age) FROM t_user;

#計算多行的總和,去掉重複值
SELECT SUM(DISTINCT user_age) FROM t_user;

#計算多行的最大值
SELECT MAX(user_age) FROM t_user;

#計算多行的最小值
SELECT MIN(user_age) FROM t_user;

#計算平均值,NULL值用0代替,否則NULL值不參加計算
SELECT AVG(IFNULL(user_age,0)) FROM t_user;

#COUNT可以用來統計重複資料,用到了下面的分組知識
SELECT COUNT(*) as repetitions, user_name FROM t_user GROUP BY user_name HAVING repetitions > 1;

分組group by

預設情況下聚合函式會將所有記錄當成一組對待,為了對記錄進行顯式分組, 可以使用group by子句。

group by後可以跟一個或多個列名,表明查詢結果根據一列或多列進行分組, 當一列或多列的值完全相同時,mysql會把這些記錄當成一組對待。 一般情況下group by需與聚合函式一起使用才有意義, group by對於分組中的空值都看成是相同的。

#將user_age值相同的記錄當成一組,並對每組統計總條數
SELECT user_age,COUNT(user_age) FROM t_user GROUP BY user_age;

#對多列分組,user_age+user_name值相同的記錄當成一組
SELECT user_age,user_name,COUNT(user_age) FROM t_user GROUP BY user_age,user_name;

#注意:對於沒有包含在GROUP BY也沒有包含在聚合函式中的欄位,如果在SELECT中,
#則mysql會顯示第一條記錄的值,其他資料庫可能會禁止這種語法,但mysql允許這種寫法,
#很多BUG因此產生,此處一定要注意,例如sql中的user_name,這裡的user_name是不準確的。
SELECT user_name,COUNT(user_age) FROM t_user GROUP BY user_age;

例如:select goods_id,goods_name,cat_id,max(shop_price) from goods group by cat_id; 這裡取出來的結果中的good_name是錯誤的! 因為shop_price使用了max函式,那麼它是取最大的, 而語句中使用了group by 分組,那麼goods_name並沒有使用聚合函式, 它只是cat_id下的第一個商品,並不會因為shop_price改變而改變。

對分組進行過濾,則使用having子句,後面接一個表示式, 只有表示式為true的記錄才會被過濾出來。 having子句可以接聚合函式,而where卻不行。 where限制的是行,having限制的是組。

where 比 having 更有效。

#使用having過濾分組
SELECT user_age,COUNT(user_age) FROM t_user GROUP BY user_age HAVING COUNT(user_age) >1;

注意事項

1.group by、having中使用的列名需要包含在select列表中,否則查詢資料不準確。

2.having中使用的列名不一定要包含在group by子句中。

3.order by必須跟在group by之後,如果使用having,必須要跟在group by之後,在order by之前。

4.having子句是對組進行限制,而where子句是對行進行限制,二者雖然有相似之處,但是仍有很大的不同。

5.當同時包含where,group by,having,以及集合函式時,執行順序如下: 5.1.執行where子句查詢符合條件的資料。 5.2.使用group by子句對資料進行分組。 5.3.對group by子句形成的組執行聚合函式計算每一組的值。 5.4.最後使用having子句去掉不符合條件的組。

WITH ROLLUP

WITH ROLLUP關鍵字將會在所有記錄的最後加上一條記錄。 加上的這一條記錄是上面所有記錄的總和。

#查詢結果最後多出一行,顯示count的總和
SELECT user_age,COUNT(user_age) FROM t_user GROUP BY user_age WITH ROLLUP;

GROUP_CONCAT()函式

GROUP BY關鍵字與GROUP_CONCAT()函式一起使用時, 每個分組中指定的欄位值會全部顯示出來。

#按照user_age進行分組查詢,使用GROUP_CONCAT()函式將每個分組的user_name欄位的值都顯示出來。
SELECT GROUP_CONCAT(user_name),COUNT(user_age) FROM t_user GROUP BY user_age

那麼有童鞋會問了,這個函式有什麼作用嗎? 思考一下如何刪除重複資料吧:)

多表查詢

有些時候我們要查詢的資料不僅僅是來自一張表而是兩張或者是多張表, 那我們就需要使用多表查詢。

連線查詢

注意:用於連線的列必須要加索引提高查詢效率。 連線查詢執行順序: 1.多表笛卡爾積 2.where過濾笛卡爾積 3.group by分組 4.having過濾分組 5.select列名 6.order by排序

1.交叉連線cross join

cross join ,等同於select * from a,b ,笛卡爾基(源自SQL92規範)。 返回兩個表中所有列的組合。如果左表有m行資料,右表有n行資料, 則執行CROSS JOIN將返回m*n行資料。

SELECT * FROM t_user u
CROSS JOIN
t_class c ON c.class_id = u.class_id
WHERE u.class_id = 1
2.內連線inner join

如果沒有使用ON條件的過濾,INNER JOIN和CROSS JOIN的效果是一樣的。 INNER JOIN只過濾出完全符合兩邊的資料,查詢結果是左右連線的交集, INNER關鍵字可以省略掉。

SELECT * FROM t_user u
INNER JOIN
t_class c ON c.class_id = u.class_id
WHERE u.class_id = 1

3.外連線outer join

outer關鍵字可以省略掉

3.1left (outer) join 獲取左表所有記錄,即使右表沒有對應匹配的記錄(右表相應列中填NULL)。

SELECT * FROM t_user u
LEFT JOIN
t_class c ON c.class_id = u.class_id

3.2right (outer) join 與左外連線相反,用於獲取右表所有記錄, 即使左表沒有對應匹配的記錄(並在左表相應列中填NULL)。

SELECT * FROM t_user u
RIGHT JOIN
t_class c ON c.class_id = u.class_id

3.3full join 全外連線將把兩個表中所有不滿足連線條件的記錄全部列出,MYSQL不支援,不再展開。

4.其他

4.1自連線 就是自己連線自己,查詢出表中的資料,但要求表中有2個id用於子連線, 一個"father_id"、一個"son_id"。

select b.xx_name from t_user a 
left join t_user b on a.father_id = b.son_id;

4.2自然連線 自然連線會以兩個表中的同名列作為連線條件,如果兩個表中沒有同名列, 則與交叉連線效果一樣。

SELECT * FROM t_user u
NATURAL JOIN
t_class c
WHERE u.class_id = 1

子查詢

子查詢是在查詢語句中巢狀另一個查詢語句,可以支援多層巢狀, 子查詢語句可以出現在from、where後面。

ANY、ALL

ANY和ALL可以與>、>=、<、<=、<>、=等運算子號結合使用。 與ANY結合分別表示大於、大於等於、小於、小於等於、不等於、等於其中任意一個值。 =ANY與IN作用相同。 與ALL結合分別表示大於、大於等於、小於、小於等於、不等於、等於其中全部值。

注意事項: 1.子查詢語句要用括號包起來。 2.當子查詢語句出現在FROM後面時,可為子查詢語句起別名。 3.當子查詢語句出現在WHERE後面時,如果子查詢返回單行單列記錄則可以使用各種運算子, 如果是多行單列則可以使用IN、ANY、ALL來處理, 如果是多行多列可以使用圓括號多個列組合起來,看下面的例子。

#在FROM後面使用子查詢
SELECT * FROM (SELECT * FROM t_user ) AS u WHERE u.user_age = 1;

#在WHERE後面使用子查詢
SELECT * FROM t_user WHERE class_id IN (SELECT class_id FROM t_class WHERE class_name = '向日葵一班');

#=ANY來代替IN
SELECT * FROM t_user WHERE class_id =ANY (SELECT class_id FROM t_class WHERE class_name = '向日葵一班');

#>=ANY用法,查詢大於t_class表中的class_id的所有t_user記錄
SELECT * FROM t_user WHERE class_id > ANY (SELECT class_id FROM t_class WHERE class_name = '向日葵一班')

#子查詢返回多列多行,使用圓括號
SELECT * FROM t_user WHERE (class_id,user_name) =ANY (SELECT class_id,class_name FROM t_class WHERE class_name = '向日葵一班')
無關子查詢

在外圍查詢之前執行,然後返回資料供外圍查詢使用,它和外圍查詢的聯絡僅此而已。

SELECT name,sex,id,salary FROM Employee WHERE dno IN (select dno FROM Employee where salary > 4000)

子查詢完全不依賴外圍查詢。 執行過程: 子查詢獨立執行,它的執行結果是一個部門號清單,部門清單產生後被用作外圍查詢的IN子句的引數。然後部門號清單中的內容被外圍查詢用來和外圍查詢訪問的表中的資料進行比較,生成最後的查詢結果。

相關子查詢

在該子查詢執行時要使用到外圍查詢的資料。子查詢執行結束後再將它的查詢結果返回到它的外圍查詢中,供外圍查詢比較使用。

SELECT dno Department,name Manager FROM Employee e WHERE 0 <= ANY(
     SELECT count(1) FROM Department WHERE e.id = mgrid 
)

執行過程: 1.外圍查詢從表Employee中按序讀取一條記錄中的內容。 2.接著執行子查詢,並將外圍查詢所獲得的記錄內容用於子查詢的WHERE子句中進行比較。 3.子查詢將它查詢的結果值回傳給外圍查詢的WHERE子句。 4.繼續執行外圍查詢,如果此時外圍查詢的WHERE子句為“真”,則回到第一步繼續迴圈。 (注:如果外圍查詢有24條記錄,那麼子查詢就要迴圈24次,效能較低。)

集合運算

SELECT語句查詢的結果是一個包含多條資料的結果集, 查詢結果可以進行交(intersect)、並(union)和差(minus)運算。

為了集合運算,2個結果集必須滿足如下條件: 1.兩個結果集所包含的資料列的數量必須相等。 2.兩個結果集所包含的資料列的資料型別必須一一對應。

1.union UNION運算可以把多個查詢的結果合併到一個結果集裡顯示。 預設的情況下,UNION子句不返回重複的記錄,如果想顯示所有記錄,可以加ALL。

select 語句 union select 語句
SELECT key_id,course_title,create_date,course_type FROM t_course_one2many 
UNION 
SELECT key_id,course_title,create_date,'course_type1' as course_type FROM t_course_one2one;

2.minus(mysql不支援)

select 語句 minus select 語句
select student_id,student_name from student_table minus select teacher_id,teacher_name from teacher_table;
(從學生記錄中減去與老師記錄相同的ID,姓名的記錄)
mysql並不支援,我們換成下面的:
select student_id,student_name from student_table where (student_id,student_name) not in (select teacher_id,teacher_name from teacher_table)

3.intersect(mysql不支援) select 語句 intersect select 語句 mysql用join on的形式代替吧。

總結

本篇文章的內容可謂是相當的多,查詢語句也是mysql的重頭戲, 請務必掌握單表查詢與多表查詢,尤其是join連線查詢。