關系模型


一 數據庫基本介紹

1. 數據庫的由來

我們使用數據庫是為了永久保存數據,如果沒有數據庫,我們要想永久保存數據只能是存在硬盤,如果忽略用硬盤存儲數據的讀寫效率問題,並且假設所有的數據都運行在一台機器之上,那么用硬盤存儲數據是沒有問題的,但毫無疑問,這個假設的前提條件是致命的。所有的數據都運行在一台機器上,假如這一台機器壞了,那么整個系統將會癱瘓,數據安全的問題都無法保證,首先喪失的是系統的穩定性,除此之外,一台機器的硬件性能的終究是有極限的,擴展機器的硬件性能有兩種方案:

  1. 垂直擴展:在一台機上用多個最牛逼的CPU,硬盤和內存
  2. 水平擴展:把多台普通的機器組合起來,使程序的各部分組建分散運行在不同機器上

顯然,單台機器的垂直擴展使用有極限(一台機器不能無限添加),更好的使用水平擴展,使用這種方案性能提升的同時兼顧了系統的穩定性,但是不同的組建所使用的數據肯定是同一份數據,多台機的數據是不能共享的,這時我們就需要一個專門負責數據存儲的機器,寫一個基於C/S架構服務端與不同的客戶端機器建立連接。

2. 數據庫管理系統

專門用來存儲數據的機器就相當於是一個存儲數據的倉庫,俗稱數據庫,在這台機器之上運行着一個數據庫管理系統,這個系統其實就是一個套接字軟件,也許你有能力寫這樣的一個軟件,不過要想用於生產環境,需要具備以下幾點要求:

  1. 數據的冗余與不一致:比如孫悟空是唐僧的大徒弟,也是花果山的美猴王,這兩個文件中都要存儲他的名字,這種不僅冗余導致存儲和訪問的開銷增大,還能導致數據不一致性,假如孫悟空的名字改成了孫行者,可能其中一個文件更改了而另外一個文件沒有更改。
  2. 數據庫訪問困難:比如某大學要找到所有課程成績都是90分以上的同學,這個檢索是相當復雜的。
  3. 數據孤立:不同的數據存儲在不同的文件中,我們假設所有文件的格式都是統一的,即便是這樣,在所有文件中檢索需要的數據依然是很困難的。
  4. 完整性問題:數據庫所存儲的值必須滿足某些特定的一致性約束,比如隨着年齡的增長,一個小孩的年齡會增高,但是一個人的身高不可能超過3米,我們可以在系統中加入適當的代碼來實現這個約束,然而當有新的約束加入時,比如一個人的體重不可能超過3噸,則很難再通過程序的限制來實現這些約束,尤其是當約束涉及到不同的文件的多項數據時,問題會變得極為復雜。
  5. 原子性問題:假如系統發生故障,當你給你女朋友轉錢的時候,你的錢沒了,你女朋友也沒收到這個錢,原子性就是要么都發生,要么都不要發生,在傳統的文件處理系統中,保持原子性是很難做到的。
  6. 並發訪問:由於數據可能同時被多個不同的客戶端機器訪問,並發訪問如果不加以控制,帶來的問題就是數據錯亂,在大量的文件中控制並發訪問,這是很難的。
  7. 安全問題:不同的角色應該有不同的訪問權限,如果一個長期被壓榨的程序員看到公司其他的的工資都比他的工資高的多,如果他有執行權限的話,那么接下來可能就是刪庫到跑路了。

每個公司的項目都需要這樣一個數據庫管理系統,既然大家都有這樣的需求,這也就產生了商業價值,這也就是造就了數據庫的誕生,下圖是全世界范圍內數據庫前十位的排名,一般企業里面使用以前面七種數據庫為主。

3. 數據庫的分類

