MySQL學習-數據庫設計以及sql的進階語句

1.數據庫設計

  • 關係型數據庫建議在E-R模型的基礎上,咱們須要根據產品經理的設計策劃,抽取出來模型與關係,制定出表結構,這是項目開始的第一步python

  • 在開發中有不少設計數據庫的軟件,經常使用的如power designer,db desinger等,這些軟件能夠直觀的看到實體及實體間的關係mysql

  • 設計數據庫,多是由專門的數據庫設計人員完成,也多是由開發組成員完成,通常是項目經理帶領組員來完成sql

1.1 實體

就是咱們根據開發需求,要保存到數據庫中做爲一張表存在的事物。實體的名稱最終會變成表名數據庫

實體會有屬性,實體的屬性就是描述這個事物的內容,實體的屬性最終會在表中做爲字段存在。服務器

實體與實體之間會存在關係,這種關係通常就是根據三範式提取出來的主外鍵。數據庫設計

1.1.1 三範式

  1. 數據要保證不可分割.
  2. 數據不能冗餘(多餘).
  3. 數據不能重複.重複的數據,新建一張表存儲.

實際中關於三範式的整理函數

  • 通過研究和對使用中問題的總結,對於設計數據庫提出了一些規範,這些規範被稱爲範式(Normal Form)性能

  • 目前有跡可尋的共有8種範式,通常須要遵照3範式便可fetch

  • ◆ 第一範式(1NF):強調的是列的原子性,即列不可以再分紅其餘幾列。優化

    考慮這樣一個表:【聯繫人】(姓名,性別,電話) 若是在實際場景中,一個聯繫人有家庭電話和公司電話,那麼這種表結構設計就沒有達到 1NF。要符合 1NF 咱們只需把列(電話)拆分,即:【聯繫人】(姓名,性別,家庭電話,公司電話)。1NF 很好辨別,可是 2NF 和 3NF 就容易搞混淆。


  • ◆ 第二範式(2NF):首先是 1NF,另外包含兩部份內容,一是表必須有一個主鍵;二是沒有包含在主鍵中的列必須徹底依賴於主鍵,而不能只依賴於主鍵的一部分。

    考慮一個訂單明細表:【OrderDetail】(OrderID,ProductID,UnitPrice,Discount,Quantity,ProductName)。 由於咱們知道在一個訂單中能夠訂購多種產品,因此單單一個 OrderID 是不足以成爲主鍵的,主鍵應該是(OrderID,ProductID)。顯而易見 Discount(折扣),Quantity(數量)徹底依賴(取決)於主鍵(OderID,ProductID),而 UnitPrice,ProductName 只依賴於 ProductID。因此 OrderDetail 表不符合 2NF。不符合 2NF 的設計容易產生冗餘數據。

    能夠把【OrderDetail】表拆分爲【OrderDetail】(OrderID,ProductID,Discount,Quantity)和【Product】(ProductID,UnitPrice,ProductName)來消除原訂單表中UnitPrice,ProductName屢次重複的狀況。


  • ◆ 第三範式(3NF):首先是 2NF,另外非主鍵列必須直接依賴於主鍵,不能存在傳遞依賴。即不能存在:非主鍵列 A 依賴於非主鍵列 B,非主鍵列 B 依賴於主鍵的狀況。

    考慮一個訂單表【Order】(OrderID,OrderDate,CustomerID,CustomerName,CustomerAddr,CustomerCity)主鍵是(OrderID)。 其中 OrderDate,CustomerID,CustomerName,CustomerAddr,CustomerCity 等非主鍵列都徹底依賴於主鍵(OrderID),因此符合 2NF。不過問題是 CustomerName,CustomerAddr,CustomerCity 直接依賴的是 CustomerID(非主鍵列),而不是直接依賴於主鍵,它是經過傳遞才依賴於主鍵,因此不符合 3NF。 經過拆分【Order】爲【Order】(OrderID,OrderDate,CustomerID)和【Customer】(CustomerID,CustomerName,CustomerAddr,CustomerCity)從而達到 3NF。 *第二範式(2NF)和第三範式(3NF)的概念很容易混淆,區分它們的關鍵點在於,2NF:非主鍵列是否徹底依賴於主鍵,仍是依賴於主鍵的一部分;3NF:非主鍵列是直接依賴於主鍵,仍是直接依賴於非主鍵列。

