1.1 今日目標
- 理解實體之間的關系
- 理解繪制E-R圖
- 理解三范式
- 理解范式和性能之間的關系
- 能夠查詢表中的數(shù)據(jù)
- 理解聚合函數(shù)
- 理解模糊查詢
- 理解分組查詢
1.2 數(shù)據(jù)庫基本概念
1.2.1 關系
兩個表的公共字段。
1.2.2 行
也稱記錄,也稱實體。
1.2.3 列
也稱字段,也稱屬性。
就表結構而言,表分為行和列;
接表結構而言,分為記錄和字段;
就面向?qū)ο蠖?,一個記錄就是一個實體,一個字段就是一個屬性。
1.2.4 數(shù)據(jù)冗余
相同的數(shù)據(jù)存儲在不同的地方。
腳下留心:
- 冗余只能減少,不能杜絕。
- 較少冗余的方法就是分表。
- 為了減少數(shù)據(jù)查詢的麻煩,允許數(shù)據(jù)有一定的冗余。
1.2.5 數(shù)據(jù)完整性
正確性:數(shù)據(jù)類型正確
準確性:數(shù)據(jù)范圍要準確
正確性+準確性=數(shù)據(jù)完整性
思考:學生的年齡是整型,輸入1000歲,正確性和準確性如何?
答:正確的,但不準確
思考:年齡是整形的,收入了字符串,正確性和準確性如何?
答:不正確
1.3 實體和實體之間的關系
1.3.1 一對多 (1:N)
主表中的一條記錄對應從表中的多條記錄。一對多和多對一是一樣的。

問題:如何實現(xiàn)一對多?
答:主鍵和非主鍵之間建立關系。
1.3.2 一對一 (1:1)
主表中的一條記錄對應從表中的一條記錄。

問:如何實現(xiàn)一對一?
答:主鍵和主鍵之間建立關系。
問:一對一兩個表完全可以用一個表實現(xiàn),為什么還要分成兩個表?
答:在字段數(shù)量很多的情況下,數(shù)據(jù)量也就很大,每次查詢都需要檢索大量的數(shù)據(jù),這樣效率低下。我們可以將所有字段分為兩個部分,“常用字段”和“不常用字段”,這樣對大部分查詢者來說效率提高了。這也叫表的垂直分割。
1.3.3 多對多(N:M)
主表中的一條記錄對應從表中的多條記錄,從表中的一條記錄對應主表中的多條記錄。

問:如何實現(xiàn)多對多?
答:建立第三張表來保存關系。
1.4 數(shù)據(jù)庫設計的步驟

