1. 程式人生 > 實用技巧 >MYSQL多表查詢相關

MYSQL多表查詢相關

1,普通

#查詢人員和部門所有資訊
select * from person,dept where person.did = dept.did;
#注意: 多表查詢時,一定要找到兩個表中相互關聯的欄位,並且作為條件使用

2,連線查詢

#多表連線查詢語法(重點) SELECT 欄位列表 FROM 表1 INNER|LEFT|RIGHT JOIN 表2 ON 表1.欄位 = 表2.欄位; 2.1, 內連線查詢 (只顯示符合條件的資料)
1 2 #查詢人員和部門所有資訊 select*fromperson innerjoindeptonperson.did =dept.did;

 效果: 內連線查詢與多表聯合查詢的效果是一樣的

2.2, 左外連線查詢 (左邊表中的資料優先全部顯示)
1 2 #查詢人員和部門所有資訊 select*fromperson leftjoindeptonperson.did =dept.did;

 效果:人員表中的資料全部都顯示,而 部門表中的資料符合條件的才會顯示,不符合條件的會以 null 進行填充.

2.3,右外連線查詢 (右邊表中的資料優先全部顯示)

1 2 #查詢人員和部門所有資訊 select*fromperson rightjoindeptonperson.did =dept.did;

 效果:正好與[左外連線相反]

2.4全連線查詢(顯示左右表中全部資料)

  全連線查詢:是在內連線的基礎上增加 左右兩邊沒有顯示的資料
  注意: mysql並不支援全連線 full JOIN 關鍵字
  注意: 但是mysql 提供了 UNION 關鍵字.使用 UNION 可以間接實現 full JOIN 功能

1 2 3 4 5 #查詢人員和部門的所有資料 SELECT * FROM person LEFT JOIN dept ON person.did = dept.did UNION SELECT * FROM person RIGHT JOIN dept ON person.did = dept.did;

3,複雜條件多表查詢

3.1,查詢出 教學部 年齡大於20歲,並且工資小於40000的員工,按工資倒序排列.(要求:分別使用多表聯合查詢和內連線查詢)

#1.多表聯合查詢方式:
select * from person p1,dept d2 where p1.did = d2.did  
    and d2.dname='python' 
    and  age>20 
    and salary <40000 
ORDER BY salary DESC;

#2.內連線查詢方式:
SELECT * FROM person p1 INNER JOIN dept d2 ON p1.did= d2.did 
    and d2.dname='python' 
    and  age>20 
    and salary <40000 
ORDER BY salary DESC;    
3.2,查詢每個部門中最高工資和最低工資是多少,顯示部門名稱
select MAX(salary),MIN(salary),dept.dname from 
        person LEFT JOIN dept
            ON person.did = dept.did
 GROUP BY person.did;

4,子語句查詢

4.1,

子查詢(巢狀查詢): 查多次, 多個select

注意: 第一次的查詢結果可以作為第二次的查詢的 條件 或者 表名 使用.

子查詢中可以包含:IN、NOT IN、ANY、ALL、EXISTS 和 NOT EXISTS等關鍵字.還可以包含比較運算子:= 、 !=、> 、<等.

1.作為表名使用
select * from (select * from person) as 表名;
 
ps:大家需要注意的是: 一條語句中可以有多個這樣的子查詢,在執行時,最裡層括號(sql語句) 具有優先執行權.<br>注意: as 後面的表名稱不能加引號('')

4.2,求最大工資那個人的姓名和薪水

1.求最大工資
select max(salary) from person;
2.求最大工資那個人叫什麼
select name,salary from person where salary=53000;
合併
select name,salary from person where salary=(select max(salary) from person);

4.3,求工資高於所有人員平均工資的人員

1.求平均工資
select avg(salary) from person;

2.工資大於平均工資的 人的姓名、工資
select name,salary from person where salary > 21298.625;
合併
select name,salary from person where salary >(select avg(salary) from person);

4.4,關鍵字

假設any內部的查詢語句返回的結果個數是三個,如:result1,result2,result3,那麼,

select ...from ... where a > any(...);
->
select ...from ... where a > result1 or a > result2 or a > result3;

4.5,ALL關鍵字與any關鍵字類似,只不過上面的or改成and。

即: select ...from ... where a > all(...); -> select ...from ... where a > result1 and a > result2 and a > result3;

4.6,some關鍵字和any關鍵字是一樣的功能。

所以: select ...from ... where a > some(...); -> select ...from ... where a > result1 or a > result2 or a > result3;