不遵循1NF

QQ20170814-230000@2x_thumb[1]

不遵循2NF

QQ20170814-231713@2x_thumb[1]

不遵循3NF

QQ20170815-081532@2x_thumb[1]

最終表

QQ20170814-231939@2x_thumb[1]

1.1.2 E-R模型

https://www.draw.io/

  • E表示entry,實體,設計實體就像定義一個類同樣,指定從哪些方面描述對象,一個實體轉換爲數據庫中的一個表

  • R表示relationship,關係,關係描述兩個實體之間的對應規則,關係的類型包括包括一對1、一對多、多對多

  • 關係也是一種數據,須要經過一個字段存儲在表中

實體之間會由於引用相互引用字段而存在關係,這種關係通常有三種:

1-1

1-n

n-m[ 多對多通常表現爲2個 1對多 ]

  • 實體A對實體B爲1對1,則在表A或表B中建立一個字段,存儲另外一個表的主鍵值

  • 實體A對實體B爲1對多:在表B中建立一個字段,存儲表A的主鍵值

  • 實體A對實體B爲1對1,則在表A或表B中建立一個字段,存儲另外一個表的主鍵值

4-1[4]

  • 實體A對實體B爲1對多:在表B中建立一個字段,存儲表A的主鍵值

4-2[4]

  • 實體A對實體B爲多對多:新建一張表C,這個表只有兩個字段,一個用於存儲A的主鍵值,一個用於存儲B的

  • 實體A對實體B爲多對多:新建一張表C,這個表只有兩個字段,一個用於存儲A的主鍵值,一個用於存儲B的主鍵值

4-3_thumb[1]

邏輯刪除
  • 對於重要數據,並不但願物理刪除,一旦刪除,數據沒法找回

  • 刪除方案:設置isDelete的列,類型爲bit,表示邏輯刪除,默認值爲0

  • 對於非重要數據,能夠進行物理刪除

  • 數據的重要性,要根據實際開發決定

能夠在設計表的時候加上一個字段isdelete

2.sql進階知識

2.1 select消除重複行

  • 在select後面列前使用distinct能夠消除重複的行

  • distinct的使用須要放在第一個字段的位置,針對第一個字段進行去重。

select distinct 列1,... from 表名;
例:
select distinct gender from students;

例如,統計下在學生表的全部的學生班級

select distinct class from student;

2.2 where條件的運算符進階-空判斷

判空is null

例1:查詢沒有填寫個性簽名的學生

select * from student where description is null;

例2:查詢填寫了個性簽名的學生

select * from student where description is not null;

例3:查詢填寫了身高的男生

select * from student where description is not null and sex=1;

2.3 運算優先級

  • 優先級由高到低的順序爲:小括號,not,比較運算符,邏輯運算符

  • and比or先運算,若是同時出現並但願先算or,須要結合()使用

2.4. 鏈接查詢(連表查詢,多表查詢)

當查詢結果的列來源於多張表時,須要將多張錶鏈接成一個大的數據集,再選擇合適的列返回

mysql支持三種類型的鏈接查詢,分別爲:

2.4.1 內鏈接查詢-inner join

查詢的結果爲兩個表匹配到的數據

使用內鏈接,必須保證兩個表都會對應id的數據纔會被查詢出來。

image

select 字段1,字段2... from 主表 inner join 從表 on 主表.主鍵=從表.外鍵

例如:查詢學生的信息[ 成績、名字、班級 ]

咱們給學生表添加一個學生信息,而後使用該學生的主鍵id來連表查詢成績、名字和班級。

insert into student (name,sex,age,class,description) values ('劉德華',1,17,406,'');
​
select achievement,name,class 
from student as a 
inner join achievement as b 
on a.id=b.sid
where id=101;
​
# 上面語句因位該學生只在學生表student中有數據,而成績表中沒有數據,因此使用內鏈接,連表查詢的結果是
Empty set (0.00 sec)
一樣,若是從表有數據,而主表沒有數據,則使用內鏈接查詢同樣沒法查詢到結果。
#例如,添加一個成績記錄,是不存在學生
insert into achievement (sid,cid,achievement) values (102,10,85);
 
 
select achievement,name,class 
from student as a 
inner join achievement as b 
on a.id=b.sid
where id=102;

2.4.2 右鏈接查詢-right join

只要從表有數據,無論主表是否有數據,都會查詢到結果。[以從表的結果爲主]

查詢的結果爲兩個表匹配到的數據,右表特有的數據,對於左表中不存在的數據使用null填充

image

select 字段1,字段2... from 主表 right join 從表 on 主表.主鍵=從表.外鍵
例如,上面的成績id爲102的學生, 咱們使用右鏈接查詢。
select achievement,name,class 
from student as a 
right join achievement as b 
on a.id=b.sid;

2.4.3左鏈接查詢-left join

只要主表有數據,無論從表是否有數據都會被查詢出來。

查詢的結果爲兩個表匹配到的數據,左表特有的數據,對於右表中不存在的數據使用null填充

image
select * from 表1 left join 表2 on 表1.列 = 表2.列
例如,使用左鏈接查詢學生表與成績表,查詢學生姓名及分數
select achievement,name,class 
from student as a 
left join achievement as b 
on a.id=b.sid;

等同於
select achievement,name,class 
from achievement as b 
right join student as a 
on a.id=b.sid;

總結:三種連表查詢,最經常使用的是 left join,而後inner join保證數據的一致性。右鏈接基本上都是使用左鏈接代替。

2.5 多表關聯

語句:

select 表.字段1,表.字段2,表.字段3..... 
from 主表
left join 從表1 on 主表.主鍵=從表1.外鍵 
left join 從表2 on 主表.主鍵=從表2.外鍵  
     # 這裏和從表2鏈接的on條件看實際狀況,也會出現從表1.主鍵=從表2.外鍵的狀況
left join 從表3 on 主表.主鍵=從表3.外鍵
     # 這裏能夠是(從表1或從表2).主鍵=從表2.外鍵的狀況
left join ...

多表查詢的缺點:

多表查詢的效率,性能比單表要差。

多表查詢之後,還會帶來字段多了會引發字段覆蓋的狀況、

主表student 從表1 achievement 從表2 course

name xxx name

上面三張表若是連表,則出現主表的name覆蓋從表2的name這種狀況。

上面兩個問題:

  1. 把多表查詢語句能夠替換成單表查詢語句【須要優化的狀況】

  2. 把重複的字段名,分別使用as來設置成別的名稱。

例如,查詢白楊的班級、id、年齡和課程名稱以及對應課程的成績

select a.id,a.class,a.age,c.course,b.achievement 
from student as a
left join achievement as b on a.id=b.sid
left join course as c on c.id=b.cid
where a.name='白楊';

2.6 單表的連表查詢(自關聯查詢)

核心就是把一張表看作2張表來操做

# 建表:
create table area(
    id smallint not null auto_increment comment '主鍵ID',
    name char(30) not null comment '地區名稱',
    pid smallint not null default 0 comment '父級地區ID',
    primary key (id)
) engine=innodb charset=utf8;

insert into area (name,pid) values ('廣東',0),('深圳',1),('龍崗',2),('福田',2),('寶安',2);

格式

select 字段1,字段2...
from 主表(當前表) as a
left join 從表(當前表) as b on a.主鍵=b.外鍵

查找深圳地區的子地區,SQL代碼:

# 主表當作保存深圳的表,
# 從表當作保存深圳子地區的表

select b.id,b.name
from area as a
left join area as b on a.id=b.pid
where a.name='深圳';

2.7 子查詢