1.4.1 數(shù)據(jù)庫設計的具體步驟
- 收集信息:與該系統(tǒng)有關人員進行交流、坐談,充分理解數(shù)據(jù)庫需要完成的任務
- 標識對象(實體-Entity)標識數(shù)據(jù)庫要管理的關鍵對象或?qū)嶓w
- 標識每個實體的屬性(Attribute
- 標識對象之間的關系(Relationship)
- 將模型轉(zhuǎn)換成數(shù)據(jù)庫
- 規(guī)范化
1.4.2繪制E-R圖
E-R:(Entity-Relationship)實體關系圖


1.4.3 將E-R圖轉(zhuǎn)成表
- 實體轉(zhuǎn)成表,屬性轉(zhuǎn)成字段
- 如果沒有合適的字段做主鍵,給表添加一個自動增長列做主鍵。
1.4.4 例題
1.4.1 項目需求
BBS論壇的基本功能:
用戶注冊和登錄,后臺數(shù)據(jù)庫需要存放用戶的注冊信息和在線狀態(tài)信息;
用戶發(fā)貼,后臺數(shù)據(jù)庫需要存放貼子相關信息,如貼子內(nèi)容、標題等;
用戶可以對發(fā)帖進行回復;
論壇版塊管理:后臺數(shù)據(jù)庫需要存放各個版塊信息,如版主、版塊名稱、貼子數(shù)等;
1.4.2 標識的對象
參與的對象有:用戶、發(fā)的帖子、跟帖、板塊
1.4.3 標識對象的屬性

1.4.4 建立關系,繪制E-R圖

1.4.5 將E-R圖轉(zhuǎn)出表結構
1.5 數(shù)據(jù)規(guī)范化
Codd博士定義了6個范式來規(guī)范化數(shù)據(jù)庫,范式由小到大來約束,范式越高冗余越小,但表的個數(shù)也越多。實驗證明,三范式是性價比最高的。
1.5.1 第一范式:確保每列原子性
第一范式確保每個字段不可再分。

問:地址包含省、市、縣、地區(qū),是否需要拆分?
答:如果僅僅起地址的作用,不需要統(tǒng)計,可以不拆分。如果有按地區(qū)統(tǒng)計的功能,需要拆分。
在實際項目中,建議拆分。
1.5.2 第二范式:非鍵字段必須依賴于鍵字段
一個表只能描述一件事。

思考:如下表設計是否合理?

1.5.3 第三范式:消除傳遞依賴
在所有的非鍵字段中,不能有傳遞依賴。

思考:下列設計是否滿足第三范式?

答:不滿足,因為語文和數(shù)學確定了,總分就確定了。
多學一招:上面的設計不滿足第三范式,但是高考分數(shù)表就是這樣設計的,為什么?
答:高考分數(shù)峰值訪問量非常大,這時候就是性能更重要。當性能和規(guī)范化沖突的時候,我們首選性能。這就是“反三范式”。
1.5.4 數(shù)據(jù)庫設計的例題
1.5.4.1 需求
1.公司承擔多個工程項目,每一項工程有:工程號、工程名稱、施工人員等
2.公司有多名職工,每一名職工有:職工號、姓名、性別、職務(工程師、技術員)等
3.公司按照工時和小時工資率支付工資,小時工資率由職工的職務決定(例如,技術員的小時工資率與工程師不同)
1.5.4.3 工資表

1.5.4.3 將工資表轉(zhuǎn)成數(shù)據(jù)庫表

1.5.4.4 這個表存在的問題
A: 新人入職需要虛擬一個項目
B: 職務更改,小時工資率可能會忘記更改,造成數(shù)據(jù)不完整
C: 有人離職,刪除記錄后,工程也沒有了
1.5.4.5 規(guī)范化表
第一步:這個表滿足第一范式
第二步:這個表不是描述了一件事情

第三步:是否滿足第三范式


1.6 查詢語句
語法:select [選項] 列名 [from 表名] [where 條件] [group by 分組] [order by 排序][having 條件] [limit 限制]
1.6.1 字段表達式
mysql> select '鋤禾日當午';
+------------+
| 鋤禾日當午 |
+------------+
| 鋤禾日當午 |
+------------+
mysql> select 10*10;
+-------+
| 10*10 |
+-------+
| 100 |
+-------+
通過as給字段取別名:
mysql> select '鋤禾日當午' as content;
+------------+
| content |
+------------+
| 鋤禾日當午 |
+------------+
1 row in set (0.00 sec)
mysql> select 10*10 as result;
+--------+
| result |
+--------+
| 100 |
+--------+
1 row in set (0.00 sec)
多學一招:as可以省略
mysql> select 10*10 result;
+--------+
| result |
+--------+
| 100 |
+--------+
1 row in set (0.00 sec)
1.6.2 from子句
from:來自,from后面跟的是數(shù)據(jù)源。數(shù)據(jù)源可以有多個。返回笛卡爾積。
插入測試表:
mysql> create table t1(
-> id int,
-> name varchar(10)
-> );
Query OK, 0 rows affected (0.05 sec)
mysql> create table t2(
-> field1 varchar(10),
-> field2 varchar(10)
-> );
Query OK, 0 rows affected (0.00 sec)
mysql> insert into t1 values (1,'tom'),(2,'berry');
Query OK, 2 rows affected (0.00 sec)
Records: 2 Duplicates: 0 Warnings: 0
mysql> insert into t2 values ('333','333'),('444','444');
Query OK, 2 rows affected (0.02 sec)
Records: 2 Duplicates: 0 Warnings: 0
測試多個數(shù)據(jù)源:
mysql> select * from t1,t2; # 返回笛卡爾積
+------+-------+--------+--------+
| id | name | field1 | field2 |
+------+-------+--------+--------+
| 1 | tom | 333 | 333 |
| 2 | berry | 333 | 333 |
| 1 | tom | 444 | 444 |
| 2 | berry | 444 | 444 |
+------+-------+--------+--------+
4 rows in set (0.00 sec)
1.6.3 dual表
dual表是一個偽表。在有些特定情況下,沒有具體的表的參與,但是為了保證select語句的完整又必須要一個表名,這時候就使用偽表。
mysql> select 10*10 as result from dual; #dual表是用來保證select語句的完整性。
+--------+
| result |
+--------+
| 100 |
+--------+
1.6.4 where子句
where后面跟的是條件,在數(shù)據(jù)源中進行篩選。返回條件為真的記錄。
MySQL支持的運算符:
| 運算符 | 含義 |
|---|---|
| > | 大于 |
| < | 小于 |
| >= | 大于等于 |
| <= | 小于等于 |
| = | 等于 |
| != | 不等于 |
| and | 與 |
| or | 或 |
| not | 非 |
mysql> select * from stu where stusex='男'; # 查找性別是男的記錄
mysql> select * from stu where stuage>=20; # 查找年齡不低于20的記錄
思考:如下代碼輸出什么:
select * from stu where 1 # 返回所有數(shù)據(jù)庫
select * from stu where 0 #返回空記錄
思考:如何查找北京和上海的學生?
mysql> select * from stu where stuaddress='上海' or stuaddress='北京';
+--------+---------+--------+--------+---------+------------+------+------+
| stuNo | stuName | stuSex | stuAge | stuSeat | stuAddress | ch | math |
+--------+---------+--------+--------+---------+------------+------+------+
| s25301 | 張秋麗 | 男 | 18 | 1 | 北京 | 80 | NULL |
| s25302 | 李文才 | 男 | 31 | 3 | 上海 | 77 | 76 |
| s25303 | 李斯文 | 女 | 22 | 2 | 北京 | 55 | 82 |
| s25320 | Tom | 男 | 24 | 8 | 北京 | 65 | 67 |
+--------+---------+--------+--------+---------+------------+------+------+
1.6.5 in | not in
上面的查詢上海和北京的學生的SQL可以通過in語句來實現(xiàn):
mysql> select * from stu where stuaddress in ('北京','上海');
練習:
- 查找學號是s25301,s25302,s25303的學生:
mysql> select * from stu where stuno in ('s25301','s25302','s25303');
- 查找年齡是18,19,20的學生:
mysql> select * from stu where stuage in(18,19,20);;
- 查找不是北京和上海的學生:
mysql> select * from stu where stuaddress not in ('北京','上海');
1.6.6 between ... and ... | not between ... and ...
查找某個范圍的記錄。
- 查找年齡在18~20之間的學生:
mysql> select * from stu where stuage>=18 and stuage<=20; # 方法一
mysql> select * from stu where stuage between 18 and 20; # 方法二
- 查找年齡不在18~20之間的學生:
mysql> select * from stu where stuage<18 or stuage>20; #方法一
mysql> select * from stu where not (stuage>=18 and stuage<=20); #方法二
mysql> select * from stu where stuage not between 18 and 20; #方法三
1.6.7 is null | is not null
腳下留心:查詢一個為空的字段不能用等于,必須用is null。
查找缺考的學生:
mysql> select * from stu where ch is null or math is null; # 查找缺考的人
查找參加考試的學生:
mysql> select * from stu where ch is not null and math is not null;
1.6.8 聚合函數(shù)
| 函數(shù)名 | 函數(shù)功能 |
|---|---|
| sum() | 求和 |
| avg() | 求平均值 |
| max() | 求最大值 |
| min() | 求最小值 |
| count | 求記錄數(shù) |
求語文總分、語文平均分、語文最高分、語文最低分、總?cè)藬?shù):
mysql> select sum(ch) '語文總分',avg(ch) '語文平均分', max(ch) '語文最高分', min(ch) '語文最低分',count(*) '總?cè)藬?shù)' from stu;
+--------------+-----------------+-----------------+-----------------+-----------+
| 語文總分 | 語文平均分 | 語文最高分 | 語文最低分 | 總?cè)藬?shù) |
+--------------+-----------------+-----------------+-----------------+-----------+
| 597 | 74.6250 | 88 | 55 | 9 |
+--------------+-----------------+-----------------+-----------------+-----------+
1.6.9 通配符
- _ [下劃線] 表示任意一個字符
- % 表示任意字符
1.6.10 模糊查詢(like)
查找姓張的同學:
mysql> select * from stu where stuname like '張%' ;
1.6.10 order by排序
asc:升序【默認】
desc:降序
mysql> select * from stu order by ch desc; # 語文成績降序排列
mysql> select * from stu order by math asc; # 數(shù)學成績升序排列
mysql> select * from stu order by math; # 默認升序排列
多列排序:
#年齡升序,成績降序
mysql> select *,(ch+math) as '總分' from stu order by stuage asc,(ch+math) desc;
思考如下代碼表示什么含義:
select * from stu order by stuage desc,ch desc; #年齡降序,語文降序
select * from stu order by stuage desc,ch asc; #年齡降序,語文升序
select * from stu order by stuage,ch desc; #年齡升序、語文降序
select * from stu order by stuage,ch; #年齡升序、語文升序
1.6.12 group by 分組查詢
將查詢的結果分組,分組查詢目的在于統(tǒng)計數(shù)據(jù)。
# 按性別分組,顯示每組的平均年齡
mysql> select avg(stuage) as '年齡',stusex from stu group by stusex;
+---------+--------+
| 年齡 | stusex |
+---------+--------+
| 22.7500 | 女 |
| 25.4000 | 男 |
+---------+--------+
2 rows in set (0.00 sec)
# 按地區(qū)分組,每個地區(qū)的平均年齡
mysql> select avg(stuage) as '年齡',stuaddress from stu group by stuaddress;
+---------+------------+
| 年齡 | stuaddress |
+---------+------------+
| 31.0000 | 上海 |
| 21.3333 | 北京 |
| 27.0000 | 天津 |
| 23.0000 | 河北 |
| 23.0000 | 河南 |
+---------+------------+
5 rows in set (0.00 sec)
腳下留心:
1、如果是分組查詢,查詢字段必須是分組字段和聚合函數(shù)。
2、查詢字段是普通字段,只取第一個值(不同設置可能會報錯)
通過group_concat()函數(shù)將同一組的值連接起來顯示:
mysql> select group_concat(stuname),stusex from stu group by stusex;
+-------------------------------------+--------+
| group_concat(stuname) | stusex |
+-------------------------------------+--------+
| 李斯文,諸葛麗麗,梅超風,Tabm | 女 |
| 張秋麗,李文才,歐陽俊雄,爭青小子,Tom | 男 |
+-------------------------------------+--------+
2 rows in set (0.00 sec)
多學一招:【了解】
1、分組后的結果默認會按升序排列顯示
2、也是可以使用desc實現(xiàn)分組后的降序
多列分組:
mysql> select stuaddress,stusex,avg(stuage) from stu group by stuaddress,stusex;
+------------+--------+-------------+
| stuaddress | stusex | avg(stuage) |
+------------+--------+-------------+
| 上海 | 男 | 31.0000 |
| 北京 | 女 | 22.0000 |
| 北京 | 男 | 21.0000 |
| 天津 | 男 | 27.0000 |
| 河北 | 女 | 23.0000 |
| 河南 | 女 | 23.0000 |
+------------+--------+-------------+
6 rows in set (0.00 sec)
1.6.13 having條件
思考:數(shù)據(jù)庫中的表是一個二維表,返回的結果是一張二維表,既然能在數(shù)據(jù)庫的二維表中進行查詢,能否在結果集的二維表上繼續(xù)進行查詢?
答:可以,having條件就是在結果集上繼續(xù)進行篩選。
例題:
mysql> select * from stu where stusex='男' having stuaddress='北京';
+--------+-----------+--------+--------+---------+------------+------+------+
| stuNo | stuName | stuSex | stuAge | stuSeat | stuAddress | ch | math |
+--------+-----------+--------+--------+---------+------------+------+------+
| s25301 | 張秋麗 | 男 | 18 | 1 | 北京 | 80 | NULL |
| s25320 | Tom | 男 | 24 | 8 | 北京 | 65 | 67 |
+--------+-----------+--------+--------+---------+------------+------+------+
2 rows in set (0.00 sec)
1.6.14 limit
語法:limit 起始位置,顯示長度。
mysql> select * from stu limit 0,2; # 從0的位置開始,取兩條數(shù)據(jù)
+--------+---------+--------+--------+---------+------------+------+------+
| stuNo | stuName | stuSex | stuAge | stuSeat | stuAddress | ch | math |
+--------+---------+--------+--------+---------+------------+------+------+
| s25301 | 張秋麗 | 男 | 18 | 1 | 北京 | 80 | NULL |
| s25302 | 李文才 | 男 | 31 | 3 | 上海 | 77 | 76 |
+--------+---------+--------+--------+---------+------------+------+------+
2 rows in set (0.00 sec)
mysql> select * from stu limit 2,2; # 從2的位置開始,取兩條數(shù)據(jù)
+--------+----------+--------+--------+---------+------------+------+------+
| stuNo | stuName | stuSex | stuAge | stuSeat | stuAddress | ch | math |
+--------+----------+--------+--------+---------+------------+------+------+
| s25303 | 李斯文 | 女 | 22 | 2 | 北京 | 55 | 82 |
| s25304 | 歐陽俊雄 | 男 | 28 | 4 | 天津 | NULL | 74 |
+--------+----------+--------+--------+---------+------------+------+------+
多學一招:limit在update和delete語句中也是可以使用的。
1.6.15 查詢語句中的選項
查詢語句中的選項有兩個:
all:顯示所有數(shù)據(jù) 【默認】
distinct:去除結果集中重復的數(shù)據(jù)
mysql> select distinct stuaddress from stu;
+------------+
| stuaddress |
+------------+
| 上海 |
| 天津 |
| 河南 |
| 河北 |
| 北京 |
+------------+
5 rows in set (0.00 sec)
1.7 union(聯(lián)合)
插入測試數(shù)據(jù):
mysql> create table GO1(
-> id int primary key,
-> name varchar(20));
Query OK, 0 rows affected (0.06 sec)
mysql> insert into Go1 values (1,'李白'),(2,'張秋麗');
Query OK, 2 rows affected (0.02 sec)
Records: 2 Duplicates: 0 Warnings: 0
1.7.1 union的使用
作用:將多個select語句結果集縱向聯(lián)合起來
語法:select 語句 union [選項] select 語句 union [選項] select 語句
mysql> select stuno,stuname from stu union select id,name from Go1;
+--------+----------+
| stuno | stuname |
+--------+----------+
| s25301 | 張秋麗 |
| s25302 | 李文才 |
| s25303 | 李斯文 |
| s25304 | 歐陽俊雄 |
| s25305 | 諸葛麗麗 |
| s25318 | 爭青小子 |
| s25319 | 梅超風 |
| s25320 | Tom |
| s25321 | Tabm |
| 1 | 李白 |
| 2 | 張秋麗 |
+--------+----------+
例題:查詢上海的男生和北京的女生:
mysql> select stuname,stuaddress,stusex from stu where (stuaddress='上海' and stusex='男') or (stuaddress='北京' and stusex='女');
+---------+------------+--------+
| stuname | stuaddress | stusex |
+---------+------------+--------+
| 張秋麗 | 上海 | 男 |
| 梅超風 | 北京 | 女 |
+---------+------------+--------+
2 rows in set (0.00 sec)
mysql> select stuname,stuaddress,stusex from stu where stuaddress='上海' and stusex='男' union select stuname,stuaddress,stusex from stu where stuaddress='北京' and stusex='女';
+---------+------------+--------+
| stuname | stuaddress | stusex |
+---------+------------+--------+
| 張秋麗 | 上海 | 男 |
| 梅超風 | 北京 | 女 |
+---------+------------+--------+
2 rows in set (0.02 sec)
1.7.2 union的選項
union的選項有兩個:
all:顯示所有數(shù)據(jù)
distinct:去除重復的數(shù)據(jù)【默認】
mysql> select name from go1 union select stuname from stu;
+----------+
| name |
+----------+
| 李白 |
| 張秋麗 |
| 李文才 |
| 李斯文 |
| 歐陽俊雄 |
| 諸葛麗麗 |
| 爭青小子 |
| 梅超風 |
| Tom |
| Tabm |
+----------+
默認是去重復的,顯示全部加all:
mysql> select name from go1 union all select stuname from stu; # all不去重復記錄
+----------+
| name |
+----------+
| 李白 |
| 張秋麗 |
| 張秋麗 |
| 李文才 |
| 李斯文 |
| 歐陽俊雄 |
| 諸葛麗麗 |
| 爭青小子 |
| 梅超風 |
| Tom |
| Tabm |
+----------+
1.7.3 union的注意事項
union兩邊的select語句的字段個數(shù)必須一致
union兩邊的select語句的字段名可以不一致,最終按第一個select語句的字段名。
union兩邊的select語句中的數(shù)據(jù)類型可以不一致。