數據庫有關系型與非關系型之說,關系型數據庫是建立在關系模型的基礎之上,而非關系型數據庫描述的是大量結構化數據存儲方法的集合,如果一定要有一個通俗的說法,可以暫時先理解為按照keyvalue的方式存取,但是這種說法並不准確,本教程主要是對關系型數據庫的研究(所有的關系型數據庫都基本相似,目前企業中所使用的主流是OracleMySQLPostgreSQL,學會了一種,另外的幾種也就差不多會用了),按照關系型與非關系型來划分,數據庫可以分為兩類:

  1. 關系型數據庫(RDBMS),例如OracleMySQLPostgreSQL
  2. 非關系型數據庫(NoSQL),例如MongoDBRedis

4. 數據庫的規則

一個完整的項目各部分的功能拆出去在各個機器上運行,大家所使用的數據應該也是同一份的,如果不添加任何的解決方案,各部分的數據肯定是獨立的,你要怎么來解決數據共享的問題呢?這個時候就要基於網絡通信寫一個C/S架構的套接字程序,有客戶端,有服務端,這就是一個數據庫管理軟件了。要寫這樣一個軟件需要考慮以下幾個問題,並發,同時操作處理好所得問題,數據核心數據的安全問題,用戶認證的操作,還要盡可能的降低IO操作,來提高程序的運行效率。以后所有的程序員寫程序都要先寫一個數據庫管理軟件,一寫寫一年,別的事都別干了,寫好了之后發現還有bug,再改一年,還沒改完,公司倒閉了,去下一家公司接着寫。

既然有了硬性需求就會與商業價值,這個時候有一家公司跳出來了,他說我來寫這個數據庫管理軟件,你們安心寫程序的業務邏輯就好了,這家公司就是Oracle,到目前為止,數據庫就誕生了。既然我們不想花時間寫數據庫管理軟件,那么使用別人寫好的軟件,就要遵守別人的游戲規則。比如我們需要記錄一個人的信息,就需要提取這個人一些明顯的特征。

id = 1
name = 'Albert'
age = 18
height = 180
weight = 80
is_hansome = True

我們使用這樣幾個數據一起來描述一個人的信息,這在數據庫中稱為一條記錄,我們把這條數據存在到文件中,那么就要創建一個文件,如果是在這個文件中有多條記錄呢?

1,Albert,18,180,80,Ture
2,James,34,203,113,False
3,Curry,30,190,95,False

顯然這樣存儲並不直觀,如果是使用表格的形式的來存儲,那么這將變得更加直觀。

id 名字 年齡 身高 體重 是否長得帥
1 Albert 18 180 80 True
2 James 34 203 113 False
3 Curry 30 190 95 False

這其實特別像我們使用的Excel的表格,所以數據庫里面就把它叫做表,而nameageheight這些就像是表頭,在數據庫中叫做字段。多條數據就構成了一張表,一個表有多個字段,這一張表就像是一個文件。你在自己的電腦上存東西不同的文件你會給它分類放到不同的文件夾中,寫數據庫管理軟件的人自然也能想到,多個表就構成了一個庫,就像是把多個文件放到一個文件夾中。

5. 數據庫服務器

數據庫管理軟件其實就是一個應用程序,要想使這個應用程序運行起來就要把它在操作系統上啟動,考慮到需求是穩定性優先,所以一定是在Linux系統上運行,當他運行起來,這就是數據庫服務器。

人們常說的數據庫指的是什么?

  1. 文件夾也就是庫,這通常開發所說的
  2. 一台機器,也就是運行數據庫管理軟件的那台裝有Linux系統的服務器
  3. 數據庫管理軟件

二 數據庫的安裝

1. 數據庫版本問題

目前MySQL最新版本是8.0以上,雖然有企業在使用,而且兩三年以后也可能會大量普及,但是大部分企業還是集中使用5.5,5.6或者5.7的版本,這里我們的講解會以5.6版本為標准講解。

8.0以上的版本相對於5.6的版本確實做了升級,在極小的一部分細微的地方使用會更加得心應手,大部分的使用還是一樣的,我們只要學會了5.6的版本,上手8.0以上版本是非常容易的,反之,則不易。

