三範式
1:什麼是範式:
--範式是指:設計數據庫表的規則(Normal Form) 好的數據庫設計對數據的存儲性能和後期的程序開發,都會產生重要的影響。建立科學的,規範的數據庫就需要滿足一些規則來優化數據的設計和存儲
2.範式的分類
--範式的基本 : 目前關係數據庫有六種範式:第一範式(1NF)、第二範式(2NF)、第三範式(3NF)、巴斯- 科德範式(BCNF)、第四範式(4NF)和第五範式(5NF,又稱完美範式)。滿足最低要求的範式是第一範 式(1NF)。在第一範式的基礎上進一步滿足更多規範要求的稱爲第二範式(2NF),其餘範式以次類推。
一般說來,數據庫只需滿足第三範式(3NF)就行了
第一範式
--即數據庫表的每一列都是不可分割的原子數據項,而不能是集合、數組、記錄等非原子數據項。即實體中的某個屬性有多個值時,必須拆分爲不同的屬性。在符合第一範式(1NF)表中每個列的值只能是表的一個屬性或一個屬性的一部分。簡而言之,第一範式每一列不可再拆分,稱爲原子性。 第一範式:每一列不能再拆分
總結:如果不遵守第一範式,查詢出數據還需要進一步處理(查詢不方便)。遵守第一範式,需要什麼字段的數據就查詢什麼數據(方便查詢)。
第二範式
--第二範式(2NF)要求數據庫表中的每個實例或記錄必須可以被唯一地區分。選取一個能區分每個實體的屬性或屬性組,作爲實體的唯一標識。例如在員工表中的身份證號碼即可實現每個員工的區分,該身份證號碼即爲候選鍵,任何一個候選鍵都可以被選作主鍵。在找不到候選鍵時,可額外增加屬性以實現區分。 第二範式(2NF)要求實體的屬性完全依賴於主關鍵字。所謂完全依賴是指不能存在僅依賴主關鍵字一部分的屬性。如果存在,那麼這個屬性和主關鍵字的這一部分應該分離出來形成一個新的實體,新實體與原實體之間是一對多的關係。爲實現區分通常需要爲表加上一個列,以存儲各個實例的唯一標識。簡而言之,第二範式就是在第一範式的基礎上屬性完全依賴於主鍵。
也就是一張表描述一件事
總結:如果不準守第二範式,數據冗餘,相同數據無法區分。遵守第二範式減少數據冗餘,通過主鍵區分相同數據.
第三範式
--在2NF基礎上,任何非主屬性不依賴於其它非主屬性(在2NF基礎上消除傳遞依賴) 第三範式(3NF)是第二範式(2NF)的一個子集,即滿足第三範式(3NF)必須滿足第二範式(2NF)。簡而言之,第三範式(3NF)要求一個關係中不包含已在其它關係已包含的非主關鍵字信息
--從表的外鍵必須使用主表的主鍵
總結:如果不準守第三範式,可能會有相同數據無法區分,修改數據的時候多張表都需要修改(不方便修改)。遵守第三範式通過id可以區分相同數據,修改數據的時候只需要修改一張表(方便修改)。
多表查詢
同時查詢多張表獲取到需要的數據 比如:我們想查詢到開發部有多少人,需要將部門表和員工表同時進行查詢
- 分類:
- 內連接
- - 隱式內連接
- 顯示內連接
- 外連接
- - 左外連接
- 右外連接
--創建員工數據庫
create database employee;
--創建部門表
create table dept(
-> id int primary key auto_increment ,
-> name varchar(20)
-> );
--插入數據
insert into dept values (null,'開發部'),(null,'市場部'),(null,'財務部');
--創建員工表
mysql> create table emp (
-> id int primary key auto_increment ,
-> name varchar(10),
-> gender char(1), -- 性別
-> salary double , --工資
-> join_date date, --入職日期
-> dept_id int
-> );
--插入數據
INSERT INTO emp(NAME,gender,salary,join_date,dept_id) VALUES('孫悟空','男',7200,'2013‐02‐24',1);
INSERT INTO emp(NAME,gender,salary,join_date,dept_id) VALUES('豬八戒','男',3600,'2010‐12‐02',2);
INSERT INTO emp(NAME,gender,salary,join_date,dept_id) VALUES('唐僧','男',9000,'2008‐08‐08',2);
INSERT INTO emp(NAME,gender,salary,join_date,dept_id) VALUES('白骨精','女',5000,'2015‐10‐07',3);
INSERT INTO emp(NAME,gender,salary,join_date,dept_id) VALUES('蜘蛛精','女',4500,'2011‐03‐14',1);
笛卡爾積現象
多表查詢時左表的每條數據和右表的每條數據組合,這種效果成爲笛卡爾積
需求:查詢每個部門有哪些人
SELECT * FROM dept, em
--左邊每條數據和右表的每條數據組合,這就是笛卡爾積現象
mysql> select * from dept,emp;
+----+--------+----+--------+--------+--------+------------+---------+
| id | name | id | name | gender | salary | join_date | dept_id |
+----+--------+----+--------+--------+--------+------------+---------+
| 1 | 開發部 | 1 | 孫悟空 | 男 | 7200 | 2013-02-24 | 1 |
| 2 | 市場部 | 1 | 孫悟空 | 男 | 7200 | 2013-02-24 | 1 |
| 3 | 財務部 | 1 | 孫悟空 | 男 | 7200 | 2013-02-24 | 1 |
| 1 | 開發部 | 2 | 豬八戒 | 男 | 3600 | 2010-12-02 | 2 |
| 2 | 市場部 | 2 | 豬八戒 | 男 | 3600 | 2010-12-02 | 2 |
| 3 | 財務部 | 2 | 豬八戒 | 男 | 3600 | 2010-12-02 | 2 |
| 1 | 開發部 | 3 | 唐僧 | 男 | 9000 | 2008-08-08 | 2 |
| 2 | 市場部 | 3 | 唐僧 | 男 | 9000 | 2008-08-08 | 2 |
| 3 | 財務部 | 3 | 唐僧 | 男 | 9000 | 2008-08-08 | 2 |
| 1 | 開發部 | 4 | 白骨精 | 女 | 5000 | 2015-10-07 | 3 |
| 2 | 市場部 | 4 | 白骨精 | 女 | 5000 | 2015-10-07 | 3 |
| 3 | 財務部 | 4 | 白骨精 | 女 | 5000 | 2015-10-07 | 3 |
| 1 | 開發部 | 5 | 蜘蛛精 | 女 | 4500 | 2011-03-14 | 1 |
| 2 | 市場部 | 5 | 蜘蛛精 | 女 | 4500 | 2011-03-14 | 1 |
| 3 | 財務部 | 5 | 蜘蛛精 | 女 | 4500 | 2011-03-14 | 1 |
+----+--------+----+--------+--------+--------+------------+---------+
--過濾掉沒用的
mysql>
select * from dept,emp where emp.dept_id = dept.id;
內連接
用左邊表的記錄去匹配右邊表的記錄,如果符合條件的則顯示
- 隱式內連接
隱式內連接:看不到 JOIN 關鍵字,條件使用 WHERE 指定 SELECT 字段名 FROM 左表, 右表 WHERE 條件;
- 顯示內連接
顯示內連接:使用 INNER JOIN ... ON 語句, 可以省略 INNER SELECT 字段名 FROM 左表 INNER JOIN 右表 ON 條件;
--確定查詢的有哪些表
SELECT * FROM dept INNER JOIN emp;
--1. 確定表連接條件,員工表.dept_id = 部門表.id 的數據纔是有效的
SELECT * FROM dept INNER JOIN emp ON emp.dept_id=dept.id;
--1. 確定表連接條件,我們查詢的是唐僧的信息,部門表.name='唐僧'
SELECT * FROM dept INNER JOIN emp ON emp.dept_id=dept.id AND emp.NAME='唐僧';
--1. 確定查詢字段,查詢唐僧的信息,顯示員工id,姓名,性別,工資和所在的部門名稱
SELECT emp.id, emp.NAME, emp.gender, emp.salary, dept.NAME FROM dept INNER JOIN emp ONemp.dept_id=dept.id AND emp.NAME='唐僧';
總結:
總結內連接查詢步驟:
1. 確定查詢哪些表
2. 確定表連接條件
3. 確定查詢字段
左外連接
左外連接:使用 LEFT OUTER JOIN ... ON , OUTER 可以省略 SELECT 字段名 FROM 左表 LEFT OUTER JOIN 右表 ON
條件; 用左邊表的記錄去匹配右邊表的記錄,如果符合條件的則顯示;否則,顯示NULL 可以理解爲:在內連接的基礎上保證左表的數據全部顯示
- 部門表中增加一個銷售部
INSERT INTO dept (NAME) VALUES ('銷售部');
- 使用內連接查詢
SELECT * FROM dept INNER JOIN emp ON emp.dept_id=dept.id;
- 使用左外連接查詢
SELECT * FROM dept LEFT OUTER JOIN emp ON emp.dept_id=dept.id;
--用左邊表的記錄去匹配右邊表的記錄,如果符合條件的則顯示;否則,顯示NULL
--可以理解爲:在內連接的基礎上保證左表的數據全部顯示
右外連接
使用 RIGHT OUTER JOIN ... ON , OUTER 可以省略 SELECT 字段名 FROM 左表 RIGHT OUTER JOIN 右表 ON 條件;
用右邊表的記錄去匹配左邊表的記錄,如果符合條件的則顯示;否則,顯示NULL 可以理解爲:在內連接的基礎上保證右表的數據全部顯示
- 在員工表中增加一個員工
INSERT INTO emp(NAME,gender,salary,join_date,dept_id) VALUES('沙僧','男',6666,'2013-02-24',NULL);
- 使用內連接查詢
SELECT * FROM dept INNER JOIN emp ON emp.dept_id =dept.id;
--用左邊表的記錄去匹配右邊表的記錄,如果符合條件則顯示
- 使用右外連接查詢
SELECT * FROM dept RIGHT OUTER JOIN emp ON emp.dept_id =dept.id;
--用右表的記錄去匹配左邊表的記錄,如果符合條件的則顯示;否則NUll
--可以理解爲:在內連接的基礎上保證右表的數據全部顯示
子查詢
---
一條SELECT語句結果作爲另一條SELECT語法一部分(作爲查詢條件或查詢結果或表) SELECT 查詢字段 FROM 表WHERE 查詢條件;
SELECT * FROM employee WHERE salary=(SELECT MAX(salary) FROM employee);
子查詢需要放在()中
子查詢結果的三種情況:
1. 子查詢的結果是一個值的時候
2. 子查詢結果是單列多行的時候
3. 子查詢的結果是多行多列的時
--說明: 子查詢結果只要是 單列 ,肯定在 WHERE 後面作爲 條件 子查詢結果只要是 多列 ,肯定在 FROM 後面作爲表;
子查詢的結果是一個值
--子查詢結果只要是 單列 ,肯定在 WHERE 後面作爲 條件 SELECT 查詢字段 FROM 表 WHERE 字段=(子查詢);
-- 查詢工資最高的員工是誰?
1. 查詢最高工資是多少
SELECT MAX(salary) FROM emp;
1. 根據最高工資到員工表查詢到對應的員工信息
SELECT * FROM emp WHERE salary=(SELECT MAX(salary) FROM emp);
--查詢工資小於平均工資的員工有哪些?
1. 查詢平均工資是多少
SELECT AVG(salary) FROM emp;
1. 到員工表查詢小於平均的員工信息
SELECT * FROM emp WHERE salary < (SELECT AVG(salary) FROM emp);
子查詢結果是單例多行
--子查詢結果只要是 單列 ,肯定在 WHERE 後面作爲 條件 子查詢結果是單例多行,結果集類似於一個數組,父查詢使用 IN 運算符 SELECT 查詢字段 FROM 表 WHERE 字段 IN (子查詢);
--1. 查詢工資大於5000的員工,來自於哪些部門的名字
1. 先查詢大於5000的員工所在的部門id
SELECT dept_id FROM emp WHERE salary > 500;
1. 再查詢在這些部門id中部門的名字
SELECT dept.name FROM dept WHERE dept.id IN (SELECT dept_id FROM emp WHERE salary > 5000);
--2. 查詢開發部與財務部所有的員工信息
1. 先查詢開發部與財務部的id
SELECT id FROM dept WHERE NAME IN('開發部','財務部');
1. 再查詢在這些部門id中有哪些員工
SELECT * FROM emp WHERE dept_id IN (SELECT id FROM dept WHERE NAME IN('開發部','財務部'));
子查詢的結果是多行多列
子查詢結果只要是 多列 ,肯定在 FROM 後面作爲 表 SELECT 查詢字段 FROM (子查詢) 表別名 WHERE 條件; 子查詢作爲表需要取別名,否則這張表沒用名稱無法訪問表中的字段
查詢出2011年以後入職的員工信息,包括部門名稱
1. 在員工表中查詢2011-1-1以後入職的員工
SELECT * FROM emp WHERE join_date > '2011‐1‐1';
1. 查詢所有的部門信息,與上面的虛擬表中的信息組合,找出所有部門id等於的dept_id
SELECT * FROM dept d, (SELECT * FROM emp WHERE join_date > '2011‐1‐1') e WHERE e.dept_id =d.id;
使用表連接:SELECT d.*, e.* FROM dept d INNER JOIN emp e ON d.id = e.dept_id WHERE e.join_date > '2011‐1‐1';
多表查詢總結
子查詢結果只要是 單列 ,肯定在 WHERE 後面作爲 條件
SELECT 查詢字段 FROM 表 WHERE 字段=(子查詢);
子查詢結果只要是 多列 ,肯定在 FROM 後面作爲 表
SELECT 查詢字段 FROM (子查詢) 表別名 WHERE 條件;
1. 不管我們查詢幾張表,表連接查詢會產出笛卡爾積,我們需要消除笛卡爾積,拿到正確的數據。我們需要找到表與表之間通過哪個字段關聯起來的(通常是 外鍵=主鍵 )
2. 消除笛卡爾積規律:2張表需要1個條件,3張表需要2個條件,4張表需要3個條件。(條件數量=表的數量-1),每張表都要參與進來
3. 多表連接查詢步驟:
3.1. 確定要查詢哪些表
3.2. 確定表連接條件
3.3. 確定查詢
事務安全
事務的應用場景說明
在實際的業務開發中,有些業務操作要多次訪問數據庫。一個業務要發送多條SQL語句給數據庫執行。需要將多次訪問數據庫的操作視爲一個整體來執行,要麼所有的SQL語句全部執行成功。如果其中有一條SQL語句失敗,就進行事務的回滾,所有的SQL語句全部執行失敗。 例如: 張三給李四轉賬,張三賬號減錢,李四賬號加錢