SQL基礎
SQL語句的分類:
- DQL: 數據庫查詢語句,基本的就是select查詢命令,用於查詢數據
- DML: 數據操縱語句,用於插入,更新,刪除數據,即INSERT, UPDATE,DELETE
- DDL: 數據定義語句,用於創建,刪除,以及修改表,索引等數據庫對象,CREATE,DRIO,ALTER
創建表:
CREATE TABLE table_name (
col01_name data_type,
col02_name data_type,
col03_name data_type,
....
)
關於data_type, 常用的有以下數據類型:
整數:
-
tinyint: 微整數:很小的整數占8位二進制
-
smallint: 小整數: 小的整數 占16位二進制
-
mediumint: 中整數:中等長度的整數,占24位二進制
-
int: 整型,整數類型,占32位二進制
小數
- float:單精度浮點數,占4個字節
- double:雙精度,占8個字節
日期
- time : 表示時間類型
- date: 表示日期類型 (只包含年月日 yyyy-MM-dd)
- datetime: 同時可以表示日期和時間類型(包含年月日十分秒,yyyy-MM-dd HH:mm:ss)
- timestamp: 時間戳類型,(包含年月日十分秒,yyyy-MM-dd HH:mm:ss),如果添加數據時沒有賦值,則自動插入當前的系統時間
字符串
-
char(m): 固定長度的字符串,使用幾個字符就占用幾個,M 為 0~65535 之間的整數
-
varchar(m):可變長度的字符串,使用幾個字符就占用幾個,M 為 0~65535 之間的整數
大二進制
- tinyblob: 允許長度0-255字節
- blog: 允許長度為0-65535字節
- longblog: 允許長度為0~4294967295 字節
大文本
1. tinytext: 允許長度 0~255 字節
2. text: 允許長度 0~65535 字節
3. mediumtext: 允許長度 0~167772150 字節
4. longtext: 允許長度 0~4294967295 字節
一個簡單的例子:
mysql> create table student (
-> id int,
-> name varchar(32),
-> age int,
-> score double(4,1),
-> birthday date,
-> insert_time timestamp
-> );
Query OK, 0 rows affected (0.01 sec)
mysql> desc student;
+-------------+-------------+------+-----+-------------------+-----------------------------+
| Field | Type | Null | Key | Default | Extra |
+-------------+-------------+------+-----+-------------------+-----------------------------+
| id | int(11) | YES | | NULL | |
| name | varchar(32) | YES | | NULL | |
| age | int(11) | YES | | NULL | |
| score | double(4,1) | YES | | NULL | |
| birthday | date | YES | | NULL | |
| insert_time | timestamp | NO | | CURRENT_TIMESTAMP | on update CURRENT_TIMESTAMP |
+-------------+-------------+------+-----+-------------------+-----------------------------+
6 rows in set (0.00 sec)
mysql>
刪除表
DROP TABLE table_name;
修改表
ALTER TABLE student RENAME TO new_student; //修改表名
ALTER TABLE new_student character SET utf8; // 修改表的字符集
ALTER TABLE new_student ADD 列名 數據類型; // 給表添加列
ALTER TABLE new_student CHANGE 要更改列名稱 要更改為的列名稱; // 更改列名稱
ALTER TABLE new_student MODIFY 列名 類型; // 更改列的類型
ALTER TABLE new_student DROP 列名; // 刪除表中一個列
插入語句
INSERT INTO student VALUES (1, '張三', 18);
INSERT INTO student (no,age,name) VALUES (1,'張三',18);
更新語句
UPDATE student SET age=16;
UPDATE student SET age=16, name="李四" WHERE no=2;
刪除語句
DELETE FROM student WHERE no=2;
注意:如果沒有WHERE 條件,將刪除student表中的所有數據,
不推薦使用,因為這里是有多少數據就執行多少次刪除數據操作
查詢語句
select
字段列表
from
表名列表
where
條件列表
group
分組字段
having
分組之后的條件
order by
排序
limit
分頁限定
基礎查詢
- select * from student;查詢數據
- select name,age from student; 查詢某些列的數據
- select distinct address from student; 去除重復的結果集
- select distinct name,address from student; 這里值name 和 address都相同的進行去重
- select name,math,english , math+english from student; 計算math和english成績之和
- select name,math,english,math+IFNULL(english,0) from student; 這里的查詢表示如果english是nulll的時候用0進行替換
- select name 名字,math 數學,english 英語,math+IFNULL(english,0) as 總分 from student; 這里是給查詢的結果起別名這里是通過as實現,也可以直接空格之后加別名
條件查詢:
where子句后跟條件
運算符:
>、 <、<=、>=、=、<> 、!=
between....and: 如between 100 and 200
in(集合):集合表示多個值,使用都好分隔
like:模糊查詢, 占位符:_ 表示單個任意字符 ; % 表示多個任意字符
is null : 查詢某一列為null的值,注:不能寫=null
and 或 &&
or 或 ||
not 或 !
- 查詢年齡大於20的數據:select * from student where age>20;
- 查詢年齡大於等於20,小於等於30:select * from student where age>=20 and age<=30;
- 查詢年齡大於等於20,小於等於30:select * from student where age between 20 and 30;
- 查詢22,18,24歲的數據:select * from student where age=22 or age=18 or age=24;
- 查詢22,18,24歲的數據:select * from student where age in (22,18,24);
- select * from student where english is null; 這里切忌不能使用= 或者 !=
- select * from student where english is not null;
- select * from student where name like '趙%'; 查詢姓趙的人
- select * from student where name like '_華%'; 查詢名字第二個是華的數據
- select * from student where name like '___'; 查詢名字為3個字的數據
- select * from student where name like '%馬%'; 查詢名字包含馬的數據
排序查詢
oder by 排序字段 排序方式(asc,desc)
- select * from student order by math; 按照數學成績排序,默認位升序(asc)
- select * from student order by math desc, english desc; 按數學成績進行排序,如果math成績一樣,按照english成績進行排序。 第二排序條件只有當第一排序條件無法排序時才會執行
聚合函數
將一列數據作為一個整體,進行縱向的計算。
count: 計算個數
max: 計算最大值
min: 計算最小值
sum: 求和
注意:聚合函數的計算都是排除了null值
avg: 計算平均值
- select count(name) from student; 查詢student表一共有多少學生
- select count(ifnull(english,0)) from student; 防止為null計算數目不對的問題
- select count(*) from sutdent;
- select count(id) from student;
- select max(english) from student; 查詢英語成績最高的數據
- select min(english) from student; 查詢英語成績最低的數據
- select sum(english) from student; 查詢英語成績的總分
- select avg(english) from student; 查詢英語成績的平均分
分組查詢
語法: group by 分組字段
注意:
分組之后查詢的字段:要么是分組字段,要么是聚合函數
where 和 having 的區別:
where 在分組之前進行限定,如果不滿足條件,則不參與分組。having 在分組之后進行限定,如果不滿足結果,則不會被查詢出來。
where 后不可以跟聚合函數,having可以進行聚合函數的判斷
- select sex, avg(math) from student group by sex; 計算男女同學的數據平均成績
- select sex, count(id) from student group by sex; 計算男女的人數
- select sex, avg(math) from student where math>70 group by sex; 計算數學成績大於70分的男女同學的數據平均成績
- select sex, avg(math), count(id) from student where math>70 gorup by sex having count(id) >2; having條件一定是在group by 分組之后
分頁查詢
語法: limit 開始索引,每頁查詢的條數
- select * from student limit 0,3; 查詢第一頁
- select * from student limit 3,3;查詢第二頁
- select * from student limit 6,3;查詢第三頁
約束
對表中的數據進行限定,保證數據的正確性,有效性和完整性
分類:
- 主鍵約束:primary key
- 非空約束: not null
- 唯一約束:unique
- 外鍵約束: foreign key
非空約束 not null
添加非空約束的方式:
-
創建表的時候添加約束
例子如下:
CREATE TABLE stu (
id INT,
name varchar(20) NOT NULL -- name 為非空
);
還有的情況是我們創建表的時候沒有添加非空的約束
alter table stu modify name varchar(20);
創建表之后,手動更改表字段添加非空限制
alter table stu modify name varchar(20) NOT NULL ;
唯一約束 unique
其實就是某一列的值不能重復
- 創建唯一約束
-- 創建表時添加唯一約束
CREATE TABLE stu (
id INT,
phone_number varchar(20) UNIQUE -- 手機號
);
注意:唯一約束可以有null值,但是只能有一條記錄為null
- 刪除唯一約束
alter table stu drop index phone_number;
- 在表創建完之后,添加唯一約束
-- 創建表
CREATE TABLE stu (
id INT,
phone_number varchar(20) -- 手機號
);
-- 添加唯一約束
alter table stu modify phone_number varchar(20) UNIQUE ;
主鍵約束 primary key
概念: 非空且唯一, 且一張表只能有一個字段位主鍵,通俗說主鍵就是表中記錄的唯一標識
- 創建表時候添加主鍵約束
-- 創建表時添加主鍵
CREATE TABLE stu (
id INT primary key , -- 給id添加主鍵約束
name varchar(20)
);
- 刪除主鍵
-- 刪除主鍵
alter table stu drop primary key ;
- 創建表之后添加主鍵
-- 創建表
CREATE TABLE stu (
id INT , -- 給id添加主鍵約束
name varchar(20)
);
-- 添加主鍵
alter table stu modify id int primary key ;
自動增長 auto_increment
概念:如果某一列是數值類型的,使用auto_increment可以來完成值的自動增長,通常和主鍵一起使用,很少單獨使用
- 在創建表時添加主鍵約束,並完整主鍵自增長
-- 創建表,添加主鍵約束,並設置主鍵自增長
CREATE TABLE stu (
id INT primary key auto_increment, -- 給id添加主鍵約束,並設置自增長
name varchar(20)
);
- 刪除自動增長
alter table stu modify id int;
- 創建表之后手動添加自增長
-- 創建表,添加主鍵約束
CREATE TABLE stu (
id INT primary key, -- 給id添加主鍵約束
name varchar(20)
);
alter table stu modify id int auto_increment ;
外鍵約束 foreign key
先通過一個例子來看為啥會有外鍵約束,通過下列sql語句創建表並插入數據:
CREATE TABLE emp (
id INT PRIMARY KEY AUTO_INCREMENT,
NAME VARCHAR(30),
age INT,
dep_name VARCHAR(30),
dep_location VARCHAR(30)
);
-- 添加數據
INSERT INTO emp (name, age, dep_name, dep_location) VALUES ('張三', 20, '研發部', '廣州');
INSERT INTO emp (name, age, dep_name, dep_location) VALUES ('李四', 21, '研發部', '廣州');
INSERT INTO emp (name, age, dep_name, dep_location) VALUES ('王五', 20, '研發部', '廣州');
INSERT INTO emp (name, age, dep_name, dep_location) VALUES ('老王', 20, '銷售部', '深圳');
INSERT INTO emp (name, age, dep_name, dep_location) VALUES ('大王', 22, '銷售部', '深圳');
INSERT INTO emp (name, age, dep_name, dep_location) VALUES ('小王', 18, '銷售部', '深圳');
生成如下數據:
1 張三 20 研發部 廣州
2 李四 21 研發部 廣州
3 王五 20 研發部 廣州
4 老王 20 銷售部 深圳
5 大王 22 銷售部 深圳
6 小王 18 銷售部 深圳
從上面的數據我們可以看到前三個人的數據和后三個人的數據顯得非常冗余
這里有一個方案就是把這張表拆程兩張表
-- 創建部門表(id,dep_name,dep_location)
-- 一方,主表
create table department(
id int primary key auto_increment,
dep_name varchar(20),
dep_location varchar(20)
);
-- 創建員工表(id,name,age,dep_id)
-- 多方,從表
create table employee(
id int primary key auto_increment,
name varchar(20),
age int,
dep_id int -- 外鍵對應主表的主鍵
);
-- 添加 2 個部門
insert into department values('研發部','廣州'),('銷售部', '深圳');
select * from department;
-- 添加員工,dep_id 表示員工所在的部門
INSERT INTO employee (name, age, dep_id) VALUES ('張三', 20, 1);
INSERT INTO employee (name, age, dep_id) VALUES ('李四', 21, 1);
INSERT INTO employee (name, age, dep_id) VALUES ('王五', 20, 1);
INSERT INTO employee (name, age, dep_id) VALUES ('老王', 20, 2);
INSERT INTO employee (name, age, dep_id) VALUES ('大王', 22, 2);
INSERT INTO employee (name, age, dep_id) VALUES ('小王', 18, 2);
生成如下數據:
1 研發部 廣州
2 銷售部 深圳
1 張三 20 1
2 李四 21 1
3 王五 20 1
4 老王 20 2
5 大王 22 2
6 小王 18 2
這樣我們雖然解決了數據冗余的問題,但是還有一個問題,假如有一天我們們不小心把部門數據刪除了,這個時候員工表中的字段依然還存在dep_id,以及當我們添加了一個員工,但是我們填寫了一個不存在dep_id
這里我們就需要解決員工表中的dep_id 只能是department表中存在的id, 這個時候就需要用到外鍵
什么是外鍵?
在從表中與主表主鍵對應的那一列,就像上面員工表中的dep_id
主表: 一方,用來約束別人的表
從表: 多方, 被別人約束的表
- 創建表的時候添加外鍵
-- 創建部門表(id,dep_name,dep_location)
-- 一方,主表
create table department(
id int primary key auto_increment,
dep_name varchar(20),
dep_location varchar(20)
);
-- 創建員工表(id,name,age,dep_id)
-- 多方,從表
create table employee(
id int primary key auto_increment,
name varchar(20),
age int,
dep_id int,-- 外鍵對應主表的主鍵
-- 創建外鍵約束
constraint emp_depid_fk foreign key (dep_id) references department(id)
);
-- 添加 2 個部門
insert into department values(null, '研發部','廣州'),(null, '銷售部', '深圳');
select * from department;
-- 添加員工,dep_id 表示員工所在的部門
INSERT INTO employee (NAME, age, dep_id) VALUES ('張三', 20, 1);
INSERT INTO employee (NAME, age, dep_id) VALUES ('李四', 21, 1);
INSERT INTO employee (NAME, age, dep_id) VALUES ('王五', 20, 1);
INSERT INTO employee (NAME, age, dep_id) VALUES ('老王', 20, 2);
INSERT INTO employee (NAME, age, dep_id) VALUES ('大王', 22, 2);
INSERT INTO employee (NAME, age, dep_id) VALUES ('小王', 18, 2);
上面的表和我們之前拆分之后的表表面上看着沒啥區別,但是其實內在它們已經建立了聯系
這個時候我們可以嘗試刪除部門表中的一個數據
db1> DELETE FROM db1.department WHERE id = 1
[2020-09-29 16:27:55] [23000][1451] Cannot delete or update a parent row: a foreign key constraint fails (`db1`.`employee`, CONSTRAINT `emp_depid_fk` FOREIGN KEY (`dep_id`) REFERENCES `department` (`id`))
[2020-09-29 16:27:55] [23000][1451] Cannot delete or update a parent row: a foreign key constraint fails (`db1`.`employee`, CONSTRAINT `emp_depid_fk` FOREIGN KEY (`dep_id`) REFERENCES `department` (`id`))
這個時候我們就可以看到因為員工表與部門表有外鍵關聯關系,所以會提示我們無法Cannot delete or update a parent row: a foreign key constraint fails...
同時這個時候我們給員工表添加一個表,同時dep_id 我們隨便寫一個
db1> INSERT INTO db1.employee (id, name, age, dep_id) VALUES (7, '哈哈哈', 222, 2323)
[2020-09-29 16:30:22] [23000][1452] Cannot add or update a child row: a foreign key constraint fails (`db1`.`employee`, CONSTRAINT `emp_depid_fk` FOREIGN KEY (`dep_id`) REFERENCES `department` (`id`))
[2020-09-29 16:30:22] [23000][1452] Cannot add or update a child row: a foreign key constraint fails (`db1`.`employee`, CONSTRAINT `emp_depid_fk` FOREIGN KEY (`dep_id`) REFERENCES `department` (`id`))
這個時候同樣因為外鍵的關系,我們因為上面指定的外鍵dep_id是個不存在的值,所以提示了:Cannot add or update a child row: a foreign key constraint fails...
注意: 外鍵值可以是null
- 刪除外鍵
-- 刪除 employee 表的 emp_depid_fk 外鍵
alter table employee drop foreign key emp_depid_fk;
- 在employee 表已經創建的情況下,添加外鍵的方法
-- 在 employee 表情存在的情況下添加外鍵
alter table employee add constraint emp_depid_fk foreign key (dep_id) references department(id);
外鍵的級聯操作
還是我們的員工表和部門表,在我們已經建立了外鍵關系的情況下,加入我們更新了部門表中的id字段,這個時候需要怎么做?
想到的笨辦法就是把員工表中和這個id相關的數據的dep_id 設置為null, 然后update 部門表中的id, 最后更新員工表中的數據的dep_id 位更新后的id.但這顯然不是好辦法,太麻煩了,其實外鍵提供了更方便的設置,就是外鍵的級聯
| 級聯操作語法 | 描述 |
|---|---|
| ON UPDATE CASCADE | 級聯更新,只能是創建表的時候創建級聯關系。更新主表中的主鍵,從表中的外鍵 列也自動同步更新 |
| ON DELETE CASCADE | 級聯刪除 |
我們重新創建表,並添加級聯:
-- 創建部門表(id,dep_name,dep_location)
-- 一方,主表
create table department(
id int primary key auto_increment,
dep_name varchar(20),
dep_location varchar(20)
);
-- 創建員工表(id,name,age,dep_id)
-- 多方,從表
create table employee(
id int primary key auto_increment,
name varchar(20),
age int,
dep_id int,-- 外鍵對應主表的主鍵
-- 創建外鍵約束, 並設置級聯刪除和級聯更新
constraint emp_depid_fk foreign key (dep_id) references department(id) on update cascade on delete cascade
);
-- 添加 2 個部門
insert into department values(null, '研發部','廣州'),(null, '銷售部', '深圳');
select * from department;
-- 添加員工,dep_id 表示員工所在的部門
INSERT INTO employee (name, age, dep_id) VALUES ('張三', 20, 1);
INSERT INTO employee (name, age, dep_id) VALUES ('李四', 21, 1);
INSERT INTO employee (name, age, dep_id) VALUES ('王五', 20, 1);
INSERT INTO employee (name, age, dep_id) VALUES ('老王', 20, 2);
INSERT INTO employee (name, age, dep_id) VALUES ('大王', 22, 2);
INSERT INTO employee (name, age, dep_id) VALUES ('小王', 18, 2);
這樣當我們更新部門表的id的時候都同時更新員工表中的dep_id
同時刪除了部門的數據也會把員工表dep_id 等於 該部門iid的數據進行刪除
約束小結
| 約束名 | 關鍵字 | 說明 |
|---|---|---|
| 主鍵 | primary key | 1) 唯一 2) 非空 |
| 默認 | default | 如果一列沒有值,使用默認值 |
| 非空 | not null | 這一列必須有值 |
| 唯一 | unique | 這一列不能有重復值 |
| 外鍵 | foreign key | 主表中主鍵列,在從表中外鍵列 |
注意:外鍵的級聯慎用
數據庫的設計
多表之間的關系
分類
-
一對一
相對了來說用的場景比較少,
例如: 一個人和身份證號碼的關系,一個人只能有一個身份證號碼
-
一對多(多對一)
例如之前筆記中整理的:部門和員工的關系
一個部門可以有多個員工,一個員工只能屬於一個部門
-
多對多
例如學生和課程之間的關系
一個學院可以選修多門課程,同樣一門課程也可以被多個學生選修
實現關系
-
一對多(多對一)
如: 部門和員工的關系
實現方式:在多的一方(員工表)去建立外鍵,來指向一的一方(部門表)的主鍵
例子:-- 創建部門表(id,dep_name,dep_location) -- 一方,主表 create table department( id int primary key auto_increment, dep_name varchar(20), dep_location varchar(20) ); -- 創建員工表(id,name,age,dep_id) -- 多方,從表 create table employee( id int primary key auto_increment, name varchar(20), age int, dep_id int,-- 外鍵對應主表的主鍵 -- 創建外鍵約束, 並設置級聯刪除和級聯更新 constraint emp_depid_fk foreign key (dep_id) references department(id) on update cascade on delete cascade ); -
多對多
如:學生和課程的關系
實現方式:多對多關系實現需要借助第三張中間表,中間表至少包含兩個字段,這兩個字段作為第三張表的外鍵,分別指向兩張表(課程表和學生表)的主鍵
-
一對一
如:人和身份證
實現方式: 一對一關系實現,可以在任意一方添加唯一外鍵指向另一方的主鍵
備注: 這種用法較少,因為這個時候通常會合成一張表
實際案例
-
一對多案例
京東和淘寶的手機分類中會有各個手機品牌,而每個手機品牌下會有很多手機型號,這就是非常典型的一對多關系
-- 創建手機品牌類別表phone_category -- pid 手機分類主鍵自動增長 -- pname 手機類別的名字,如蘋果, 小米 create table phone_category ( cid int primary key auto_increment, cname varchar(20) not null unique ); -- 添加手機牌子 insert into phone_category (cname) values ("蘋果"), ("小米"),("三星"); -- 創建具體手機表 /* pid 手機表主鍵,自動增長 pname 手機名字 price 價格 cid 外鍵 */ create table phone_name ( pid int primary key auto_increment, pname varchar(20) not null unique , price double, cid int, constraint foreign key (cid) references phone_category(cid) ); -- 添加手機數據 insert into phone_name values (NULL, "IPhonex",4999.9,1), (NULL, "IPhone11",5999.9,1), (NUll, "mi6",1999.9,2), (NUll, "mi10",2999.9,2), (NUll, "s8",6999.9,3), (NUll, "s10",7999.9,3); -
多對多案例
還是關於手機的案例,接着上面一對多的案例,其實我們再添加一個關系手機存儲,就像手機有64G,128G, 256G, 不管那個手機都會有多個存儲的,所以這里的手機和存儲之間的關系就是多對多的關系
/* 創建手機存儲表 mid 存儲表的id,自增長 memory 內存大小,唯一,非空 */ create table phone_memory ( mid int primary key auto_increment, memory int not null unique ); -- 添加內存數據 insert into phone_memory values (NULL, 64), (NULL, 128), (NULL, 256); /* 創建第三張表存手機和內存之間的關系 mid 內存的id 外鍵 pid 手機的id 外鍵 */ create table phone_m2m_memory( mid int, pid int, primary key (mid,pid), foreign key (mid) references phone_memory(mid), foreign key (pid) references phone_name(pid) ); -- 增加手機內存數據 insert into phone_m2m_memory values (1, 1), (1,2), (1,3), (2,1), (2,2), (2,3), (3,1), (3,2), (3,3);
表與表關系小結
| 表與表關系 | 關系的維護 |
|---|---|
| 一對多 | 主外鍵的關系 |
| 多對多 | 中間表,兩個一對多 |
| 一對一 | 1)特殊一對多,從表中的外鍵設置為唯一 2)從表中的 |
數據庫設計的范式
概念: 在設計數據庫的時候需要遵循的規范。 要遵循后面的范式,必須先遵循前面的所有范式。
目前關系數據庫有六種范式:第一范式(1NF)、第二范式(2NF)、第三范式(3NF)、巴斯-科德范式(BCNF)、第四范式(4NF)和第五范式(5NF,又稱完美范式)。
掌握前三個設計出來的數據庫基本不會有啥大問題
第一范式(1NF):用一句話總結就是每一列是不可分割的原子數據項
第二范式(2NF): 在1NF的基礎上,非碼屬性必須完全依賴於候選碼(在1NF基礎上消除非主屬性對主碼的部分函數依賴)
概念補充:
-
函數依賴:
A-->B,如果通過A屬性(屬性組)的值,可以確定唯一B屬性的值。則稱B依 賴於A
例如:學號-->姓名。 (學號,課程名稱) --> 分數
-
完全函數依賴:
A-->B, 如果A是一個屬性組,則B屬性值得確定需要依賴於A屬性組中所有的屬性值。
例如:(學號,課程名稱) --> 分數
-
部分函數依賴:
A-->B, 如果A是一個屬性組,則B屬性值得確定只需要依賴於A屬性組中某一些值即可。
例如:(學號,課程名稱) -- > 姓名
-
傳遞函數依賴:
A-->B, B -- >C . 如果通過A屬性(屬性組)的值,可以確定唯一B屬性的值,在通過B屬性(屬性組)的值可以確定唯一C屬性的值,則稱 C 傳遞函數依賴於A
例如:學號-->系名,系名-->系主任
-
碼:
如果在一張表中,一個屬性或屬性組,被其他所有屬性所完全依賴,則稱這個屬性(屬性組)為該表的碼
例如:該表中碼為:(學號,課程名稱)
主屬性:碼屬性組中的所有屬性
非主屬性:除了碼屬性組的屬性
傳遞函數依賴:
A-->B, B -- >C . 如果通過A屬性(屬性組)的值,可以確定唯一B屬性的值,在通過B屬性(屬性組)的值可以確定唯一C屬性的值,則稱 C 傳遞函數依賴於A
第三范式 (3NF): 在2NF基礎上,任何非主屬性不依賴於其它非主屬性(在2NF基礎上消除傳遞依賴)
案例


多表查詢
基礎數據
create table dept(
id int primary key auto_increment,
name varchar(20)
);
insert into dept(name) values('研發部'),('市場部'),('財務部');
create table emp(
id int primary key auto_increment,
name varchar(10),
gender char(1), -- 性別
salary double, -- 工資
join_date date, -- 入職日期
dept_id INT,
foreign key (dept_id) references dept(id) -- 外鍵,關聯部門表的主鍵
);
insert into emp(name, gender,salary, join_date,dept_id) values('張三','男', 12000, '2020-01-01',1);
insert into emp(name, gender,salary, join_date,dept_id) values('李四','男', 22000, '2019-03-01',2);
insert into emp(name, gender,salary, join_date,dept_id) values('王五','女', 3200, '2025-02-01',3);
insert into emp(name, gender,salary, join_date,dept_id) values('哈哈','女', 1000, '2000-12-01',1);
多表查詢
內連接查詢
隱式內連接
使用where條件消除無用信息
例如:查詢所有員工信息和對應的部門信息
select * from emp,dept where emp.dept_id=dept.id
例如:查詢員工表的名稱,性別,部門表的名稱
select emp.name, gender,dept.name from emp,dept where emp.dept_id= dept.id;
select
t1.name,t1.gender,t2.name
from
emp t1, dept t2
where
t1.dept_id=t2.id;
顯式內連接
語法: select 字段列表 from 表名 inner join 表名2 on 條件
inner 關鍵字可以省略
select * from emp inner join dept on emp.dept_id = dept.id;
外連接查詢
左外連接
語法:select 字段列表 from 表1 left outer join 表2 on 條件
outer 關鍵字可以省略
select
t1.*,t2.name
from
emp t1
left join
dept t2
on
t1.dept_id=t2.id;
總結過可以看出其實左外連接查詢的是左表所有數據以及其他交集部分
右外連接
語法:select 字段列表 from 表1 right outer join 表2 on 條件
outer 關鍵字可以省略
其實右外連接查詢的是右表所有數據以及其他交集部分
select
t1.*,t2.name
from
emp t1
right join
dept t2
on
t1.dept_id=t2.id;
子查詢
概念: 查詢中嵌套查詢,稱為子查詢.
select * from emp where emp.salary=(select max(salary) from emp);
子查詢包含幾種不同的情況:
- 子查詢的結果是單行單列
- 子查詢的結果是多行單列的
- 子查詢的結果是多行多列的
子查詢的結果是單行單列
子查詢可以作為條件,使用運算符去判斷。 運算符: > , >= , <=, =
--- 查詢員工工資小於平均工資的人
select * from emp where emp.salary < (select avg(salary) from emp);
子查尋的結果是多行單列
子查詢可以作為條件,使用運算符in來判斷
-- 查詢財務部和市場部所有員工的信息
select * from emp where dept_id in (select id from dept where name='財務部' or name='市場部');
子查詢的結果是多行多列的
-- 查詢入職日期在2019-01-01 之后的員工信息和部門信息
select * from dept t1, (select * from emp where emp.join_date>'2019-01-01') t2 where t1.id = t2.id;
-- 通過內連接也可以;
select * from dept t1, emp t2 where t2.join_date>'2019-01-01' and t1.id=t2.id;
多表查詢練習
基礎數據
-- 部門表
CREATE TABLE dept (
id INT PRIMARY KEY PRIMARY KEY, -- 部門id
dname VARCHAR(50), -- 部門名稱
loc VARCHAR(50) -- 部門所在地
);
-- 添加4個部門
INSERT INTO dept(id,dname,loc) VALUES
(10,'教研部','北京'),
(20,'學工部','上海'),
(30,'銷售部','廣州'),
(40,'財務部','深圳');
-- 職務表,職務名稱,職務描述
CREATE TABLE job (
id INT PRIMARY KEY,
jname VARCHAR(20),
description VARCHAR(50)
);
-- 添加4個職務
INSERT INTO job (id, jname, description) VALUES
(1, '董事長', '管理整個公司,接單'),
(2, '經理', '管理部門員工'),
(3, '銷售員', '向客人推銷產品'),
(4, '文員', '使用辦公軟件');
-- 員工表
CREATE TABLE emp (
id INT PRIMARY KEY, -- 員工id
ename VARCHAR(50), -- 員工姓名
job_id INT, -- 職務id
mgr INT , -- 上級領導
joindate DATE, -- 入職日期
salary DECIMAL(7,2), -- 工資
bonus DECIMAL(7,2), -- 獎金
dept_id INT, -- 所在部門編號
CONSTRAINT emp_jobid_ref_job_id_fk FOREIGN KEY (job_id) REFERENCES job (id),
CONSTRAINT emp_deptid_ref_dept_id_fk FOREIGN KEY (dept_id) REFERENCES dept (id)
);
-- 添加員工
INSERT INTO emp(id,ename,job_id,mgr,joindate,salary,bonus,dept_id) VALUES
(1001,'孫悟空',4,1004,'2000-12-17','8000.00',NULL,20),
(1002,'盧俊義',3,1006,'2001-02-20','16000.00','3000.00',30),
(1003,'林沖',3,1006,'2001-02-22','12500.00','5000.00',30),
(1004,'唐僧',2,1009,'2001-04-02','29750.00',NULL,20),
(1005,'李逵',4,1006,'2001-09-28','12500.00','14000.00',30),
(1006,'宋江',2,1009,'2001-05-01','28500.00',NULL,30),
(1007,'劉備',2,1009,'2001-09-01','24500.00',NULL,10),
(1008,'豬八戒',4,1004,'2007-04-19','30000.00',NULL,20),
(1009,'羅貫中',1,NULL,'2001-11-17','50000.00',NULL,10),
(1010,'吳用',3,1006,'2001-09-08','15000.00','0.00',30),
(1011,'沙僧',4,1004,'2007-05-23','11000.00',NULL,20),
(1012,'李逵',4,1006,'2001-12-03','9500.00',NULL,30),
(1013,'小白龍',4,1004,'2001-12-03','30000.00',NULL,20),
(1014,'關羽',4,1007,'2002-01-23','13000.00',NULL,10);
-- 工資等級表
CREATE TABLE salarygrade (
grade INT PRIMARY KEY, -- 級別
losalary INT, -- 最低工資
hisalary INT -- 最高工資
);
-- 添加5個工資等級
INSERT INTO salarygrade(grade,losalary,hisalary) VALUES
(1,7000,12000),
(2,12010,14000),
(3,14010,20000),
(4,20010,30000),
(5,30010,99990);
-- 需求:
-- 1.查詢所有員工信息。查詢員工編號,員工姓名,工資,職務名稱,職務描述
-- 2.查詢員工編號,員工姓名,工資,職務名稱,職務描述,部門名稱,部門位置
-- 3.查詢員工姓名,工資,工資等級
-- 4.查詢員工姓名,工資,職務名稱,職務描述,部門名稱,部門位置,工資等級
-- 5.查詢出部門編號、部門名稱、部門位置、部門人數
-- 6.查詢所有員工的姓名及其直接上級的姓名,沒有領導的員工也需要查詢
-
查詢所有員工信息。查詢員工編號,員工姓名,工資,職務名稱,職務描述
-- 查詢所有員工信息。查詢員工編號,員工姓名,工資,職務名稱,職務描述 select t1.id,t1.ename,t1.salary,t2.jname,t2.description from emp t1, job t2 where t1.job_id = t2.id; -
查詢員工編號,員工姓名,工資,職務名稱,職務描述,部門名稱,部門位置
-- 查詢員工編號,員工姓名,工資,職務名稱,職務描述,部門名稱,部門位置 select t1.id,t1.ename,t1.salary,t2.jname,t2.description,t3.dname,t3.loc from emp t1, job t2, dept t3 where t1.job_id = t2.id and t1.dept_id=t3.id; -
查詢員工姓名,工資,工資等級
-- 查詢員工姓名,工資,工資等級 select t1.ename, t1.salary, (select grade from salarygrade where t1.salary> losalary and salary<hisalary) grade from emp t1或者
select t1.ename,t1.salary,t2.grade from emp t1, salarygrade t2 where t1.salary > t2.losalary and t1.salary < t2.hisalary; -
查詢員工姓名,工資,職務名稱,職務描述,部門名稱,部門位置,工資等級
-- 查詢員工姓名,工資,職務名稱,職務描述,部門名稱,部門位置,工資等級 select t1.id,t1.ename,t1.salary,t2.jname,t2.description,t3.dname,t3.loc ,t4.grade from emp t1, job t2, dept t3, salarygrade t4 where t1.job_id = t2.id and t1.dept_id=t3.id and (t1.salary > t4.losalary and t1.salary < t4.hisalary); -
查詢出部門編號、部門名稱、部門位置、部門人數
-- 查詢出部門編號、部門名稱、部門位置、部門人數 select t1.dname, t1.loc, (select count(id) from emp where emp.dept_id=t1.id ) count from dept t1;select t1.id, t1.dname,t1.loc, t2.total from dept t1, (select dept_id,count(id) total from emp group by dept_id) t2 where t1.id = t2.dept_id -
查詢所有員工的姓名及其直接上級的姓名,沒有領導的員工也需要查詢
-- 查詢所有員工的姓名及其直接上級的姓名,沒有領導的員工也需要查詢 select t1.ename, (select ename from emp where t1.mgr=id)from emp t1;select t1.ename, t1.mgr, t2.id, t2.ename from emp t1 left join emp t2 on t1.mgr = t2.id
事務
事務的基本介紹
概念:如果一個包含多個步驟的業務操作,被事務管理,那么這些操作要么同時成功,要么同時失敗
操作:
- 開啟事務:start transaction
- 回滾:rollback
- 提交:commit
注意:
Mysql數據庫中事務默認自動提交
一條DML(增刪改)語句會自動提交奧一次事務
事務提交的兩種方式:
自動提交:mysql的默認方式
手動提交:需要先開啟事務,再提交
查看事務的默認提交方式:
select @@autocommit; -- 1 表示自動提交,0表示手動提交
如果想要更改:
set @@autocommit = 0;
事務的四大特征
- 原子性:是不可分割的最小單位,要么同時成功,要么同時失敗
- 持久性:當事務提交或回滾后,數據會持久化的保存數據
- 隔離性: 多個事務之間。相互獨立
- 一致性: 事務操作前后,數據總量不變
事務的隔離級別
概念:多個事務之間是相互隔離的,相互獨立。但是多個事務操作同一批數據,會引發一些問題。設置不同的隔離級別就卡一解決這些問題
存在的問題:
- 臟讀:一個事務,讀取到另外一個事務中沒有提交的數據
- 不可重復讀(虛讀):在同一個事務中,兩次讀取到的數據不一樣
- 幻讀:一個事務操作數據表中所有記錄,另外一個事務添加了一條數據,則第一個事務查詢不到自己的修改
隔離級別:
- read umcommitted:讀未提交。 會產生的問題:臟讀,不可重復讀,幻讀
- read commit:讀已提交。會產生的問題:不可重復讀,幻讀
- repeatable read: 可重復讀。 會產生幻讀 (Mysql默認級別)
- serializable: 串行化。 可以解決所有的問題
注意:隔離級別從小到大安全性越來越高,但是效率越來越低
數據庫查詢隔離級別:
select @@tx_isolation;
數據設置隔離級別:
set global transaction isolation level 級別字符串