2. 安裝方式

打開官網地址:https://dev.mysql.com/downloads/mysql/ ,點擊鏈接:MySQL Community Server 5.6 下載對應操作系統版本。在Windows系統上以你可以很容易的找到MySQL的文件夾內的bin文件夾,這里面就會存放mysql.exemysqld.exe文件,分別對應MySQL客戶端和服務端的啟動程序,MacOS系統是前往文件夾/usr/local/mysql/,回車之后也可以看到同樣的文件內容,Windows系統把MySQL做成系統服務是的開機就會啟動,我們使用:mysqld --install命令來完成,MacOS系統不需要做這些配置。

3. 配置環境變量

配置環境變量的目的是希望能夠在終端直接執行MySQL命令而不是在進入/usr/local/mysql/bin/路徑之后才能執行MySQL命令,所以我們需要把這個路徑添加至環境變量中,root權限使用命令vim /usr/local/mysql/.bash_profile創建並打開一個新文件,把export PATH=${PATH}:/usr/local/mysql/bin,添加到空文件中環境變量即配置完成。

三 數據的基本使用

1. 登錄

我們使用命令:mysql -h 127.0.0.1 -P 3306 -uroot -p來完成客戶端的登錄,-h指的是IP地址,本機連接可以省略,-P(大寫P)指的是端口號,默認是3306,也可以省略,-uroot指的是用戶名,自帶一個管理員root用戶名,-p(小寫p)指的是密碼。簡化之后的登錄命令是:mysql -uroot -p,剛開始我們沒有設置密碼,直接回車就可以登錄了。

2. 修改密碼

MySQL會自帶一個管理員用戶,默認是沒有密碼的,我們需要對他進行修改密碼,方式有很多,這里我們講三種。

  1. 使用mysqladmin -uroot -p password命令,這是直接修改服務端保存的密碼,注意要以管理員的身份運行,並且在MySQL的安裝路徑下執行,Windows系統和MacOS系統略有差異,Windows:mysqladmin -uroot -p password '123',把無密碼修改為密碼123,mysqladmin -uroot -p123 password '123456',把密碼123改成123456,MacOS:先執行sudo -i命令獲取本機管理員權限,在執行cd /usr/local/mysql/bin切換到安裝路徑下,最后執行./mysqladmin -uroot -p password '123',以上這些是把無密碼改成123的完整操作,./mysqladmin -uroot -p password '123456',這是把密碼由123改成123456的操作,與Windows區別在於兩點:命令前面要加./,原來的密碼要再次輸入而不是寫在命令行。密碼修改成功之后會有一個警告:Warning: Using a password on the command line interface can be insecure.,這說的是你在使用明文密碼不安全,別人通過上下命令翻滾可以看到這個密碼,可以暫時先忽略。
  2. 登錄之后使用set password for 用戶名@地址 = password('新密碼');命令,這是通過客戶端間接修改密碼,示例:set password for root@localhost = password('1');,把原來密碼修改為1,然后在執行刷新命令:flush privileges;
  3. 登錄之后使用命令:update mysql.user set password=password('1') where user='root' and host='localhost';,然后再刷新。

3. 破解管理員密碼

如果密碼忘記了,在測試環境下你可能會有很多種方式來解決這個問題,但是生產環境並不是允許的。這時我們可以思考一下,MySQL是操作系統的一個軟件,我們只需要控制操作系統啟動的時候mysql的用戶認證機制,這樣就能無需密碼直接登錄,然后再修改密碼。Windows系統和MacOS系統略有差異,WIndows:先關閉MySQL服務,然后以管理員身份在安裝路徑下執行mysqld --skip-grant-tables命令,這指的是跳過授權表(表就是文件)啟動MySQL服務,下一步再打開一個命令行窗口不輸入密碼直接登錄,然后就可以進行密碼修改了,最后關閉MySQL服務並正常啟動。MacOS:執行命令略有差異./mysqld_safe --skip-grant-tables,其他操作一致。

