1.1 今日目標
- 理解實體之間的關(guān)系
- 理解繪制E-R圖
- 理解三范式
- 理解范式和性能之間的關(guān)系
- 能夠查詢表中的數(shù)據(jù)
- 理解聚合函數(shù)
- 理解模糊查詢
- 理解分組查詢
1.2 數(shù)據(jù)庫基本概念
1.2.1 關(guān)系
兩個表的公共字段悔耘。
1.2.2 行
也稱記錄,也稱實體藕届。
1.2.3 列
也稱字段百炬,也稱屬性。
就表結(jié)構(gòu)而言蔽挠,表分為行和列汇四;
接表結(jié)構(gòu)而言踏烙,分為記錄和字段;
就面向?qū)ο蠖郧辏粋€記錄就是一個實體妇智,一個字段就是一個屬性。
1.2.4 數(shù)據(jù)冗余
相同的數(shù)據(jù)存儲在不同的地方氏身。
腳下留心:
- 冗余只能減少巍棱,不能杜絕。
- 較少冗余的方法就是分表蛋欣。
- 為了減少數(shù)據(jù)查詢的麻煩拉盾,允許數(shù)據(jù)有一定的冗余。
1.2.5 數(shù)據(jù)完整性
正確性:數(shù)據(jù)類型正確
準確性:數(shù)據(jù)范圍要準確
正確性+準確性=數(shù)據(jù)完整性
思考:學(xué)生的年齡是整型豁状,輸入1000歲捉偏,正確性和準確性如何?
答:正確的泻红,但不準確
思考:年齡是整形的夭禽,收入了字符串,正確性和準確性如何谊路?
答:不正確
1.3 實體和實體之間的關(guān)系
1.3.1 一對多 (1:N)
主表中的一條記錄對應(yīng)從表中的多條記錄讹躯。一對多和多對一是一樣的。
問題:如何實現(xiàn)一對多缠劝?
答:主鍵和非主鍵之間建立關(guān)系潮梯。
1.3.2 一對一 (1:1)
主表中的一條記錄對應(yīng)從表中的一條記錄。
問:如何實現(xiàn)一對一惨恭?
答:主鍵和主鍵之間建立關(guān)系秉馏。
問:一對一兩個表完全可以用一個表實現(xiàn),為什么還要分成兩個表脱羡?
答:在字段數(shù)量很多的情況下萝究,數(shù)據(jù)量也就很大,每次查詢都需要檢索大量的數(shù)據(jù)锉罐,這樣效率低下帆竹。我們可以將所有字段分為兩個部分,“常用字段”和“不常用字段”脓规,這樣對大部分查詢者來說效率提高了栽连。這也叫表的垂直分割。
1.3.3 多對多(N:M)
主表中的一條記錄對應(yīng)從表中的多條記錄侨舆,從表中的一條記錄對應(yīng)主表中的多條記錄秒紧。
問:如何實現(xiàn)多對多舷暮?
答:建立第三張表來保存關(guān)系。
1.4 數(shù)據(jù)庫設(shè)計的步驟
1.4.1 數(shù)據(jù)庫設(shè)計的具體步驟
- 收集信息:與該系統(tǒng)有關(guān)人員進行交流噩茄、坐談下面,充分理解數(shù)據(jù)庫需要完成的任務(wù)
- 標識對象(實體-Entity)標識數(shù)據(jù)庫要管理的關(guān)鍵對象或?qū)嶓w
- 標識每個實體的屬性(Attribute
- 標識對象之間的關(guān)系(Relationship)
- 將模型轉(zhuǎn)換成數(shù)據(jù)庫
- 規(guī)范化
1.4.2繪制E-R圖
E-R:(Entity-Relationship)實體關(guān)系圖
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ù)庫需要存放貼子相關(guān)信息,如貼子內(nèi)容衅谷、標題等椒拗;
用戶可以對發(fā)帖進行回復(fù);
論壇版塊管理:后臺數(shù)據(jù)庫需要存放各個版塊信息获黔,如版主蚀苛、版塊名稱、貼子數(shù)等玷氏;
1.4.2 標識的對象
參與的對象有:用戶堵未、發(fā)的帖子、跟帖盏触、板塊
1.4.3 標識對象的屬性
1.4.4 建立關(guān)系渗蟹,繪制E-R圖
1.4.5 將E-R圖轉(zhuǎn)出表結(jié)構(gòu)
1.5 數(shù)據(jù)規(guī)范化
Codd博士定義了6個范式來規(guī)范化數(shù)據(jù)庫,范式由小到大來約束赞辩,范式越高冗余越小雌芽,但表的個數(shù)也越多。實驗證明辨嗽,三范式是性價比最高的世落。
1.5.1 第一范式:確保每列原子性
第一范式確保每個字段不可再分。
問:地址包含省召庞、市岛心、縣、地區(qū)篮灼,是否需要拆分?
答:如果僅僅起地址的作用徘禁,不需要統(tǒng)計诅诱,可以不拆分。如果有按地區(qū)統(tǒng)計的功能送朱,需要拆分娘荡。
在實際項目中干旁,建議拆分。
1.5.2 第二范式:非鍵字段必須依賴于鍵字段
一個表只能描述一件事炮沐。
思考:如下表設(shè)計是否合理争群?
1.5.3 第三范式:消除傳遞依賴
在所有的非鍵字段中,不能有傳遞依賴大年。
思考:下列設(shè)計是否滿足第三范式换薄?
答:不滿足,因為語文和數(shù)學(xué)確定了翔试,總分就確定了轻要。
多學(xué)一招:上面的設(shè)計不滿足第三范式,但是高考分數(shù)表就是這樣設(shè)計的垦缅,為什么冲泥?
答:高考分數(shù)峰值訪問量非常大,這時候就是性能更重要壁涎。當性能和規(guī)范化沖突的時候凡恍,我們首選性能。這就是“反三范式”怔球。
1.5.4 數(shù)據(jù)庫設(shè)計的例題
1.5.4.1 需求
1.公司承擔多個工程項目咳焚,每一項工程有:工程號、工程名稱庞溜、施工人員等
2.公司有多名職工革半,每一名職工有:職工號、姓名流码、性別又官、職務(wù)(工程師、技術(shù)員)等
3.公司按照工時和小時工資率支付工資漫试,小時工資率由職工的職務(wù)決定(例如六敬,技術(shù)員的小時工資率與工程師不同)
1.5.4.3 工資表
1.5.4.3 將工資表轉(zhuǎn)成數(shù)據(jù)庫表
1.5.4.4 這個表存在的問題
A: 新人入職需要虛擬一個項目
B: 職務(wù)更改,小時工資率可能會忘記更改驾荣,造成數(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)
多學(xué)一招: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 #返回空記錄
思考:如何查找北京和上海的學(xué)生?
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
上面的查詢上海和北京的學(xué)生的SQL可以通過in語句來實現(xiàn):
mysql> select * from stu where stuaddress in ('北京','上海');
練習(xí):
- 查找學(xué)號是s25301,s25302,s25303的學(xué)生:
mysql> select * from stu where stuno in ('s25301','s25302','s25303');
- 查找年齡是18,19,20的學(xué)生:
mysql> select * from stu where stuage in(18,19,20);;
- 查找不是北京和上海的學(xué)生:
mysql> select * from stu where stuaddress not in ('北京','上海');
1.6.6 between ... and ... | not between ... and ...
查找某個范圍的記錄景描。
- 查找年齡在18~20之間的學(xué)生:
mysql> select * from stu where stuage>=18 and stuage<=20; # 方法一
mysql> select * from stu where stuage between 18 and 20; # 方法二
- 查找年齡不在18~20之間的學(xué)生:
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。
查找缺考的學(xué)生:
mysql> select * from stu where ch is null or math is null; # 查找缺考的人
查找參加考試的學(xué)生:
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)
查找姓張的同學(xué):
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ù)學(xué)成績升序排列
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 分組查詢
將查詢的結(jié)果分組,分組查詢目的在于統(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麸俘、查詢字段是普通字段辩稽,只取第一個值(不同設(shè)置可能會報錯)
通過group_concat()函數(shù)將同一組的值連接起來顯示:
mysql> select group_concat(stuname),stusex from stu group by stusex;
+-------------------------------------+--------+
| group_concat(stuname) | stusex |
+-------------------------------------+--------+
| 李斯文,諸葛麗麗,梅超風(fēng),Tabm | 女 |
| 張秋麗,李文才,歐陽俊雄,爭青小子,Tom | 男 |
+-------------------------------------+--------+
2 rows in set (0.00 sec)
多學(xué)一招:【了解】
1、分組后的結(jié)果默認會按升序排列顯示
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ù)庫中的表是一個二維表逞泄,返回的結(jié)果是一張二維表,既然能在數(shù)據(jù)庫的二維表中進行查詢拜效,能否在結(jié)果集的二維表上繼續(xù)進行查詢喷众?
答:可以,having條件就是在結(jié)果集上繼續(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 |
+--------+----------+--------+--------+---------+------------+------+------+
多學(xué)一招:limit在update和delete語句中也是可以使用的愈涩。
1.6.15 查詢語句中的選項
查詢語句中的選項有兩個:
all:顯示所有數(shù)據(jù) 【默認】
distinct:去除結(jié)果集中重復(fù)的數(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語句結(jié)果集縱向聯(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 | 梅超風(fēng) |
| s25320 | Tom |
| s25321 | Tabm |
| 1 | 李白 |
| 2 | 張秋麗 |
+--------+----------+
例題:查詢上海的男生和北京的女生:
mysql> select stuname,stuaddress,stusex from stu where (stuaddress='上海' and stusex='男') or (stuaddress='北京' and stusex='女');
+---------+------------+--------+
| stuname | stuaddress | stusex |
+---------+------------+--------+
| 張秋麗 | 上海 | 男 |
| 梅超風(fēng) | 北京 | 女 |
+---------+------------+--------+
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 |
+---------+------------+--------+
| 張秋麗 | 上海 | 男 |
| 梅超風(fēng) | 北京 | 女 |
+---------+------------+--------+
2 rows in set (0.02 sec)
1.7.2 union的選項
union的選項有兩個:
all:顯示所有數(shù)據(jù)
distinct:去除重復(fù)的數(shù)據(jù)【默認】
mysql> select name from go1 union select stuname from stu;
+----------+
| name |
+----------+
| 李白 |
| 張秋麗 |
| 李文才 |
| 李斯文 |
| 歐陽俊雄 |
| 諸葛麗麗 |
| 爭青小子 |
| 梅超風(fēng) |
| Tom |
| Tabm |
+----------+
默認是去重復(fù)的望抽,顯示全部加all:
mysql> select name from go1 union all select stuname from stu; # all不去重復(fù)記錄
+----------+
| name |
+----------+
| 李白 |
| 張秋麗 |
| 張秋麗 |
| 李文才 |
| 李斯文 |
| 歐陽俊雄 |
| 諸葛麗麗 |
| 爭青小子 |
| 梅超風(fēng) |
| Tom |
| Tabm |
+----------+
1.7.3 union的注意事項
union兩邊的select語句的字段個數(shù)必須一致
union兩邊的select語句的字段名可以不一致加矛,最終按第一個select語句的字段名。
union兩邊的select語句中的數(shù)據(jù)類型可以不一致煤篙。