sql會創(chuàng)建多表以及多表的關(guān)系
需求:
上篇文章中的商品表和分類表之間存在著所屬關(guān)系掏熬,在數(shù)據(jù)庫如何表示這種關(guān)系
分析:
多表之間的關(guān)系如何來維護
- 外鍵約束:假設(shè)現(xiàn)在在product表中插入一條cno為99的數(shù)據(jù)王悍,但是cno為99的列在category中并不存在棋弥。這時候就需要外鍵約束贷岸,
foreign key
- 給product這個表中的cno添加一個外鍵約束:
alter table product add foreign key(cno) references category(cid);
添加外鍵約束之后融击,再向product中插入category中沒有的種類會失敗役听。
- 給product這個表中的cno添加一個外鍵約束:
添加外鍵約束之后,如果想刪除category中的某一列踩蔚,也會報錯棚放,因為product中有記錄在引用枚粘。首先得去product表中刪除引用該種類的所有記錄馅闽,才能去刪除category中的相應(yīng)列。
在下面的例子中馍迄,有如下2張表:
mysql> select * from product;
+-----+--------------+-------+---------------------+------+
| pid | pname | price | pdate | cno |
+-----+--------------+-------+---------------------+------+
| 4 | 小米mix2s | 2700 | 2019-04-14 13:14:31 | 1 |
| 5 | 華為p30 | 4788 | 2019-04-14 13:14:32 | 1 |
| 6 | 阿迪王 | 99 | 2019-04-14 13:14:32 | 2 |
| 7 | 老村長 | 88 | 2019-04-14 13:14:32 | 3 |
| 8 | 勁酒 | 35 | 2019-04-14 13:14:32 | 3 |
| 9 | 小熊餅干 | 3 | 2019-04-14 13:14:32 | 4 |
| 10 | 衛(wèi)龍辣條 | 1 | 2019-04-14 13:14:32 | 5 |
| 11 | 旺旺雪餅 | 1 | 2019-04-14 13:14:32 | 5 |
+-----+--------------+-------+---------------------+------+
8 rows in set (0.00 sec)
mysql> select * from category;
+-----+--------------+-----------------------+
| cid | cname | cdesc |
+-----+--------------+-----------------------+
| 1 | 手機數(shù)碼 | 電子產(chǎn)品 |
| 2 | 鞋靴箱包 | 江南皮鞋廠打造 |
| 3 | 香煙酒水 | 芙蓉王福也,茅臺 |
| 4 | 酸奶餅干 | 哇哈哈 |
| 5 | 饞嘴零食 | 瓜子花生 |
+-----+--------------+-----------------------+
5 rows in set (0.00 sec)
其中2張表存在CONSTRAINT product_ibfk_1
FOREIGN KEY (cno
) REFERENCES category
(cid
)外鍵約束
建立數(shù)據(jù)庫的原則
通常情況下,一個應(yīng)用一個數(shù)據(jù)庫攀圈。
多表之間的建表原則
一對多
例子中提到的product和category就是一對多的關(guān)系暴凑,一個種類對應(yīng)多個商品
- 建表原則: 在多的一方(product)添加一個外鍵(cno)指向一的一方(category)的主鍵(cid)
多對多
例如:老師和學(xué)生,學(xué)生和課程等
mysql> select * from student;
+----+--------+--------+-----+
| id | name | gender | age |
+----+--------+--------+-----+
| 1 | 張三 | 男 | 18 |
| 2 | 李四 | 男 | 20 |
| 3 | 王五 | 女 | 19 |
+----+--------+--------+-----+
3 rows in set (0.00 sec)
mysql> select * from subject;
+----+--------+-----------+
| id | name | teacher |
+----+--------+-----------+
| 1 | 英語 | 張老師 |
| 2 | 語文 | 李老師 |
| 3 | 數(shù)學(xué) | 楊老師 |
+----+--------+-----------+
3 rows in set (0.00 sec)
假設(shè)現(xiàn)在學(xué)生開始選課赘来,選課結(jié)果如下:
張三: 英語现喳,語文
李四: 數(shù)學(xué)
王五: 英語,語文
student表中的學(xué)生選了不固定的subject表里的科目犬辰,這種關(guān)系為多對多
這時候我們就需要建立如下表:
CREATE TABLE `stu-submiddletable` (
`stu-subId` int(11) NOT NULL AUTO_INCREMENT COMMENT '中間表主鍵',
`subjectId` int(11) DEFAULT NULL COMMENT '外鍵',
`studentId` int(11) NOT NULL COMMENT '外鍵',
PRIMARY KEY (`stu-subId`),
KEY `studentId` (`studentId`),
KEY `subjectId` (`subjectId`),
CONSTRAINT `studentId` FOREIGN KEY (`studentId`) REFERENCES `student` (`id`) ON DELETE CASCADE ON UPDATE CASCADE,
CONSTRAINT `subjectId` FOREIGN KEY (`subjectId`) REFERENCES `subject` (`id`) ON DELETE CASCADE ON UPDATE CASCADE
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
- 建表原則: 需要多建一張中間表嗦篱,將多對多的關(guān)系拆為一對多的關(guān)系,中間表至少要有2個外鍵幌缝,這2個外鍵分別指向原來的那張表灸促。
在上述中間表中,至少需要2個外鍵涵卵,即學(xué)生的id和科目的id浴栽,其中對于中間表的學(xué)生id來說,student表中和中間表的學(xué)生id為一對多(即category和product的關(guān)系轿偎,student為一典鸡,中間表學(xué)生id為多),而對于中間表中課程編號坏晦,跟subject萝玷,也是一樣的(subject的id對中間表的科目編號也為一對多)伊者。
在這張中間表中,對于上述選擇結(jié)果间护,就可以這么存儲:
一對一
例如公民和身份證的關(guān)系:
People表: id name income
IDcard表:sid 頭像 性別
- 可以直接合并2張表
- 可以在一張表中新增列亦渗,作為外鍵,當初一對多的情況來處理汁尺,這個外鍵指向另外一張表法精。
- 將2張表的主鍵建立起連接,讓2張表主鍵相等痴突。
實際用途不多搂蜓,比如說拆表操作(將表中的一些列拆分出來)
關(guān)于主鍵約束和唯一約束的區(qū)別:
主鍵約束:默認不能為空且唯一,并且不能有多個主鍵
唯一約束:默認為空且唯一辽装,可以有多個唯一約束鍵
外鍵都是指向另一張表的主鍵,而唯一的約束不可以作為其他表的外鍵
多表查詢
對于上述的product表和category表來進行下面的舉例:
多表查詢的幾種類型:
- 交叉連接查詢 笛卡爾積
對于上述的product表以及category表帮碰,如果想要一次查詢2張表的內(nèi)容,我們可以輸入:
select * from product,category;
mysql> SELECT * from product,category;
+-----+--------------+-------+---------------------+------+-----+--------------+-----------------------+
| pid | pname | price | pdate | cno | cid | cname | cdesc |
+-----+--------------+-------+---------------------+------+-----+--------------+-----------------------+
| 4 | 小米mix2s | 2700 | 2019-04-14 13:14:31 | 1 | 1 | 手機數(shù)碼 | 電子產(chǎn)品 |
| 4 | 小米mix2s | 2700 | 2019-04-14 13:14:31 | 1 | 2 | 鞋靴箱包 | 江南皮鞋廠打造 |
| 4 | 小米mix2s | 2700 | 2019-04-14 13:14:31 | 1 | 3 | 香煙酒水 | 芙蓉王拾积,茅臺 |
| 4 | 小米mix2s | 2700 | 2019-04-14 13:14:31 | 1 | 4 | 酸奶餅干 | 哇哈哈 |
| 4 | 小米mix2s | 2700 | 2019-04-14 13:14:31 | 1 | 5 | 饞嘴零食 | 瓜子花生 |
| 5 | 華為p30 | 4788 | 2019-04-14 13:14:32 | 1 | 1 | 手機數(shù)碼 | 電子產(chǎn)品 |
| 5 | 華為p30 | 4788 | 2019-04-14 13:14:32 | 1 | 2 | 鞋靴箱包 | 江南皮鞋廠打造 |
| 5 | 華為p30 | 4788 | 2019-04-14 13:14:32 | 1 | 3 | 香煙酒水 | 芙蓉王殉挽,茅臺 |
| 5 | 華為p30 | 4788 | 2019-04-14 13:14:32 | 1 | 4 | 酸奶餅干 | 哇哈哈 |
| 5 | 華為p30 | 4788 | 2019-04-14 13:14:32 | 1 | 5 | 饞嘴零食 | 瓜子花生 |
| 6 | 阿迪王 | 99 | 2019-04-14 13:14:32 | 2 | 1 | 手機數(shù)碼 | 電子產(chǎn)品 |
| 6 | 阿迪王 | 99 | 2019-04-14 13:14:32 | 2 | 2 | 鞋靴箱包 | 江南皮鞋廠打造 |
| 6 | 阿迪王 | 99 | 2019-04-14 13:14:32 | 2 | 3 | 香煙酒水 | 芙蓉王,茅臺 |
| 6 | 阿迪王 | 99 | 2019-04-14 13:14:32 | 2 | 4 | 酸奶餅干 | 哇哈哈 |
| 6 | 阿迪王 | 99 | 2019-04-14 13:14:32 | 2 | 5 | 饞嘴零食 | 瓜子花生 |
| 7 | 老村長 | 88 | 2019-04-14 13:14:32 | 3 | 1 | 手機數(shù)碼 | 電子產(chǎn)品 |
| 7 | 老村長 | 88 | 2019-04-14 13:14:32 | 3 | 2 | 鞋靴箱包 | 江南皮鞋廠打造 |
| 7 | 老村長 | 88 | 2019-04-14 13:14:32 | 3 | 3 | 香煙酒水 | 芙蓉王拓巧,茅臺 |
| 7 | 老村長 | 88 | 2019-04-14 13:14:32 | 3 | 4 | 酸奶餅干 | 哇哈哈 |
| 7 | 老村長 | 88 | 2019-04-14 13:14:32 | 3 | 5 | 饞嘴零食 | 瓜子花生 |
| 8 | 勁酒 | 35 | 2019-04-14 13:14:32 | 3 | 1 | 手機數(shù)碼 | 電子產(chǎn)品 |
| 8 | 勁酒 | 35 | 2019-04-14 13:14:32 | 3 | 2 | 鞋靴箱包 | 江南皮鞋廠打造 |
| 8 | 勁酒 | 35 | 2019-04-14 13:14:32 | 3 | 3 | 香煙酒水 | 芙蓉王斯碌,茅臺 |
| 8 | 勁酒 | 35 | 2019-04-14 13:14:32 | 3 | 4 | 酸奶餅干 | 哇哈哈 |
| 8 | 勁酒 | 35 | 2019-04-14 13:14:32 | 3 | 5 | 饞嘴零食 | 瓜子花生 |
| 9 | 小熊餅干 | 3 | 2019-04-14 13:14:32 | 4 | 1 | 手機數(shù)碼 | 電子產(chǎn)品 |
| 9 | 小熊餅干 | 3 | 2019-04-14 13:14:32 | 4 | 2 | 鞋靴箱包 | 江南皮鞋廠打造 |
| 9 | 小熊餅干 | 3 | 2019-04-14 13:14:32 | 4 | 3 | 香煙酒水 | 芙蓉王,茅臺 |
| 9 | 小熊餅干 | 3 | 2019-04-14 13:14:32 | 4 | 4 | 酸奶餅干 | 哇哈哈 |
| 9 | 小熊餅干 | 3 | 2019-04-14 13:14:32 | 4 | 5 | 饞嘴零食 | 瓜子花生 |
| 10 | 衛(wèi)龍辣條 | 1 | 2019-04-14 13:14:32 | 5 | 1 | 手機數(shù)碼 | 電子產(chǎn)品 |
| 10 | 衛(wèi)龍辣條 | 1 | 2019-04-14 13:14:32 | 5 | 2 | 鞋靴箱包 | 江南皮鞋廠打造 |
| 10 | 衛(wèi)龍辣條 | 1 | 2019-04-14 13:14:32 | 5 | 3 | 香煙酒水 | 芙蓉王肛度,茅臺 |
| 10 | 衛(wèi)龍辣條 | 1 | 2019-04-14 13:14:32 | 5 | 4 | 酸奶餅干 | 哇哈哈 |
| 10 | 衛(wèi)龍辣條 | 1 | 2019-04-14 13:14:32 | 5 | 5 | 饞嘴零食 | 瓜子花生 |
| 11 | 旺旺雪餅 | 1 | 2019-04-14 13:14:32 | 5 | 1 | 手機數(shù)碼 | 電子產(chǎn)品 |
| 11 | 旺旺雪餅 | 1 | 2019-04-14 13:14:32 | 5 | 2 | 鞋靴箱包 | 江南皮鞋廠打造 |
| 11 | 旺旺雪餅 | 1 | 2019-04-14 13:14:32 | 5 | 3 | 香煙酒水 | 芙蓉王傻唾,茅臺 |
| 11 | 旺旺雪餅 | 1 | 2019-04-14 13:14:32 | 5 | 4 | 酸奶餅干 | 哇哈哈 |
| 11 | 旺旺雪餅 | 1 | 2019-04-14 13:14:32 | 5 | 5 | 饞嘴零食 | 瓜子花生 |
+-----+--------------+-------+---------------------+------+-----+--------------+-----------------------+
40 rows in set (0.00 sec)
上述例子中查出來的結(jié)果稱為笛卡爾積即2張表的乘積,沒什么實際意義承耿。其實我們需要的就是cno等于cid的那些數(shù)據(jù)冠骄,所以需要做一下過濾。
select * from product,category where cno=cid;
對于上述查詢加袋,經(jīng)常會發(fā)現(xiàn)cno和cid屬于哪個表不明確凛辣,可以通過起個別名的方式來指明是哪個表:
select * from product as P,category as C where p.cno=c.cid;
mysql> select * from product as P,category as C where p.cno=c.cid;
+-----+--------------+-------+---------------------+------+-----+--------------+-----------------------+
| pid | pname | price | pdate | cno | cid | cname | cdesc |
+-----+--------------+-------+---------------------+------+-----+--------------+-----------------------+
| 4 | 小米mix2s | 2700 | 2019-04-14 13:14:31 | 1 | 1 | 手機數(shù)碼 | 電子產(chǎn)品 |
| 5 | 華為p30 | 4788 | 2019-04-14 13:14:32 | 1 | 1 | 手機數(shù)碼 | 電子產(chǎn)品 |
| 6 | 阿迪王 | 99 | 2019-04-14 13:14:32 | 2 | 2 | 鞋靴箱包 | 江南皮鞋廠打造 |
| 7 | 老村長 | 88 | 2019-04-14 13:14:32 | 3 | 3 | 香煙酒水 | 芙蓉王,茅臺 |
| 8 | 勁酒 | 35 | 2019-04-14 13:14:32 | 3 | 3 | 香煙酒水 | 芙蓉王锁荔,茅臺 |
| 9 | 小熊餅干 | 3 | 2019-04-14 13:14:32 | 4 | 4 | 酸奶餅干 | 哇哈哈 |
| 10 | 衛(wèi)龍辣條 | 1 | 2019-04-14 13:14:32 | 5 | 5 | 饞嘴零食 | 瓜子花生 |
| 11 | 旺旺雪餅 | 1 | 2019-04-14 13:14:32 | 5 | 5 | 饞嘴零食 | 瓜子花生 |
+-----+--------------+-------+---------------------+------+-----+--------------+-----------------------+
8 rows in set (0.00 sec)
- 內(nèi)連接查詢
對于上述例子蟀给,使用where進行條件過濾的為隱式內(nèi)連接,- 隱式內(nèi)連接
select * from product p,category c where p.cno=c.cid;
- 顯式內(nèi)鏈接(使用inner join)
select * from product p inner join category on p.cno=c.cid;
mysql> select * from product as P join category as C on p.cno=c.cid;
+-----+--------------+-------+---------------------+------+-----+--------------+-----------------------+
| pid | pname | price | pdate | cno | cid | cname | cdesc |
+-----+--------------+-------+---------------------+------+-----+--------------+-----------------------+
| 4 | 小米mix2s | 2700 | 2019-04-14 13:14:31 | 1 | 1 | 手機數(shù)碼 | 電子產(chǎn)品 |
| 5 | 華為p30 | 4788 | 2019-04-14 13:14:32 | 1 | 1 | 手機數(shù)碼 | 電子產(chǎn)品 |
| 6 | 阿迪王 | 99 | 2019-04-14 13:14:32 | 2 | 2 | 鞋靴箱包 | 江南皮鞋廠打造 |
| 7 | 老村長 | 88 | 2019-04-14 13:14:32 | 3 | 3 | 香煙酒水 | 芙蓉王阳堕,茅臺 |
| 8 | 勁酒 | 35 | 2019-04-14 13:14:32 | 3 | 3 | 香煙酒水 | 芙蓉王跋理,茅臺 |
| 9 | 小熊餅干 | 3 | 2019-04-14 13:14:32 | 4 | 4 | 酸奶餅干 | 哇哈哈 |
| 10 | 衛(wèi)龍辣條 | 1 | 2019-04-14 13:14:32 | 5 | 5 | 饞嘴零食 | 瓜子花生 |
| 11 | 旺旺雪餅 | 1 | 2019-04-14 13:14:32 | 5 | 5 | 饞嘴零食 | 瓜子花生 |
+-----+--------------+-------+---------------------+------+-----+--------------+-----------------------+
8 rows in set (0.00 sec)
上述2種方式查詢出來的結(jié)果都是一樣的,但是區(qū)別在于恬总,隱式內(nèi)連接是在查詢出的結(jié)果基礎(chǔ)上進行的where條件過濾前普,但是顯式內(nèi)連接是帶著條件去查詢結(jié)果的,執(zhí)行效率是比較高的壹堰。
- 左外連接(使用 left join)
左(外)連接拭卿,左表的記錄將會全部表示出來骡湖,而右表只會顯示符合搜索條件的記錄。右表記錄不足的地方均為NULL峻厚。
對于上述例子响蕴,我們先給product表插入一條沒有對應(yīng)cno的數(shù)據(jù)。
insert into product values(null, "一條沒有cno的記錄", 100, null, null);
然后執(zhí)行左外連接查詢:
select * from product p left join category c on p.cno=c.cid;
mysql> select * from product as P left outer join category as C on p.cno=c.cid;
+-----+--------------------------+-------+---------------------+------+------+--------------+-----------------------+
| pid | pname | price | pdate | cno | cid | cname | cdesc |
+-----+--------------------------+-------+---------------------+------+------+--------------+-----------------------+
| 4 | 小米mix2s | 2700 | 2019-04-14 13:14:31 | 1 | 1 | 手機數(shù)碼 | 電子產(chǎn)品 |
| 5 | 華為p30 | 4788 | 2019-04-14 13:14:32 | 1 | 1 | 手機數(shù)碼 | 電子產(chǎn)品 |
| 6 | 阿迪王 | 99 | 2019-04-14 13:14:32 | 2 | 2 | 鞋靴箱包 | 江南皮鞋廠打造 |
| 7 | 老村長 | 88 | 2019-04-14 13:14:32 | 3 | 3 | 香煙酒水 | 芙蓉王惠桃,茅臺 |
| 8 | 勁酒 | 35 | 2019-04-14 13:14:32 | 3 | 3 | 香煙酒水 | 芙蓉王浦夷,茅臺 |
| 9 | 小熊餅干 | 3 | 2019-04-14 13:14:32 | 4 | 4 | 酸奶餅干 | 哇哈哈 |
| 10 | 衛(wèi)龍辣條 | 1 | 2019-04-14 13:14:32 | 5 | 5 | 饞嘴零食 | 瓜子花生 |
| 11 | 旺旺雪餅 | 1 | 2019-04-14 13:14:32 | 5 | 5 | 饞嘴零食 | 瓜子花生 |
| 13 | 一條沒有cno的記錄 | 100 | NULL | NULL | NULL | NULL | NULL |
+-----+--------------------------+-------+---------------------+------+------+--------------+-----------------------+
9 rows in set (0.00 sec)
可以看到右表的這一條的輸出都為null,而左表的數(shù)據(jù)都輸出了辜王。
- 右外連接(使用right join)
和左外連接相對的劈狐,右外連接查詢會將右表的所有數(shù)據(jù)查詢出來,如果左表沒有對應(yīng)數(shù)據(jù)的話會用null代替
left join和right join分別為left outer join和right outer join的縮寫 inner/outer在語句中可以省略
簡單理解: 內(nèi)連接查出來的是2個表的交集呐馆,左外和右外其實查詢了左表或者右表的全部肥缔,并且如果有相對應(yīng)的左表記錄的話也會顯示。
- 全連接(使用union /union all)
全連接指的是將2個表合并汹来。
語句:
(select colum1,colum2...columN from tableA ) union (select colum1,colum2...columN from tableB );
(select colum1,colum2...columN from tableA ) union all (select colum1,colum2...columN from tableB );
通過union連接的SQL它們分別單獨取出的列數(shù)必須相同续膳;不要求合并的表列名稱相同時,以第一個sql 表列名為準俗慈;使用union 時姑宽,完全相等的行遣耍,將會被合并闺阱,由于合并比較耗時,一般不直接使用 union 進行合并舵变,而是通常采用union all 進行合并酣溃;
被union 連接的sql 子句,單個子句中不用寫order by 纪隙,因為不會有排序的效果赊豌。但可以對最終的結(jié)果集進行排序;
(select id,name from A order by id) union all (select id,name from B order by id); //沒有排序效果
(select id,name from A ) union all (select id,name from B ) order by id; //有排序效果
分頁查詢(使用limit)
假設(shè)規(guī)定pageSize為3
select * from product limit 0,3; // 第一頁
select * from product limit 3,3; // 第二頁
select * from product limit 6,3; // 第一頁
即每次查詢根據(jù)page和pageSize計算一個起始的索引位置即可:
select * from product limit (page-1)*pageSize,pageSize;
子查詢
即sql語句中再嵌套sql語句
- 如果要查詢分類名為“手機數(shù)碼”的商品绵咱,需要動態(tài)查詢出在category中的cid
select * from product where cno=(select cid from category where cname="手機數(shù)碼");
mysql> SELECT * from product as p WHERE p.cno=(SELECT cid from category WHERE cname="手機數(shù)碼");
+-----+-------------+-------+---------------------+------+
| pid | pname | price | pdate | cno |
+-----+-------------+-------+---------------------+------+
| 4 | 小米mix2s | 2700 | 2019-04-14 13:14:31 | 1 |
| 5 | 華為p30 | 4788 | 2019-04-14 13:14:32 | 1 |
+-----+-------------+-------+---------------------+------+
2 rows in set (0.00 sec)
- 如果要查詢出商品名稱(pname)和商品分類名稱(cname)的信息
選用左右鏈接查詢都可達到目的碘饼,在這介紹下子查詢的寫法:
select pname,(select cname from category as c where p.cno=c.cid) from product as p;
mysql> SELECT pname,(SELECT cname from category as C WHERE p.cno=c.cid) from product as P;
+--------------------------+-----------------------------------------------------+
| pname | (SELECT cname from category as C WHERE p.cno=c.cid) |
+--------------------------+-----------------------------------------------------+
| 小米mix2s | 手機數(shù)碼 |
| 華為p30 | 手機數(shù)碼 |
| 阿迪王 | 鞋靴箱包 |
| 老村長 | 香煙酒水 |
| 勁酒 | 香煙酒水 |
| 小熊餅干 | 酸奶餅干 |
| 衛(wèi)龍辣條 | 饞嘴零食 |
| 旺旺雪餅 | 饞嘴零食 |
| 一條沒有cno的記錄 | NULL |
+--------------------------+-----------------------------------------------------+
9 rows in set (0.00 sec)