4.7,EXISTS 和 NOT EXISTS 子查詢語法如下:


  SELECT ... FROM table WHERE  EXISTS (subquery)
該語法可以理解為:主查詢(外部查詢)會根據子查詢驗證結果(TRUE 或 FALSE)來決定主查詢是否得以執行。

mysql> SELECT * FROM person
    -> WHERE EXISTS
    -> (SELECT * FROM dept WHERE did=5);
Empty set (0.00 sec)
此處內層迴圈並沒有查詢到滿足條件的結果,因此返回false,外層查詢不執行。

NOT EXISTS剛好與之相反

mysql> SELECT * FROM person 
    -> WHERE NOT EXISTS 
    -> (SELECT * FROM dept WHERE did=5);
+----+----------+-----+-----+--------+------+
| id | name     | age | sex | salary | did  |
+----+----------+-----+-----+--------+------+
|  1 | alex     |  28 | 女  |  53000 |    1 |
|  2 | wupeiqi  |  23 | 女  |  29000 |    1 |
|  3 | egon     |  30 | 男  |  27000 |    1 |
|  4 | oldboy   |  22 | 男  |      1 |    2 |
|  5 | jinxin   |  33 | 女  |  28888 |    1 |
|  6 | 張無忌   |  20 | 男  |   8000 |    3 |
|  7 | 令狐沖   |  22 | 男  |   6500 |    2 |
|  8 | 東方不敗 |  23 | 女  |  18000 | NULL |
+----+----------+-----+-----+--------+------+
8 rows in set

當然,EXISTS關鍵字可以與其他的查詢條件一起使用,條件表示式與EXISTS關鍵字之間用AND或者OR來連線,如下:

mysql> SELECT * FROM person 
    -> WHERE AGE >23 AND NOT EXISTS 
    -> (SELECT * FROM dept WHERE did=5);
提示:
•EXISTS (subquery) 只返回 TRUE 或 FALSE,因此子查詢中的 SELECT * 也可以是 SELECT 1 或其他,官方說法是實際執行時會忽略 SELECT 清單,因此沒有區別。

5,其他方式查詢

5.1,臨時表查詢

需求: 查詢高於本部門平均工資的人員

解析思路: 1.先查詢本部門人員平均工資是多少.

     2.再使用人員的工資與部門的平均工資進行比較

#1.先查詢部門人員的平均工資
SELECT dept_id,AVG(salary)as sal from person GROUP BY dept_id;
 
#2.再用人員的工資與部門的平均工資進行比較
SELECT * FROM person as p1,
    (SELECT dept_id,AVG(salary)as '平均工資' from person GROUP BY dept_id) as p2
where p1.dept_id = p2.dept_id AND p1.salary >p2.`平均工資`;

ps:在當前語句中,我們可以把上一次的查詢結果當前做一張表來使用.因為p2表不是真是存在的,所以:我們稱之為 臨時表  
   臨時表:不侷限於自身表,任何的查詢結果集都可以認為是一個臨時表.

5.2,判斷查詢 IF關鍵字

需求1 :根據工資高低,將人員劃分為兩個級別,分別為 高階人群和低端人群。顯示效果:姓名,年齡,性別,工資,級別

select p1.*, 
    
    IF(p1.salary >10000,'高階人群','低端人群') as '級別'
 
from person p1;

#ps: 語法: IF(條件表示式,"結果為true",'結果為false');

5.3,需求2: 根據工資高低,統計每個部門人員收入情況,劃分為 富人,小資,平民,吊絲 四個級別, 要求統計四個級別分別有多少人

#語法一:
SELECT
    CASE WHEN STATE = '1' THEN '成功'
         WHEN STATE = '2' THEN '失敗'
         ELSE '其他' END 
FROM 表;
 
#語法二:
SELECT CASE age
           WHEN 23 THEN '23歲'
           WHEN 27 THEN '27歲'
           WHEN 30 THEN '30歲'
        ELSE '其他歲' END
FROM person;

SELECT dname '部門',
             sum(case WHEN salary >50000 THEN 1 ELSE 0 end) as '富人',
             sum(case WHEN salary between 29000 and 50000 THEN 1 ELSE 0 end) as '小資',
             sum(case WHEN salary between 10000 and 29000 THEN 1 ELSE 0 end) as '平民',
             sum(case WHEN salary <10000 THEN 1 ELSE 0 end) as '吊絲'
FROM person,dept where person.dept_id = dept.did GROUP BY dept_id

6,SQL邏輯查詢語句執行順序(重點)