4. 修改配置文件

在不同的操作系統和不同的SQL版本上字符編碼會略有差異,登錄之后我們可以使用\s命令來查看本機字符編碼,所以,需要對配置文件的字符編碼進行修改。Windows:在安裝路徑根目錄下創建一個my.ini文件,MySQL會自動讀取配置文件內容,MacOS:找到路徑usr/local/mysql下的my.cnf文件,兩個系統配置文件內容一致,如下所示:

# 1. 修改服務端字符編碼配置
[mysqld]
default-character-set=utf8
collation-server=utf8_general_ci

# 2. 修改全局客戶端字符編碼配置
[client]
default-character-set=utf8

# 3. 修改客戶端字符編碼
[mysql]
default-character-set=utf8

# 全局客戶端client配置指的是針對全局所有的客戶端軟件
# 如果與mysql配置有沖突以mysql為准
# 如果沒有mysql配置,以client配置為准

四 SQL基本語句

基本SQL語句是入門的重點,但是簡單到你懷疑人生。我們使用數據庫就是對數據的增刪改查,而數據的的對象就是庫,表和記錄。

創建或刪除的過程中,你可以看到你的數據庫安裝目錄中data目錄下面的文件夾或者文件的變化,其中.frm文件使用存儲表結構,.ibd文件是存放與表相關的數據。

# 查看當前登錄用戶
select user();
# 如果命令寫錯了可以使用:【\c】結束


# 1 文件夾(庫)

# 數據庫命名:可以由字母、數字、下划線、@、#組成,區分大小寫,不能重復,不能使用關鍵字如 create select,不能單獨使用數字,最長128位。

# 增:創建數據庫db1,可以指定自己編碼,如果不指定則使用配置文件字符編碼,簡化:create database db1;
create database db1 charset utf8;

# 改:修改數據庫的字符編碼為gbk,數據庫名不能直接修改
alter database db1 charset gbk;

# 查:查看所有庫的庫名,在查看中可以看到字符編碼的變化
show databases;
# 單獨查看某一個庫的信息
show create database db1;

# 刪
drop database db1;


# 2 文件(表)
# 首先切換文件夾:
use db1;
# 查看當前所在的文件夾
select database();

# 增:創建表,字段名必須指定字符編碼,int整型,char字符串。
create table t1
(
    id   int,
    name char
);

# 改:修改表名
rename table t1 to t0;
# 修改字段名長度,char類型有默認長度,為1
alter table t0
    modify name char(16);

# 查:查看當前庫下所有的表名
show tables;
# 查看t0表的詳細信息
show create table t0;
# 查看表結構,可以簡寫成【desc t0】
describe t0;

# 刪;刪除表內所有數據和模型關系
drop table t0;

# 刪除:保留模型關系,只刪除數據
delete from t0;


# 3 文件的一行內容(記錄)

# 增:插入記錄,在db1目錄下可以把【db1.】省略
insert into db1.t0
values (1, 'Albert'),
       (2, 'James'),
       (3, 'zimuge');

# 改:修改字段名
update db1.t0
set name='nba'
where id >= 1;
# 同時修改多個字段
update db1.t0
set name='nbaaaa',id=111
where id >= 1;

# 查:查看數據
select id, name
from db1.t0;

# # 刪:刪除記錄
delete
from db1.t0
where name = 'nba';

五 系統庫文件說明

  1. information_schema: 虛擬庫,不占用磁盤空間,存儲的是數據庫啟動后的一些參數,如用戶表信息、列信息、權限信息、字符信息等。
  2. performance_schema: MySQL 5.5開始新增一個數據庫:主要用於收集數據庫服務器性能參數,記錄處理查詢請求時發生的各種事件、鎖等現象。
  3. mysql:授權庫,主要存儲系統用戶的權限信息。
  4. test:MySQL數據庫系統自動創建的測試數據庫。

六 存儲引擎

