MySQL數(shù)據(jù)庫設計與查詢語句

1.1 今日目標

  1. 理解實體之間的關系
  2. 理解繪制E-R圖
  3. 理解三范式
  4. 理解范式和性能之間的關系
  5. 能夠查詢表中的數(shù)據(jù)
  6. 理解聚合函數(shù)
  7. 理解模糊查詢
  8. 理解分組查詢

1.2 數(shù)據(jù)庫基本概念

1.2.1 關系

兩個表的公共字段。

1.2.2 行

也稱記錄,也稱實體。

1.2.3 列

也稱字段,也稱屬性。

就表結構而言,表分為行和列;
接表結構而言,分為記錄和字段;
就面向?qū)ο蠖?,一個記錄就是一個實體,一個字段就是一個屬性。

1.2.4 數(shù)據(jù)冗余

相同的數(shù)據(jù)存儲在不同的地方。

腳下留心:

  1. 冗余只能減少,不能杜絕。
  2. 較少冗余的方法就是分表。
  3. 為了減少數(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ù)庫設計的步驟

數(shù)據(jù)庫設計的步驟

1.4.1 數(shù)據(jù)庫設計的具體步驟

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

1.4.2繪制E-R圖

E-R:(Entity-Relationship)實體關系圖

E-R圖

繪制E-R圖

1.4.3 將E-R圖轉(zhuǎn)成表

  1. 實體轉(zhuǎn)成表,屬性轉(zhuǎn)成字段
  2. 如果沒有合適的字段做主鍵,給表添加一個自動增長列做主鍵。

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圖

繪制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ù)庫表

數(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 ('北京','上海');

練習:

  1. 查找學號是s25301,s25302,s25303的學生:
mysql> select * from stu where stuno in ('s25301','s25302','s25303');
  1. 查找年齡是18,19,20的學生:
mysql> select * from stu where stuage in(18,19,20);;
  1. 查找不是北京和上海的學生:
mysql> select * from stu where stuaddress not in ('北京','上海');

1.6.6 between ... and ... | not between ... and ...

查找某個范圍的記錄。

  1. 查找年齡在18~20之間的學生:
mysql> select * from stu where stuage>=18 and stuage<=20;   # 方法一

mysql> select * from stu where stuage between 18 and 20;   # 方法二
  1. 查找年齡不在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. _ [下劃線] 表示任意一個字符
  2. % 表示任意字符

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 查詢語句中的選項

查詢語句中的選項有兩個:

  1. all:顯示所有數(shù)據(jù) 【默認】

  2. 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的選項有兩個:

  1. all:顯示所有數(shù)據(jù)

  2. 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的注意事項

  1. union兩邊的select語句的字段個數(shù)必須一致

  2. union兩邊的select語句的字段名可以不一致,最終按第一個select語句的字段名。

  3. union兩邊的select語句中的數(shù)據(jù)類型可以不一致。

?著作權歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務。

相關閱讀更多精彩內(nèi)容

  • 這世界又飄起了小雨 時間也開始轉(zhuǎn)誤 只是不在乎 于你有何聯(lián)系 有沒有你 一樣陰晴 我在乎的。。。 是什么?
    訾_差矣閱讀 202評論 2 1
  • 習慣的養(yǎng)成大概需要兩點,一個是動力,一個靠意志力。 動力是取決于當時的心態(tài)的,當環(huán)境改變,身體狀態(tài)改變,動力就不再...
    just4progress閱讀 430評論 0 0
  • 姓名:袁磊 公司:海南蔚藍時代實業(yè)有限公司 組別:第361期感謝二組 【日精進打卡第 162 天】 【知~學習】 ...
    三石_58fb閱讀 250評論 0 0
  • 具體問題得具體分析 人性本沒有善惡,是環(huán)境造就了善惡;而底層的貧窮會徹底激發(fā)人性的惡。
    YKCHHR閱讀 163評論 0 0

友情鏈接更多精彩內(nèi)容