基本查詢
MySQL通用程式設計第二篇
一 SQL查詢基本結構
SQL查詢是由三個基本語句構成,select
,from
和where
,更多的語句也是在這個基礎上進行拓展。from
後面新增模型關係,在這些關係上進行where
和select
語句中指定的運算,然後產生一個關係作為結果,查詢的結果是一個元組。
二 單關係模型查詢
我們使用如下結構構建我們的表結構和資料。
# 使用`use + 庫名`切換資料庫 use db1; # 部門表 create table department ( department_name varchar(20), buildingvarchar(15), budgetnumeric(12, 0), primary key (department_name) ); # 產品表 create table product ( # 用int型別 改成自動增長的主鍵 product_idint primary key auto_increment, titlevarchar(20) not null, creditsnumeric(8, 0), department_name varchar(20), foreign key (department_name) references department (department_name) on update cascade on delete cascade ); # 人員表 create table staff ( staff_idvarchar(10), namevarchar(15) not null, ageint, # 關聯的外來鍵與被關聯外來鍵資料型別應該一致 product_idint, department_name varchar(20), primary key (staff_id), foreign key (product_id) references product (product_id), foreign key (department_name) references department (department_name) on update cascade on delete cascade ); insert into department(department_name, building, budget) values ('市場部', '市場部大樓', 35000000), ('研發部', '研發部大樓', 20000000), ('人事部', '人事部大樓', 1000000); insert into product(title, credits, department_name) values ('1號產品', 888888, '研發部'), ('2號產品', 7777, '研發部'), ('3號產品', 999999, '研發部'), ('4號產品', 100000, '市場部'), ('5號產品', 5000000, '人事部'); insert into staff(staff_id, name, age, department_name,product_id) values ('1', 'Albert', 18, '市場部',1), ('2', 'James', 35, '市場部',2), ('3', '劉德華', 50, '研發部',3), ('4', '張學友', 35, '人事部',4), ('5', '孫悟空', 500, '人事部',5);
單模型關係基本的查詢語句使用起來非常簡單,就像講話一樣方便。
# 1 找出所有的產品 select title from product; # 2 找出負責所有的產品的部分 select department_name from product; # 這樣看到的結果會有重複,保留重複元組在大部分的資料庫軟體中是預設的,如異,可以使用all指明不重複 select all department_name from product; # 我們希望看到的結果肯定是沒有重複的,在必要的地方使用distinct來完成基本去重 select distinct department_name from product; # 3 select中可以使用 + - * / 算數運算,這只是查詢結果的的改變,不會影響模型關係的任何變化 select budget + 10000000 from department; select budget - 800000 from department; select budget * 1.1 from department; select budget * 2 from department; # 4 where允許只選出滿足特定條件的元組 # (1) 查出年齡超過100的員工 select name from staff where age > 100; # (2) 查出年齡超過30的市場部人員 select name from staff where age > 30 and department_name = '市場部';
三 多關係模型查詢
1. 多表查詢基本形式
依然是上面的表結構,這裡面有三個模型關係,我們可以是使用非常簡單的查詢語句select * from 表名
查詢出每個表的資料如下:
department_name | building | budget |
---|---|---|
人事部 | 人事部大樓 | 1000000 |
市場部 | 市場部大樓 | 35000000 |
研發部 | 研發部大樓 | 20000000 |
product_id | title | credits | department_name |
---|---|---|---|
1 | 1號產品 | 888888 | 研發部 |
2 | 2號產品 | 7777 | 研發部 |
3 | 3號產品 | 999999 | 研發部 |
4 | 4號產品 | 100000 | 市場部 |
5 | 5號產品 | 5000000 | 人事部 |
staff_id | name | age | product_id | department_name |
---|---|---|---|---|
1 | Albert | 18 | 1 | 市場部 |
2 | James | 35 | 2 | 市場部 |
3 | 劉德華 | 50 | 3 | 研發部 |
4 | 張學友 | 35 | 4 | 人事部 |
5 | 孫悟空 | 500 | 5 | 人事部 |
如果我們需要查詢出所有的員工名字以及他們負責的產品和所在的部門,就需要找一個關聯關係進行跨表查詢。
select distinct name, title, staff.department_name from department, product, staff where staff.department_name = product.department_name;
2. 查詢語句的理解
再來看一下select
,from
和where
三個語句的作用:
- select語句用於列出查詢結果所需要的屬性
- from語句是查詢求值中需要訪問的關係模型
- where語句是作用在from語句關係之上的限制要求
where限制要求是可選項,如果沒有where語句,則限制要求預設為True。
儘管查詢語句必須以select
,from
,where
這樣的次序書寫,但理解查詢所代表的運算最容易的方式是按照運算的順序來看:首先是from,然後是where,最後是select。
3. 查詢過程說明
查詢過程是迭代迴圈,可能在這個過程中不同關係模型中會出現相同的屬性名,所以我們在屬性名前面加上關係名作為說明,表示該屬性來自於哪個關係。如果是單個關係模型,我們通常會去掉關係名,因為這樣不會造成任何混淆。查詢結果是求笛卡爾積,如果我們我們不指定條件,使用select * from department,product,staff
語句,那麼即使是我們測試的資料量這麼小,得出的結果也會使你眼花繚亂,而且得出的這些組合也是沒有意義的,where語句就是限制笛卡爾積所建立的組合,只留下那些對所需答案有意義的組合。
四 自然連線
1. 自然連線說明
在多關係模型查詢中我們往往需要多個表的組合資訊,匹配條件是屬性名相同,為了簡化SQL開發者的工作,我們可以使用自然連線
作用在模型關係之上,自然連線會自動的將多關係模型中的所有元組進行連線,重要的是:自然連線只考慮那些在多關係模型中都出現的屬性相同的元組對。
# 查詢每個部門所負責的產品 # 不實用自然連線 select product.department_name, title from department, product where department.department_name = product.department_name; # 使用自然連線 select product.department_name, title from department natural join product;
2. 多關係自然連線
自然連線可以兩個關係連線,也可以多個關係連線。
# 查出一個人負責一個產品的人,產品和所在部門,然後給他漲工資 select name, title, department_name from staff natural join department natural join product;
自然連線的結果依然是關係,所以在自然連線之後的結果依然可以像模型關係那樣使用在from語句中。
select name, title from staff natural join department, product;
3. 自然連線揚長避短
自然連線會只考慮那些在多關係模型中都出現的屬性相同的元組對,也就是說會考慮多關係模型中所有屬性相同的元組對,而在有些時候,我們並不需要所有的元組對。為了發揚自然連線的有點,同時避免不必要的相等的屬性帶來的限制,SQL提供了一種自然連線的構造形式,允許用書來指定需要哪些屬性相等,我們使用using
語句來完成,這時是限制性連線而不是自然連線,所以natural
關鍵字就不再需要了。
select name, title from staff natural join product join department using (department_name);
五 SQL附加運算
1. 更名運算
資料庫設計者為了查詢方便,有些欄位名會使用一樣的,對於呼叫者在複雜查詢中就可能會混淆。而有些時候這個欄位名又會很長,呼叫者使用不方便,這時我們可以使用as對屬性名做更名。
select department_name as Dname from department;
as可以使用在select中也可以使用在from中
select D.department_name as Dname, title as T from department as D, product as P where D.department_name = P.department_name;
需要注意的是:在一些SQL版本,尤其是Oracle中,不允許在from中使用as
,但是也有同樣的功能,所以這並不影響大局,Oracle中會把select department_name as Dname
語句寫成select department_name Dname
,幸運的是,在MySQL中as
也可以省略,所以影響不大(把以上語句所有as都去掉依然可以正常執行)。
2. 字串運算
(1) SQL標準
在SQL標準中,字串的相等運算是大小寫敏感的,所以在表示式staff.name = staff.Name
的結果是假,然而在一些特定的資料庫,如MySQL
和SQL Server
中,預設並不區分大小寫,所以返回結果可能為真,但是這種預設方式是可以在資料庫級或者特定屬性級做修改的。SQL中還允許在字串上有變種函式,如upper(s)
,lower(s)
和trim(s)去除空格
。
(2) like匹配
在字串上可以使用like
操作符來實現模式匹配。
-
百分號
%
:匹配任意字串 -
下劃線
_
:匹配任意一個字元,模式匹配中大小寫是敏感的,例如:Pro%
匹配任何以‘Pro’開頭的字串,% Pro %
匹配任何包含‘Pro’的字串。 -
三個下劃線
___
:匹配只包含三個字元的字串 -
___%
:匹配至少包含三個字元的字串
為了是模式中能夠包含特殊模式的字元(百分號或者下劃線),SQL中允許定義轉義字元,使用的時候轉義字元直接放在特殊字元前面,在like
比較運算中使用escape
關鍵字來定義轉義字元。
# \ 為定義的轉義字元, # 轉義第一個%為字串,匹配所有以'abc%d'開頭的字串 like 'abc\%d %' escape '\' # 第一個\為轉義字元,匹配所有以''abc\d''開頭的字串 like 'abc\\d %' escape '\'
SQL中還允許使用not like
比較運算搜尋不匹配項。
# 匹配所有不是以以'abc%d'開頭的字串 not like 'abc\%d %' escape '\'
(3) 正則匹配
SQL可以支援使用REGEXP
來進行正則匹配,用法和Python,PHP等語言非常類似,這裡我們給出幾個簡單的示例。
select * from staff where name REGEXP 'A.'; select * from staff where name REGEXP '^A'; select * from staff where name REGEXP 't$'; select * from staff where name REGEXP '[abc]'; select * from staff where name REGEXP '[^abc]'; select * from staff where name REGEXP '[a|b|c]';
3. 序列運算
(1) 基本排序
SQL為使用者提供了一種對關係中元組顯示次序的控制,使用order by
語句可以時候查詢結果中的元組按照排列順序顯示。
# 按照年齡大小排序,預設是升序 select name from staff order by age; # 新增desc改為降序 select name from staff order by age desc; # 按照字元編碼表排序,預設是從前往後,asc可以省略 select name from staff order by name asc; # 從後往前排序 select name from staff order by name desc;
(2) 時間排序
除此之外,我們常用的還有按照時間排序,這些我們先介紹以下用於儲存時間的資料型別。
- date:日期,包括年月日
- time:一天當中的時間,包括時分秒
- timestamp:data與time的組合,包括年月日和時分秒
除此之外,SQL中定義了一些函式獲取當前的時間和日期。
select now()
create table time1 ( namechar(10), data_time date ); create table time2 ( namechar(10), time_time time ); create table time3 ( namechar(10), timestamp_time timestamp ); insert into time1 (name, data_time) values ('1', '2001-04-25'), ('2', '2021-04-25'), ('3', '2011-01-25'), ('4', '2001-12-02'), ('5', '2001-04-05'), ('6', '2001-04-11'); # 插入資料的時候可以隨意一些,但是儲存的時候還是按照規範2001-04-11儲存 insert into time1 (name, data_time) values ('1', '2001-04-5'), ('2', '2001-4-11'); insert into time2 (name, time_time) values ('1', '09:30:00'), ('2', '21:4:11'), ('3', '00:4:11'), ('4', '21:14:11'), ('5', '21:42:11'), ('6', '21:47:11'); insert into time3 (name, timestamp_time) values ('1', '2001-04-5 09:30:00'), ('2', '2011-04-5 09:30:00'), ('4', '2011-04-5 01:30:00'), ('5', '2011-04-5 10:30:00'), ('6', '2011-04-5 01:30:01'); select * from time1 order by data_time; select * from time1 order by data_time desc; select * from time2 order by time_time; select * from time2 order by time_time desc; select * from time3 order by timestamp_time; select * from time3 order by timestamp_time desc; select current_date; select current_time; select localtime; select now(); select current_timestamp; select localtimestamp;
4. 比較運算
(1) between比較
為了簡化where語句,SQL提供了between
比較運算子來說明一個值小於等於某個值,同時打不等於另外一個值。
use db1; # 不支援 30 <= age <= 100 select name from staff where age <= 100 and age >= 30; select name from staff where age between 30 and 100;
同理,我們還是可以使用not between
來做排除篩選
select name from staff where age not between 30 and 100;
(2) 分量比較
在SQL中使用(v1,v2,v3,...,vn)來表示一個分量的值分別為v1,v2,v3,vn的n維元組,在元組上可以使用比較運算,按照字典的順序進行一對一比較,比如(a1,a2) <= (b1,b2)
,則代表a1<=b1
並且a2<=b2
,同理,比較相當的時候也是一樣的。
# 查出研發部負責3號產品的人 select name from staff, product where (product.title, staff.department_name) = ('3號產品', '研發部');
5. 集合運算
(1) 集合運算說明
SQL中有語句union
,intersect
和except
分別對應數學集合中並,交,差運算。
(2) 並運算
與select語句不同的是,union語句會自動去除重複。
select name from staff where staff.product_id = 1; (select name from staff, product where staff.product_id = 1) union (select name from staff, product where staff.product_id = 2);
如果我們希望保留重複,需使用union all
代替union
。
(select name from staff, product where staff.product_id = 1) union all (select name from staff, product where staff.product_id = 2);
(3) 交運算
非常遺憾的是MySQL中沒有交運算的instersect
語句,但是這樣的語句在Oracle,PostgreSQL和SQL Server中都是有的。
# 適用於Oracle,PostgreSQL和SQL Server (select name from staff where staff.department_name = '市場部'); instersect (select name from staff where staff.product_id = 2); # instersect 語句會自動去重,如果希望顯示全部結果,在後面新增一個all即可 # MySQL中等價用法可以用分量比較 select name from staff where (staff.product_id, staff.department_name) = (2, '市場部');
(4) 差運算
MySQL中也沒有差運算的except
,但是在PostgreSQL和SQL Server中有,Oracle中使用MINUS
語句來實現同樣的功能,並運算和交運算都很好理解,差運算這裡說明一下:指的是前一個結果關係中有而後一個結果關係中沒有,也就是前一個比後一個多的部分,即前一個減去後一個,這也就是差的來源。
# PostgreSQL和SQL Server (select name from staff where staff.department_name = '市場部'); except (select name from staff where staff.product_id = 2); # 同理,自動去重,可以加all顯示全部
MySQL中類似於差運算的語句將會在我們下一章節的內容中講解。
6. 空值運算
在生產環境中,資料庫出現空值是很常見的,空值運算也給我們帶來了特殊的問題。如果在比較運算中出現1 > null
這樣的語句,由於我們不知道空值代表的是什麼,所以也發評定結果是為真還是為假,但是根據我們的邏輯語句來推理,如果1 > null
為真,那麼not (1 > null)
就應該為假,反之,亦然。但是這卻並沒有什麼意義,資料庫設計者為了程式的健壯性,將涉及空值的任何比較運算的結果視為unknown
,這是除了true
和false
之外的第三個邏輯。由於where語句可以使用and
,or
和not
做邏輯運算,所以unknown
也被擴充套件到這三個關鍵字上面。
-
and:
ture and unknown
的結果是unknown
,false and unknown
的結果是false,unknown and unknown
的結果是unknown
。 -
or:
ture or unknown
的結果是true,false or unknown
的結果是unknown
,unknown or unknown
的結果是unknown
。 -
not:
not unknown
的結果是unknown
。
如果是在where語句中對一個元組計算出false或者unknown,那麼該元組自然不能被加入到結果中。在where語句中可以使用特殊關鍵字null
來測試空值,也允許使用is unknown
來測試一個表示式的結果是否為unknown,它們兩種的用法前面新增not
自然也是可以使用的。
# 插入空值 insert into staff(staff_id, name, product_id) values ('6', '小龍女', 1); select * from staff; # 找空值 select * from staff where age is null; select * from staff where age is not null; # 條件為unknown select * from staff where name = '小龍女' and age > 15; # 認為條件正確,取其結果 select * from staff where (name = '小龍女' and age > 15) is unknown; # unknown is True 自然是false,無結果 select * from staff where (name = '小龍女' and age > 15) is true; # 認為條件不正確,取反 select * from staff where (name = '小龍女' and age > 15) is FALSE;