我們可以在登錄之后使用show engines;命令來查看存儲引擎,所謂的存儲引擎就是一個存儲的功能,對於不同的文件,我們存儲的方式是不同的,就是想你保存文本文件可以使用.doc,保存圖片文件使用.jpg,MySQL就是存取數據的,5.6以上的版本存儲引擎默認是InnoDB,我們直接使用就可以了。

# 默認存儲引擎,我們重點講解的
create table t1
(
    id int
) engine = innodb;

# 不支持事務,行級鎖,外鍵
create table t2
(
    id int
) engine = myisam;

# 相當於是回收站,寫進去的東西都消失了
create table t3
(
    id int
) engine = blackhole;

# 存入內存,不能永久保存
create table t4
(
    id int
) engine = memory;

七 數據模型

數據庫結構的基礎是數據模型,數據模型是描述數據,數據聯系,數據語義以及一致性約束的概念工具的集合,數據模型可以分為四類:

  1. 關系模型:其實就是SQL語句,使用表的集合來表示數據和數據間的聯系,這是關系模型使用最廣泛的數據模型,也是本教程講解中所使用的數據模型。
  2. 實體聯系模型:又叫做E-R數據模型,是基於對現實世界的一種認識,實體聯系模型被廣泛應用與數據庫設計。
  3. 基於對象的數據模型:這就是用編程語言來操縱數據庫,這種數據模型結合編程語言面向對象和關系模型的特征。
  4. 半結構化數據模型:半結構化數據模型允許出現相同類型的數據項含有不同屬性集的數據定義,XML廣泛用來表示半結構化數據,現在用的很少了,了解即可。

八 數據庫的碼

1. 主鍵

數據庫的每行數據是以元組(tuple)的形式來保存的,查詢的結果是求笛卡爾積,也是以元組的形式呈現出來的。我們必須要有一種能夠區分數據關系中不同元組的方法,這就是用它們的屬性來表明,也就是說,一個關系中沒有兩個元組在所有屬性上的取值都相同。

  1. 超碼(super key)是一個或多個屬性的集合,這些屬性的組合可以使我們在一個關系中唯一的標識一個元組,第四小節用的的id就是一個超碼。
  2. 候選碼(candidate key)超碼中可能會包含無關緊要的屬性,假如我們用idname的組合作為超碼,這自然是符合超碼的定義,但是我們通常只對一些超碼感興趣,最小的超碼稱為候選碼。
  3. 主碼(primary key)這也就是人們常說的主鍵了,我們用主碼來表示被數據庫設計者選中的,主要用來在一個關系中區分不同元組的候選碼。碼的指定代表了被建模事物在現實世界中的約束。

主碼的選擇必須慎重,正如我們所知道的,人名顯然是不足以做主碼的,而身份證號碼或者護照號碼卻可以,習慣上我們會把一個關系模型的主鍵屬性列在其他屬性前面。

微信這么偉大的產品,一直以來因為不能更改微信號被人們吐槽,而在有些產品中昵稱甚至都不能隨意更改,這其中原因大家自由想象,但卻說明了微信還是很NB的。

2. 外鍵

一個關系模型[r1]可能在它的屬性中包括另一個關系模型[r2]中的主鍵,那么這個屬性在[r1]上被稱作參照[r2]的外鍵(foreign key),模型[r1]被稱為外鍵一類的參照關系,模型[r2]被稱作外鍵的被參照關系。

九 基本數據類型

SQL標准中有多種數據類型,在這里我們想講幾個最基礎的,后面的章節會涉及到更多的數據類型。

  1. char(n):固定長度的字符串,用戶指定長度n,默認為1,可以使用全程character
  2. varchar(n):可變長度的字符串,用戶指定最大長度n,等價於character varying
  3. int:整數類型,等價於integer
  4. numeric(p,d):定點數,精度由用戶指定,p指的是這個數有p位數字,其中d為數字在小數點右邊。所以這個類型的字段上,numeric(3,1)可以精確存儲到21.6,但不能精確存儲898.2或者1.23這樣的數。