在一個 select 語句中,嵌入了另一個 select 語句, 那麼被嵌入的 select 語句稱之爲子查詢語句格式:select 字段 from 表名 where 條件(另外一條查詢語句)主查詢與子查詢的關係

  • 子查詢是嵌入到主查詢中

  • 子查詢是輔助主查詢的,要麼充當條件,要麼充當數據源

  • 子查詢是能夠獨立存在的語句,是一條完整的 select 語句

例如:查詢406班級大於平均年齡的學生

使用 子查詢:

  1. 查詢406班學平生均年齡

  2. 查詢大於平均年齡的學生

查詢406班級學生的平均年齡

select name,age from student where age > (select avg(age) as avg from student where class=406) and class=406;

2.8 having

group by 字段 having 條件;

過濾篩選,主要做用相似於where關鍵字,用於在SQL語句中進行條件判斷,過濾結果的。可是與where不一樣的地方在於having只能跟在group by 以後使用。

例如:查詢301班級大於班上平均成績的學生成績信息(name,平均分,班級)。

# 先求301班的平均成績
select avg(achievement) as achi from student as a
left join achievement as b on a.id=b.sid 
where class=301;

# 判斷301中的每一個人平均成績大於上面的到的平均成績
select name,avg(achievement) from student as a
left join achievement as b on a.id=b.sid
where class=301 group by name having avg(achievement) > (select avg(achievement) as achi from student as a

left join achievement as b on a.id=b.sid 
where class=301);

2.9 select查詢語句的完整格式

select distinct 字段1,字段2....
from 表名 as 表別名
left join 從表1 on 表名.主鍵=從表1.外鍵
left join ....
where ....
group by ... having ...
order by ...
limit start,count
  • 執行順序爲:

    • from 表名[包括連表]

    • where ....

    • group by ...

    • select distinct *

    • having ...

    • order by ...

    • limit start,count


  • 實際使用中,只是語句中某些部分的組合,而不是所有

3.數據庫的備份與恢復

3.1 備份

運行mysqldump命令

mysqldump –uroot –p 數據庫名 > python.sql;

# 按提示輸入mysql的密碼

3.2 恢復

  • 鏈接mysql,建立新的數據庫

  • 退出鏈接,執行以下命令

1.第一種方式
mysql -uroot –p 新數據庫名 < python.sql

# 根據提示輸入mysql密碼

2.第二種方式
mysql> create database abc; # 建立數據庫 
mysql> use abc; # 使用已建立的數據庫 
mysql> set names utf8; # 設置編碼 
mysql> source /home/abc/abc.sql # 導入備份數據庫

倆種方式的區別
1.第一種方式能夠本地和遠程操做
2,第二種方式只能本地操做

4.python操做mysql

通常使用pymysql模塊操做數據庫

import pymysql

# from pymysql import *

# 建立和數據庫服務器的鏈接  connection 
conn = pymysql.connect(host='localhost',port=3306,user='root',password='root123456',
                db='student',charset='utf8')

# 建立遊標對象
cursor = conn.cursor()

# 中間可使用遊標完成對數據庫的操做
sql = "select * from student;"

# 執行sql語句的函數  返回值是該SQL語句影響的行數
count = cursor.execute(sql)
print("操做影響的行數%d" % count)
# print(cursor.fetchone())   # 返回值類型是元祖,表示一條記錄

# 獲取本次操做的全部數據
for line in cursor.fetchall():
    print("數據是%s" % str(line))

# 關閉資源 先關遊標
cursor.close()
# 再關鏈接
conn.close()

執行語句

#執行sql,更新單條數據,並返回受影響行數
result = cursor.execute("SQL語句")

#插入多條,並返回受影響的函數,例如批量添加
result2 = cursor.executemany("多條數據")
#獲取最新自增ID
new_id = cursor.lastrowid

獲取結果

#獲取一行
result1 = cursor.fetchone()
#獲取多行[參數能夠設置指定返回數量]
result2 = cursor.fetchmany(整型)
#獲取全部
result3 = cursor.fetchall()

操做數據

#提交,保存新建或修改的數據,若是是查詢則不須要
conn.commit() # 寫在execute()以後

ok

相關文章
相關標籤/搜索