在這些SQL語句的執行過程中,都會產生一個虛擬表,用來儲存SQL語句的執行結果(這是重點),我們現在就來跟蹤這個虛擬表的變化,得到最終的查詢結果的過程,來分析整個SQL邏輯查詢的執行順序和過程。

6.1.執行FROM語句

第一步,執行FROM語句。我們首先需要知道最開始從哪個表開始的,這就是FROM告訴我們的。現在有了<left_table><right_table>兩個表,我們到底從哪個表開始,還是從兩個表進行某種聯絡以後再開始呢?它們之間如何產生聯絡呢?——笛卡爾積

經過FROM語句對兩個表執行笛卡爾積,會得到一個虛擬表,暫且叫VT1(vitual table 1),內容如下:

+-------------+----------+----------+-------------+
| customer_id | city     | order_id | customer_id |
+-------------+----------+----------+-------------+
| 163         | hangzhou |        1 | 163         |
| 9you        | shanghai |        1 | 163         |
| baidu       | hangzhou |        1 | 163         |
| tx          | hangzhou |        1 | 163         |
| 163         | hangzhou |        2 | 163         |
| 9you        | shanghai |        2 | 163         |
| baidu       | hangzhou |        2 | 163         |
| tx          | hangzhou |        2 | 163         |
| 163         | hangzhou |        3 | 9you        |
| 9you        | shanghai |        3 | 9you        |
| baidu       | hangzhou |        3 | 9you        |
| tx          | hangzhou |        3 | 9you        |
| 163         | hangzhou |        4 | 9you        |
| 9you        | shanghai |        4 | 9you        |
| baidu       | hangzhou |        4 | 9you        |
| tx          | hangzhou |        4 | 9you        |
| 163         | hangzhou |        5 | 9you        |
| 9you        | shanghai |        5 | 9you        |
| baidu       | hangzhou |        5 | 9you        |
| tx          | hangzhou |        5 | 9you        |
| 163         | hangzhou |        6 | tx          |
| 9you        | shanghai |        6 | tx          |
| baidu       | hangzhou |        6 | tx          |
| tx          | hangzhou |        6 | tx          |
| 163         | hangzhou |        7 | NULL        |
| 9you        | shanghai |        7 | NULL        |
| baidu       | hangzhou |        7 | NULL        |
| tx          | hangzhou |        7 | NULL        |
+-------------+----------+----------+-------------+

總共有28(table1的記錄條數 * table2的記錄條數)條記錄。這就是VT1的結果,接下來的操作就在VT1的基礎上進行。

6.2.執行ON過濾

執行完笛卡爾積以後,接著就進行ON a.customer_id = b.customer_id條件過濾,根據ON中指定的條件,去掉那些不符合條件的資料,得到VT2表,內容如下:

+-------------+----------+----------+-------------+
| customer_id | city     | order_id | customer_id |
+-------------+----------+----------+-------------+
| 163         | hangzhou |        1 | 163         |
| 163         | hangzhou |        2 | 163         |
| 9you        | shanghai |        3 | 9you        |
| 9you        | shanghai |        4 | 9you        |
| 9you        | shanghai |        5 | 9you        |
| tx          | hangzhou |        6 | tx          |
+-------------+----------+----------+-------------+

T2就是經過ON條件篩選以後得到的有用資料,而接下來的操作將在VT2的基礎上繼續進行。

6.3.新增外部行

這一步只有在連線型別為OUTER JOIN時才發生,如LEFT OUTER JOINRIGHT OUTER JOIN。在大多數的時候,我們都是會省略掉OUTER關鍵字的,但OUTER表示的就是外部行的概念。

LEFT OUTER JOIN把左表記為保留表,得到的結果為:

+-------------+----------+----------+-------------+
| customer_id | city     | order_id | customer_id |
+-------------+----------+----------+-------------+
| 163         | hangzhou |        1 | 163         |
| 163         | hangzhou |        2 | 163         |
| 9you        | shanghai |        3 | 9you        |
| 9you        | shanghai |        4 | 9you        |
| 9you        | shanghai |        5 | 9you        |
| tx          | hangzhou |        6 | tx          |
| baidu       | hangzhou |     NULL | NULL        |
+-------------+----------+----------+-------------+

RIGHT OUTER JOIN把右表記為保留表,得到的結果為:

+-------------+----------+----------+-------------+
| customer_id | city     | order_id | customer_id |
+-------------+----------+----------+-------------+
| 163         | hangzhou |        1 | 163         |
| 163         | hangzhou |        2 | 163         |
| 9you        | shanghai |        3 | 9you        |
| 9you        | shanghai |        4 | 9you        |
| 9you        | shanghai |        5 | 9you        |
| tx          | hangzhou |        6 | tx          |
| NULL        | NULL     |        7 | NULL        |
+-------------+----------+----------+-------------+

新增外部行的工作就是在VT2表的基礎上新增保留表中被過濾條件過濾掉的資料,非保留表中的資料被賦予NULL值,最後生成虛擬表VT3。

由於我在準備的測試SQL查詢邏輯語句中使用的是LEFT JOIN,過濾掉了以下這條資料:

| baidu       | hangzhou |     NULL | NULL        |

現在就把這條資料新增到VT2表中,得到的VT3表如下:

+-------------+----------+----------+-------------+
| customer_id | city     | order_id | customer_id |
+-------------+----------+----------+-------------+
| 163         | hangzhou |        1 | 163         |
| 163         | hangzhou |        2 | 163         |
| 9you        | shanghai |        3 | 9you        |
| 9you        | shanghai |        4 | 9you        |
| 9you        | shanghai |        5 | 9you        |
| tx          | hangzhou |        6 | tx          |
| baidu       | hangzhou |     NULL | NULL        |
+-------------+----------+----------+-------------+

接下來的操作都會在該VT3表上進行。

6.4.執行WHERE過濾

對新增外部行得到的VT3進行WHERE過濾,只有符合<where_condition>的記錄才會輸出到虛擬表VT4中。當我們執行WHERE a.city = 'hangzhou'的時候,就會得到以下內容,並存在虛擬表VT4中:

+-------------+----------+----------+-------------+
| customer_id | city     | order_id | customer_id |
+-------------+----------+----------+-------------+
| 163         | hangzhou |        1 | 163         |
| 163         | hangzhou |        2 | 163         |
| tx          | hangzhou |        6 | tx          |
| baidu       | hangzhou |     NULL | NULL        |
+-------------+----------+----------+-------------+

但是在使用WHERE子句時,需要注意以下兩點:

  1. 由於資料還沒有分組,因此現在還不能在WHERE過濾器中使用where_condition=MIN(col)這類對分組統計的過濾;
  2. 由於還沒有進行列的選取操作,因此在SELECT中使用列的別名也是不被允許的,如:SELECT city as c FROM t WHERE c='shanghai';是不允許出現的。

6.5.執行GROUP BY分組

GROU BY子句主要是對使用WHERE子句得到的虛擬表進行分組操作。我們執行測試語句中的GROUP BY a.customer_id,就會得到以下內容:

+-------------+----------+----------+-------------+
| customer_id | city     | order_id | customer_id |
+-------------+----------+----------+-------------+
| 163         | hangzhou |        1 | 163         |
| baidu       | hangzhou |     NULL | NULL        |
| tx          | hangzhou |        6 | tx          |
+-------------+----------+----------+-------------+

得到的內容會存入虛擬表VT5中,此時,我們就得到了一個VT5虛擬表,接下來的操作都會在該表上完成。

6.6.執行HAVING過濾

HAVING子句主要和GROUP BY子句配合使用,對分組得到的VT5虛擬表進行條件過濾。當我執行測試語句中的HAVING count(b.order_id) < 2時,將得到以下內容:

+-------------+----------+----------+-------------+
| customer_id | city     | order_id | customer_id |
+-------------+----------+----------+-------------+
| baidu       | hangzhou |     NULL | NULL        |
| tx          | hangzhou |        6 | tx          |
+-------------+----------+----------+-------------+

這就是虛擬表VT6。

6.7.SELECT列表

現在才會執行到SELECT子句,不要以為SELECT子句被寫在第一行,就是第一個被執行的。

我們執行測試語句中的SELECT a.customer_id, COUNT(b.order_id) as total_orders,從虛擬表VT6中選擇出我們需要的內容。我們將得到以下內容:

+-------------+--------------+
| customer_id | total_orders |
+-------------+--------------+
| baidu       |            0 |
| tx          |            1 |
+-------------+--------------+

不,還沒有完,這只是虛擬表VT7。

6.8.執行DISTINCT子句

如果在查詢中指定了DISTINCT子句,則會建立一張記憶體臨時表(如果記憶體放不下,就需要存放在硬碟了)。這張臨時表的表結構和上一步產生的虛擬表VT7是一樣的,不同的是對進行DISTINCT操作的列增加了一個唯一索引,以此來除重複資料。

由於我的測試SQL語句中並沒有使用DISTINCT,所以,在該查詢中,這一步不會生成一個虛擬表。

6.9.執行ORDER BY子句

對虛擬表中的內容按照指定的列進行排序,然後返回一個新的虛擬表,我們執行測試SQL語句中的ORDER BY total_orders DESC,就會得到以下內容:

+-------------+--------------+
| customer_id | total_orders |
+-------------+--------------+
| tx          |            1 |
| baidu       |            0 |
+-------------+--------------+

可以看到這是對total_orders列進行降序排列的。上述結果會儲存在VT8中。

6.10.執行LIMIT子句

LIMIT子句從上一步得到的VT8虛擬表中選出從指定位置開始的指定行資料。對於沒有應用ORDER BY的LIMIT子句,得到的結果同樣是無序的,所以,很多時候,我們都會看到LIMIT子句會和ORDER BY子句一起使用。

MySQL資料庫的LIMIT支援如下形式的選擇:

LIMIT n, m

表示從第n條記錄開始選擇m條記錄。而很多開發人員喜歡使用該語句來解決分頁問題。對於小資料,使用LIMIT子句沒有任何問題,當資料量非常大的時候,使用LIMIT n, m是非常低效的。因為LIMIT的機制是每次都是從頭開始掃描,如果需要從第60萬行開始,讀取3條資料,就需要先掃描定位到60萬行,然後再進行讀取,而掃描的過程是一個非常低效的過程。所以,對於大資料處理時,是非常有必要在應用層建立一定的快取機制(貌似現在的大資料處理,都有快取哦).

7,外來鍵約束

1.問題?

  什麼是約束:約束是一種限制,它通過對錶的行或列的資料做出限制,來確保表的資料的完整性、唯一性

2.問題?

  以上兩個表 person和dept中, 新人員可以沒有部門嗎?

3.問題?

  新人員可以新增一個不存在的部門嗎?

4.如何解決以上問題呢?

  簡單的說,就是對兩個表的關係進行一些約束(即: froegin key).

  foreign key 定義:就是表與表之間的某種約定的關係,由於這種關係的存在,能夠讓表與表之間的資料,更加的完整,關連性更強。

5.具體操作

5.1建立表時,同時建立外來鍵約束

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 CREATE TABLE IF NOT EXISTS dept ( did int not null auto_increment PRIMARY KEY, dname VARCHAR(50) not null COMMENT '部門名稱' )ENGINE=INNODB DEFAULT charset utf8; CREATE TABLE IF NOT EXISTS person( id int not null auto_increment PRIMARY KEY, name VARCHAR(50) not null, age TINYINT(4) null DEFAULT 0, sex enum('男','女','人妖') NOT NULL DEFAULT '人妖', salary decimal(10,2) NULL DEFAULT '250.00', hire_date date NOT NULL, dept_id int(11) DEFAULT NULL,   CONSTRAINT fk_did FOREIGN KEY(dept_id) REFERENCES dept(did) -- 新增外來鍵約束 )ENGINE = INNODB DEFAULT charset utf8;

5.2 已經建立表後,追加外來鍵約束

1 2 3 4 5 #新增外來鍵約束 ALTER table person add constraint fk_did FOREIGN key(dept_id) REFERENCES dept(did); #刪除外來鍵約束 ALTER TABLE person drop FOREIGN key fk_did;

定義外來鍵的條件:

(1)外來鍵對應的欄位資料型別保持一致,且被關聯的欄位(即references指定的另外一個表的欄位),必須保證唯一

(2)所有tables的儲存引擎必須是InnoDB型別.

(3)外來鍵的約束4種類型: 1.RESTRICT 2.NO ACTION 3.CASCADE 4.SET NULL

RESTRICT
同no action, 都是立即檢查外來鍵約束

NO ACTION
如果子表中有匹配的記錄,則不允許對父表對應候選鍵進行update/delete操作  

CASCADE
在父表上update/delete記錄時,同步update/delete掉子表的匹配記錄 

SET NULL
在父表上update/delete記錄時,將子表上匹配記錄的列設為null (要注意子表的外來鍵列不能為not null) 

(4)建議:1.如果需要外來鍵約束,最好建立表同時建立外來鍵約束.

     2.如果需要設定級聯關係,刪除時最好設定為 SET NULL.

注:插入資料時,先插入主表中的資料,再插入從表中的資料。

刪除資料時,先刪除從表中的資料,再刪除主表中的資料。

8,其他約束型別

1.非空約束

關鍵字:NOT NULL ,表示 不可空. 用來約束表中的欄位列

create table t1(
       id int(10) not null primary key,
       name varchar(100) null
 );    

2.主鍵約束

用於約束表中的一行,作為這一行的識別符號,在一張表中通過主鍵就能準確定位到一行,因此主鍵十分重要。

create table t2(
    id int(10) not null primary key
);

注意: 主鍵這一行的資料不能重複不能為空

還有一種特殊的主鍵——複合主鍵。主鍵不僅可以是表中的一列,也可以由表中的兩列或多列來共同標識

create table t3(
    id int(10) not null,
    name varchar(100) ,
    primary key(id,name)
);

3.唯一約束

關鍵字:UNIQUE,比較簡單,它規定一張表中指定的一列的值必須不能有重複值,即這一列每個值都是唯一的。

create table t4(
    id int(10) not null,
    name varchar(255) ,
    unique id_name(id,name)
);
//新增唯一約束
alter table t4 add unique id_name(id,name);
//刪除唯一約束
alter table t4 drop index id_name;

注意: 當INSERT語句新插入的資料和已有資料重複的時候,如果有UNIQUE約束,則INSERT失敗. 

4.預設值約束

關鍵字:DEFAULT

create table t5(
    id int(10) not null primary key,
    name varchar(255) default '張三'   
);
#插入資料
INSERT into t5(id) VALUES(1),(2);

注意: INSERT語句執行時.,如果被DEFAULT約束的位置沒有值,那麼這個位置將會被DEFAULT的值填充

9,表間關係

1.表關係分類:

  總體可以分為三類: 一對一 、一對多(多對一) 、多對多

2.如何區分表與表之間是什麼關係?

#分析步驟:
#多對一 /一對多
#1.站在左表的角度去看右表(情況一)
如果左表中的一條記錄,對應右表中多條記錄.那麼他們的關係則為 一對多 關係.約束關係為:左表普通欄位, 對應右表foreign key 欄位.

注意:如果左表與右表的情況反之.則關係為 多對一 關係.約束關係為:左表foreign key 欄位, 對應右表普通欄位.

#一對一
#2.站在左表的角度去看右表(情況二)
如果左表中的一條記錄 對應 右表中的一條記錄. 則關係為 一對一關係.
約束關係為:左表foreign key欄位上 新增唯一(unique)約束, 對應右表 關聯欄位.
或者:右表foreign key欄位上 新增唯一(unique)約束, 對應右表 關聯欄位.

#多對多
#3.站在左表和右表同時去看(情況三)
如果左表中的一條記錄 對應 右表中的多條記錄,並且右表中的一條記錄同時也對應左表的多條記錄. 那麼這種關係 則 多對多 關係. 
這種關係需要定義一個這兩張表的[關係表]來專門存放二者的關係

3.建立表關係

1.一對多關係

 例如:一個人可以擁有多輛汽車,要求查詢某個人擁有的所有車輛。
 分析:人和車輛分別單獨建表,那麼如何將兩個表關聯呢?有個巧妙的方法,在車輛的表中加個外來鍵欄位(人的編號)即可。
 * (思路小結:’建兩個表,一’方不動,’多’方新增一個外來鍵欄位)*

//建立人員表
CREATE TABLE people(
    id VARCHAR(12) PRIMARY KEY,
    sname VARCHAR(12),
    age INT,
    sex CHAR(1)
);
INSERT INTO people VALUES('H001','小王',27,'1');
INSERT INTO people VALUES('H002','小明',24,'1');
INSERT INTO people VALUES('H003','張慧',28,'0');
INSERT INTO people VALUES('H004','李小燕',35,'0');
INSERT INTO people VALUES('H005','王大拿',29,'1');
INSERT INTO people VALUES('H006','周強',36,'1');
 //建立車輛資訊表
CREATE TABLE car(
    id VARCHAR(12) PRIMARY KEY,
    mark VARCHAR(24),
    price NUMERIC(6,2),
    pid VARCHAR(12),
    CONSTRAINT fk_people FOREIGN KEY(pid) REFERENCES people(id)
);
INSERT INTO car VALUES('C001','BMW',65.99,'H001');
INSERT INTO car VALUES('C002','BenZ',75.99,'H002');
INSERT INTO car VALUES('C003','Skoda',23.99,'H001');
INSERT INTO car VALUES('C004','Peugeot',20.99,'H003');
INSERT INTO car VALUES('C005','Porsche',295.99,'H004');
INSERT INTO car VALUES('C006','Honda',24.99,'H005');
INSERT INTO car VALUES('C007','Toyota',27.99,'H006');
INSERT INTO car VALUES('C008','Kia',18.99,'H002');
INSERT INTO car VALUES('C009','Bentley',309.99,'H005');
例子1:學生和班級之間的關係

班級表
id   class_name 
1    python脫產100期
2    python脫產300期

學生表          foreign key               
id     name    class_id
1       alex     2
2       劉強東    2
3       馬雲      1

例子2: 一個女孩 擁有多個男朋友...

例子3:....

2.一對一關係

例如:一箇中國公民只能有一個身份證資訊

分析: 一對一的表關係實際上是 變異了的 一對多關係. 通過在從表的外來鍵欄位上新增唯一約束(unique)來實現一對一表關係.

#身份證資訊表
CREATE TABLE card (
  id int NOT NULL AUTO_INCREMENT PRIMARY KEY,
  code varchar(18) DEFAULT NULL,
  UNIQUE un_code (CODE) -- 建立唯一索引的目的,保證身份證號碼同樣不能出現重複
);

INSERT INTO card VALUES(null,'210123123890890678'),
                       (null,'210123456789012345'),
                       (null,'210098765432112312');

#公民表
CREATE TABLE people (
  id int NOT NULL AUTO_INCREMENT PRIMARY KEY,
  name varchar(50) DEFAULT NULL,
  sex char(1) DEFAULT '0',
  c_id int UNIQUE, -- 外來鍵新增唯一約束,確保一對一
  CONSTRAINT fk_card_id FOREIGN KEY (c_id) REFERENCES card(id)
);

INSERT INTO people VALUES(null,'zhangsan','1',1),
                         (null,'lisi','0',2),
                         (null,'wangwu','1',3);
例子一:一個使用者只有一個部落格
    使用者表:
    主鍵
    id   name
    1    egon
    2    alex
    3    wupeiqi


    部落格表   
           fk+unique
    id url user_id
    1  xxxx   1
    2  yyyy   3
    3  zzz    2

例子2: 一個男人的戶口本上,一輩子最多隻能一個女主的名字.等等

3.多對多關係

 例如:學生選課,一個學生可以選修多門課程,每門課程可供多個學生選擇。
 分析:這種方式可以按照類似一對多方式建表,但冗餘資訊太多,好的方式是實體和關係分離並單獨建表,實體表為學生表和課程表,關係表為選修表,
其中關係表採用聯合主鍵的方式(由學生表主鍵和課程表主鍵組成)建表。

#//建立學生表
CREATE TABLE student(
    id VARCHAR(10) PRIMARY KEY,
    sname VARCHAR(12),
    age INT,
    sex CHAR(1)
);
INSERT INTO student VALUES('S0001','王軍',20,1);
INSERT INTO student VALUES('S0002','張宇',21,1);
INSERT INTO student VALUES('S0003','劉飛',22,1);
INSERT INTO student VALUES('S0004','趙燕',18,0);
INSERT INTO student VALUES('S0005','曾婷',19,0);
INSERT INTO student VALUES('S0006','周慧',21,0);
INSERT INTO student VALUES('S0007','小紅',23,0);
INSERT INTO student VALUES('S0008','楊曉',18,0);
INSERT INTO student VALUES('S0009','李傑',20,1);
INSERT INTO student VALUES('S0010','張良',22,1);

# //建立課程表
CREATE TABLE course(
    id VARCHAR(10) PRIMARY KEY,
    sname VARCHAR(12),
    credit DOUBLE(2,1),
    teacher VARCHAR(12)
);
INSERT INTO course VALUES('C001','Java',3.5,'李老師');
INSERT INTO course VALUES('C002','高等數學',5.0,'趙老師');
INSERT INTO course VALUES('C003','JavaScript',3.5,'王老師');
INSERT INTO course VALUES('C004','離散數學',3.5,'卜老師');
INSERT INTO course VALUES('C005','資料庫',3.5,'廖老師');
INSERT INTO course VALUES('C006','作業系統',3.5,'張老師');

# //建立選修表
CREATE TABLE sc(
    sid VARCHAR(10),
    cid VARCHAR(10),
      PRIMARY KEY(sid,cid),
      CONSTRAINT fk_student FOREIGN KEY(sid) REFERENCES student(id),
      CONSTRAINT fk_course FOREIGN KEY(cid) REFERENCES course(id)
);

INSERT INTO sc VALUES('S0001','C001');
INSERT INTO sc VALUES('S0001','C002');
INSERT INTO sc VALUES('S0001','C003');
INSERT INTO sc VALUES('S0002','C001');
INSERT INTO sc VALUES('S0002','C004');
INSERT INTO sc VALUES('S0003','C002');
INSERT INTO sc VALUES('S0003','C005');
INSERT INTO sc VALUES('S0004','C003');
INSERT INTO sc VALUES('S0005','C001');
INSERT INTO sc VALUES('S0006','C004');
INSERT INTO sc VALUES('S0007','C002');
INSERT INTO sc VALUES('S0008','C003');
INSERT INTO sc VALUES('S0009','C001');
INSERT INTO sc VALUES('S0009','C005');

例子1:中華相親網: 男嘉賓表+相親關係表+女嘉賓表
男嘉賓:
    1  孟飛
    2  樂嘉
女嘉賓:
    1  小樂
    2  小嘉
                    
相親表:(中間表)
                    
男嘉賓  女嘉賓  相親時間
1          1            2017-10-12 12:12:12
                    
1          2           2017-10-13 12:12:12

1          1           2017-10-15 12:12:12


例子2: 使用者表,選單表,使用者許可權表...

10,資料三正規化

正規化的概念

  為了建立冗餘較小、結構合理的資料庫,設計資料庫時必須遵循一定的規則。在關係型資料庫中這種規則就稱為正規化。正規化是符合某一種設計要求的總結。要想設計一個結構合理的關係型資料庫,必須滿足一定的正規化。

在實際開發中最為常見的設計正規化有三個:

1.第一正規化(確保每列保持原子性)

第一正規化是最基本的正規化。如果資料庫表中的所有欄位值都是不可分解的原子值,就說明該資料庫表滿足了第一正規化。

第一正規化的合理遵循需要根據系統的實際需求來定。比如某些資料庫系統中需要用到“地址”這個屬性,本來直接將“地址”屬性設計成一個數據庫表的欄位就行。但是如果系統經常會訪問“地址”屬性中的“城市”部分,那麼就非要將“地址”這個屬性重新拆分為省份、城市、詳細地址等多個部分進行儲存,這樣在對地址中某一部分操作的時候將非常方便。這樣設計才算滿足了資料庫的第一正規化,如下表所示。

上表所示的使用者資訊遵循了第一正規化的要求,這樣在對使用者使用城市進行分類的時候就非常方便,也提高了資料庫的效能。

2.第二正規化(確保表中的每列都和主鍵相關)

第二正規化在第一正規化的基礎之上更進一層。第二正規化需要確保資料庫表中的每一列都和主鍵相關,而不能只與主鍵的某一部分相關(主要針對聯合主鍵而言)。也就是說在一個數據庫表中,一個表中只能儲存一種資料,不可以把多種資料儲存在同一張資料庫表中。

比如要設計一個訂單資訊表,因為訂單中可能會有多種商品,所以要將訂單編號和商品編號作為資料庫表的聯合主鍵,如下表所示。

訂單資訊表

這樣就產生一個問題:這個表中是以訂單編號和商品編號作為聯合主鍵。這樣在該表中商品名稱、單位、商品價格等資訊不與該表的主鍵相關,而僅僅是與商品編號相關。所以在這裡違反了第二正規化的設計原則。

而如果把這個訂單資訊表進行拆分,把商品資訊分離到另一個表中,把訂單專案表也分離到另一個表中,就非常完美了。如下所示。

這樣設計,在很大程度上減小了資料庫的冗餘。如果要獲取訂單的商品資訊,使用商品編號到商品資訊表中查詢即可。

3.第三正規化(確保每列都和主鍵列直接相關,而不是間接相關)

第三正規化需要確保資料表中的每一列資料都和主鍵直接相關,而不能間接相關。

比如在設計一個訂單資料表的時候,可以將客戶編號作為一個外來鍵和訂單表建立相應的關係。而不可以在訂單表中新增關於客戶其它資訊(比如姓名、所屬公司等)的欄位。如下面這兩個表所示的設計就是一個滿足第三正規化的資料庫表。

這樣在查詢訂單資訊的時候,就可以使用客戶編號來引用客戶資訊表中的記錄,也不必在訂單資訊表中多次輸入客戶資訊的內容,減小了資料冗餘。

注意事項:

1.第二正規化與第三正規化的本質區別:在於有沒有分出兩張表。

第二正規化是說一張表中包含了多種不同實體的屬性,那麼必須要分成多張表,第三正規化是要求已經分好了多張表的話,一張表中只能有另一張標的ID,而不能有其他任何資訊,(其他任何資訊,一律用主鍵在另一張表中查詢)。

2.必須先滿足第一正規化才能滿足第二正規化,必須同時滿足第一第二正規化才能滿足第三正規化。

————————————————————————————————————————————————————————

轉載引用:https://www.cnblogs.com/bypp/p/8618382.html