charvarchar的爭論由來已久,我們應該針對不同的業務場景選擇合適的數據類型,我不喜歡中庸的評判,如果一定籠統的要選擇一個話,我會更傾向於使用varchar

char類型用於存放固定長度的字符串,例如,屬性name的類型是char(10),我們為此屬性存入字符串“albert”,那么該字符串后面會追加4個空格以達到10個字符串的長度,反之,varchar則不會增加空格。當我們在比較兩個char類型的值時,如果他們長度不同,在比較之前會自動在短值后面加上額外的空格以使它們長度一致。當比較一個char類型和一個varchar類型的時候,我們期待的自動補充可能發生也可能不會發生,這取決於數據庫系統,幸運的是,在MySQL中5.6和5.6以上的版本會自動補充,但是我們依然建議始終使用一個char或者varchar來避免這樣的問題,因為並不是所有的數據庫都具有這樣的容錯性。

十 基本模式定義

1. 數據庫使用

這是對以上所講的內容作為一個應用性的總結,另外還會設計到一個完整性約束的概念,這其實就是對建模事物在現實中的約束體現到了SQL語句上面。

# 部門表
create table department
(
    department_name varchar(20),
    building        varchar(15),
    budget          numeric(12, 2),
    primary key (department_name)
);

# 產品表
use db1;
create table product
(
    product_id      varchar(7),
    title           varchar(20) not null ,
    credits         numeric(8, 0),
    department_name varchar(20),
    primary key (product_id),
    foreign key (department_name) references department (department_name)
);

# 人員表
create table staff
(
    staff_id   varchar(10),
    name       varchar(15) not null ,
    age        int,
    product_id varchar(7),
    primary key (staff_id),
    foreign key (product_id) references product (product_id)
);

說明:

  1. 每種數據類型都可能包含一個被稱作空值的特殊值,空值是一個缺失值,該值可能存在但並不為人所知,也可能根本不存在,在能夠控制的情況下,我要禁止空值的加入。一個屬性上的not null約束表明在該屬性上不允許空值。
  2. 主鍵屬性已經限制非空且唯一,所以不需要約束空值限制。
  3. references約束外鍵屬性必然存在於關聯模型的主鍵屬性中。

2. 完整性約束限制

SQL禁止破壞完整性約束的任何數據庫更新,比如我們上面建了三個表,如果要刪除department表這回破壞外鍵的完整性約束,SQL將會標記一個錯誤,並阻止更新。要想實現刪除,目前只能是先刪除沒有被外鍵關聯的模型。除此之外當我們需要更新或者刪除數據時,也會因為外鍵的完整性約束被破壞而無法執行,這時我們應在在外鍵中使用cascade命令來串聯更新。

# 優化外鍵關系

# 部門表
create table department
(
    department_name varchar(20),
    building        varchar(15),
    budget          numeric(12, 2),
    primary key (department_name)
);

# 產品表
use db1;
create table product
(
    # 用int類型 改成自動增長的主鍵
    product_id      int primary key auto_increment,
    title           varchar(20) not null,
    credits         numeric(8, 0),
    department_name varchar(20),
    foreign key (department_name) references department (department_name)
        on update cascade
        on delete cascade
);

# 人員表
create table staff
(
    staff_id   varchar(10),
    name       varchar(15) not null,
    age        int,

    # 關聯的外鍵與被關聯外鍵數據類型應該一致
    product_id int,
    primary key (staff_id),
    foreign key (product_id) references product (product_id)
        on update cascade
        on delete cascade
);

insert into department(department_name, building)
values ('市場部', '市場部大樓'),
       ('研發部', '研發部大樓'),
       ('人事部', '人事部大樓');


insert into product(title, department_name)
values ('1號產品', '研發部'),
       ('2號產品', '研發部'),
       ('3號產品', '研發部'),
       ('4號產品', '市場部');


delete
from department
where department_name = '研發部';

update department
set building='市場部牛逼大樓'
where department_name = '市場部';


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM