數據庫 設計 和多表查詢
#1、首先明確一點:分組發生在where之後,即分組是基於where之後得到的記錄而進行的 #2、分組指的是:將所有記錄按照某個相同字段進行歸類,比如針對員工信息表的職位分組,或者按照性別進行分組等 #3、為何要分組呢? 取每個部門的最高工資 取每個部門的員工數 取男人數和女人數 小竅門:‘每’這個字後面的字段,就是我們分組的依據 #4、大前提: 可以按照任意字段分組,但是分組完畢後,比如group by post,只能查看post字段,如果想查看組內信息,需要借助於聚合函數
單獨使用GROUP BY關鍵字分組 SELECT post FROM employee GROUP BY post; 註意:我們按照post字段分組,那麽select查詢的字段只能是post,想要獲取組內的其他相關信息,需要借助函數 GROUP BY關鍵字和GROUP_CONCAT()函數一起使用 SELECT post,GROUP_CONCAT(name) FROM employee GROUP BY post;#按照崗位分組,並查看組內成員名 SELECT post,GROUP_CONCAT(name) as emp_members FROM employee GROUP BY post; GROUP BY與聚合函數一起使用 select post,count(id) as count from employee group by post;#按照崗位分組,並查看每個組有多少人
聚合函數
#強調:聚合函數聚合的是組的內容,若是沒有分組,則默認一組 示例: SELECT COUNT(*) FROM employee; SELECT COUNT(*) FROM employee WHERE depart_id=1; SELECT MAX(salary) FROM employee; SELECT MIN(salary) FROM employee; SELECT AVG(salary) FROM employee; SELECT SUM(salary) FROM employee; SELECT SUM(salary) FROM employee WHERE depart_id=3;
HAVING過濾
having用於分組之後的條件查詢
限制查詢的記錄數:LIMIT
示例: SELECT * FROM employee ORDER BY salary DESC LIMIT 3; #默認初始位置為0 SELECT * FROM employee ORDER BY salary DESC LIMIT 0,5; #從第0開始,即先查詢出第一條,然後包含這一條在內往後查5條 SELECT * FROM employee ORDER BY salary DESC LIMIT 5,5; #從第5開始,即先查詢出第6條,然後包含這一條在內往後查5條
使用正則表達式查詢
SELECT * FROM employee WHERE name REGEXP ‘^ale‘; SELECT * FROM employee WHERE name REGEXP ‘on$‘; SELECT * FROM employee WHERE name REGEXP ‘m{2}‘; 小結:對字符串匹配的方式 WHERE name = ‘egon‘; WHERE name LIKE ‘yua%‘; WHERE name REGEXP ‘on$‘
多表查詢
多表連接查詢
#重點:外鏈接語法 SELECT 字段列表 FROM 表1 INNER|LEFT|RIGHT JOIN 表2 ON 表1.字段 = 表2.字段;
交叉連接:不適用任何匹配條件。生成笛卡爾積
mysql> select * from employee,department;
+----+------------+--------+------+--------+------+--------------+
| id | name | sex | age | dep_id | id | name |
+----+------------+--------+------+--------+------+--------------+
| 1 | egon | male | 18 | 200 | 200 | 技術 |
| 1 | egon | male | 18 | 200 | 201 | 人力資源 |
| 1 | egon | male | 18 | 200 | 202 | 銷售 |
| 1 | egon | male | 18 | 200 | 203 | 運營 |
| 2 | alex | female | 48 | 201 | 200 | 技術 |
| 2 | alex | female | 48 | 201 | 201 | 人力資源 |
| 2 | alex | female | 48 | 201 | 202 | 銷售 |
| 2 | alex | female | 48 | 201 | 203 | 運營 |
| 3 | wupeiqi | male | 38 | 201 | 200 | 技術 |
| 3 | wupeiqi | male | 38 | 201 | 201 | 人力資源 |
| 3 | wupeiqi | male | 38 | 201 | 202 | 銷售 |
| 3 | wupeiqi | male | 38 | 201 | 203 | 運營 |
| 4 | yuanhao | female | 28 | 202 | 200 | 技術 |
| 4 | yuanhao | female | 28 | 202 | 201 | 人力資源 |
| 4 | yuanhao | female | 28 | 202 | 202 | 銷售 |
| 4 | yuanhao | female | 28 | 202 | 203 | 運營 |
| 5 | liwenzhou | male | 18 | 200 | 200 | 技術 |
| 5 | liwenzhou | male | 18 | 200 | 201 | 人力資源 |
| 5 | liwenzhou | male | 18 | 200 | 202 | 銷售 |
| 5 | liwenzhou | male | 18 | 200 | 203 | 運營 |
| 6 | jingliyang | female | 18 | 204 | 200 | 技術 |
| 6 | jingliyang | female | 18 | 204 | 201 | 人力資源 |
| 6 | jingliyang | female | 18 | 204 | 202 | 銷售 |
| 6 | jingliyang | female | 18 | 204 | 203 | 運營 |
+----+------------+--------+------+--------+------+--------------+
內連接:只連接匹配的行
#找兩張表共有的部分,相當於利用條件從笛卡爾積結果中篩選出了正確的結果 #department沒有204這個部門,因而employee表中關於204這條員工信息沒有匹配出來 mysql> select employee.id,employee.name,employee.age,employee.sex,department.name from employee inner join department on employee.dep_id=department.id; +----+-----------+------+--------+--------------+ | id | name | age | sex | name | +----+-----------+------+--------+--------------+ | 1 | egon | 18 | male | 技術 | | 2 | alex | 48 | female | 人力資源 | | 3 | wupeiqi | 38 | male | 人力資源 | | 4 | yuanhao | 28 | female | 銷售 | | 5 | liwenzhou | 18 | male | 技術 | +----+-----------+------+--------+--------------+ #上述sql等同於 mysql> select employee.id,employee.name,employee.age,employee.sex,department.name from employee,department where employee.dep_id=department.id;
外鏈接之左連接:優先顯示左表全部記錄
#以左表為準,即找出所有員工信息,當然包括沒有部門的員工 #本質就是:在內連接的基礎上增加左邊有右邊沒有的結果 mysql> select employee.id,employee.name,department.name as depart_name from employee left join department on employee.dep_id=department.id; +----+------------+--------------+ | id | name | depart_name | +----+------------+--------------+ | 1 | egon | 技術 | | 5 | liwenzhou | 技術 | | 2 | alex | 人力資源 | | 3 | wupeiqi | 人力資源 | | 4 | yuanhao | 銷售 | | 6 | jingliyang | NULL | +----+------------+--------------+
外鏈接之右連接:優先顯示右表全部記錄
#以右表為準,即找出所有部門信息,包括沒有員工的部門 #本質就是:在內連接的基礎上增加右邊有左邊沒有的結果 mysql> select employee.id,employee.name,department.name as depart_name from employee right join department on employee.dep_id=department.id; +------+-----------+--------------+ | id | name | depart_name | +------+-----------+--------------+ | 1 | egon | 技術 | | 2 | alex | 人力資源 | | 3 | wupeiqi | 人力資源 | | 4 | yuanhao | 銷售 | | 5 | liwenzhou | 技術 | | NULL | NULL | 運營 | +------+-----------+--------------+
全外連接:顯示左右兩個表全部記錄
全外連接:在內連接的基礎上增加左邊有右邊沒有的和右邊有左邊沒有的結果 #註意:mysql不支持全外連接 full JOIN #強調:mysql可以使用此種方式間接實現全外連接 select * from employee left join department on employee.dep_id = department.id union select * from employee right join department on employee.dep_id = department.id ; #查看結果 +------+------------+--------+------+--------+------+--------------+ | id | name | sex | age | dep_id | id | name | +------+------------+--------+------+--------+------+--------------+ | 1 | egon | male | 18 | 200 | 200 | 技術 | | 5 | liwenzhou | male | 18 | 200 | 200 | 技術 | | 2 | alex | female | 48 | 201 | 201 | 人力資源 | | 3 | wupeiqi | male | 38 | 201 | 201 | 人力資源 | | 4 | yuanhao | female | 28 | 202 | 202 | 銷售 | | 6 | jingliyang | female | 18 | 204 | NULL | NULL | | NULL | NULL | NULL | NULL | NULL | 203 | 運營 | +------+------------+--------+------+--------+------+--------------+ #註意 union與union all的區別:union會去掉相同的紀錄
符合條件連接查詢
#示例1:以內連接的方式查詢employee和department表,並且employee表中的age字段值必須大於25,即找出年齡大於25歲的員工以及員工所在的部門 select employee.name,department.name from employee inner join department on employee.dep_id = department.id where age > 25; #示例2:以內連接的方式查詢employee和department表,並且以age字段的升序方式顯示 select employee.id,employee.name,employee.age,department.name from employee,department where employee.dep_id = department.id and age > 25 order by age asc;
子查詢
#1:子查詢是將一個查詢語句嵌套在另一個查詢語句中。 #2:內層查詢語句的查詢結果,可以為外層查詢語句提供查詢條件。 #3:子查詢中可以包含:IN、NOT IN、ANY、ALL、EXISTS 和 NOT EXISTS等關鍵字 #4:還可以包含比較運算符:= 、 !=、> 、<等
帶IN關鍵字的子查詢
#查詢平均年齡在25歲以上的部門名 select id,name from department where id in (select dep_id from employee group by dep_id having avg(age) > 25); #查看技術部員工姓名 select name from employee where dep_id in (select id from department where name=‘技術‘); #查看不足1人的部門名 select name from department where id in (select dep_id from employee group by dep_id having count(id) <=1);
帶比較運算符的子查詢
#比較運算符:=、!=、>、>=、<、<=、<> #查詢大於所有人平均年齡的員工名與年齡 mysql> select name,age from emp where age > (select avg(age) from emp); +---------+------+ | name | age | +---------+------+ | alex | 48 | | wupeiqi | 38 | +---------+------+ 2 rows in set (0.00 sec) #查詢大於部門內平均年齡的員工名、年齡 select t1.name,t1.age from emp t1 inner join (select dep_id,avg(age) avg_age from emp group by dep_id) t2 on t1.dep_id = t2.dep_id where t1.age > t2.avg_age;
帶EXISTS關鍵字的子查詢
EXISTS關字鍵字表示存在。在使用EXISTS關鍵字時,內層查詢語句不返回查詢的記錄。
而是返回一個真假值。True或False
當返回True時,外層查詢語句將進行查詢;當返回值為False時,外層查詢語句不進行查詢
#department表中存在dept_id=203,Ture mysql> select * from employee -> where exists -> (select id from department where id=200); +----+------------+--------+------+--------+ | id | name | sex | age | dep_id | +----+------------+--------+------+--------+ | 1 | egon | male | 18 | 200 | | 2 | alex | female | 48 | 201 | | 3 | wupeiqi | male | 38 | 201 | | 4 | yuanhao | female | 28 | 202 | | 5 | liwenzhou | male | 18 | 200 | | 6 | jingliyang | female | 18 | 204 | +----+------------+--------+------+--------+ #department表中存在dept_id=205,False mysql> select * from employee -> where exists -> (select id from department where id=204); Empty set (0.00 sec)
約束條件
約束條件與數據類型的寬度一樣,都是可選參數
作用:用於保證數據的完整性和一致性
主要分為:
PRIMARY KEY (PK) 標識該字段為該表的主鍵,可以唯一的標識記錄 FOREIGN KEY (FK) 標識該字段為該表的外鍵 NOT NULL 標識該字段不能為空 UNIQUE KEY (UK) 標識該字段的值是唯一的 AUTO_INCREMENT 標識該字段的值自動增長(整數類型,而且為主鍵) DEFAULT 為該字段設置默認值 UNSIGNED 無符號 ZEROFILL 使用0填充
not null與default
是否可空,null表示空,非字符串
not null - 不可空
null - 可空
默認值,創建列時可以指定默認值,當插入數據時如果未主動設置,則自動添加默認值
create table tb1(
nid int not null defalut 2,
num int not null
)
==================not null====================
mysql> create table t1(id int); #id字段默認可以插入空
mysql> desc t1;
+-------+---------+------+-----+---------+-------+
| Field | Type | Null | Key | Default | Extra |
+-------+---------+------+-----+---------+-------+
| id | int(11) | YES | | NULL | |
+-------+---------+------+-----+---------+-------+
mysql> insert into t1 values(); #可以插入空
mysql> create table t2(id int not null); #設置字段id不為空
mysql> desc t2;
+-------+---------+------+-----+---------+-------+
| Field | Type | Null | Key | Default | Extra |
+-------+---------+------+-----+---------+-------+
| id | int(11) | NO | | NULL | |
+-------+---------+------+-----+---------+-------+
mysql> insert into t2 values(); #不能插入空
ERROR 1364 (HY000): Field ‘id‘ doesn‘t have a default value
==================default====================
#設置id字段有默認值後,則無論id字段是null還是not null,都可以插入空,插入空默認填入default指定的默認值
mysql> create table t3(id int default 1);
mysql> alter table t3 modify id int not null default 1;
unique
============設置唯一約束 UNIQUE=============== 方法一: create table department1( id int, name varchar(20) unique, comment varchar(100) ); 方法二: create table department2( id int, name varchar(20), comment varchar(100), constraint uk_name unique(name) ); mysql> insert into department1 values(1,‘IT‘,‘技術‘); Query OK, 1 row affected (0.00 sec) mysql> insert into department1 values(1,‘IT‘,‘技術‘); ERROR 1062 (23000): Duplicate entry ‘IT‘ for key ‘name‘
create table service(
id int primary key auto_increment,
name varchar(20),
host varchar(15) not null,
port int not null,
unique(host,port) #聯合唯一
);
mysql> insert into service values
-> (1,‘nginx‘,‘192.168.0.10‘,80),
-> (2,‘haproxy‘,‘192.168.0.20‘,80),
-> (3,‘mysql‘,‘192.168.0.30‘,3306)
-> ;
Query OK, 3 rows affected (0.01 sec)
Records: 3 Duplicates: 0 Warnings: 0
mysql> insert into service(name,host,port) values(‘nginx‘,‘192.168.0.10‘,80);
ERROR 1062 (23000): Duplicate entry ‘192.168.0.10-80‘ for key ‘host‘
當一個字段被設置為not null和unique時,默認為主鍵
primary key
primary key字段的值不為空且唯一
一個表中可以:
單列做主鍵
多列做主鍵(復合主鍵)
但一個表內只能有一個主鍵primary key
============單列做主鍵===============
#方法一:not null+unique
create table department1(
id int not null unique, #主鍵
name varchar(20) not null unique,
comment varchar(100)
);
mysql> desc department1;
+---------+--------------+------+-----+---------+-------+
| Field | Type | Null | Key | Default | Extra |
+---------+--------------+------+-----+---------+-------+
| id | int(11) | NO | PRI | NULL | |
| name | varchar(20) | NO | UNI | NULL | |
| comment | varchar(100) | YES | | NULL | |
+---------+--------------+------+-----+---------+-------+
rows in set (0.01 sec)
#方法二:在某一個字段後用primary key
create table department2(
id int primary key, #主鍵
name varchar(20),
comment varchar(100)
);
mysql> desc department2;
+---------+--------------+------+-----+---------+-------+
| Field | Type | Null | Key | Default | Extra |
+---------+--------------+------+-----+---------+-------+
| id | int(11) | NO | PRI | NULL | |
| name | varchar(20) | YES | | NULL | |
| comment | varchar(100) | YES | | NULL | |
+---------+--------------+------+-----+---------+-------+
rows in set (0.00 sec)
#方法三:在所有字段後單獨定義primary key
create table department3(
id int,
name varchar(20),
comment varchar(100),
constraint pk_name primary key(id); #創建主鍵並為其命名pk_name
mysql> desc department3;
+---------+--------------+------+-----+---------+-------+
| Field | Type | Null | Key | Default | Extra |
+---------+--------------+------+-----+---------+-------+
| id | int(11) | NO | PRI | NULL | |
| name | varchar(20) | YES | | NULL | |
| comment | varchar(100) | YES | | NULL | |
+---------+--------------+------+-----+---------+-------+
rows in set (0.01 sec)
==================多列做主鍵================
create table service(
ip varchar(15),
port char(5),
service_name varchar(10) not null,
primary key(ip,port)
);
mysql> desc service;
+--------------+-------------+------+-----+---------+-------+
| Field | Type | Null | Key | Default | Extra |
+--------------+-------------+------+-----+---------+-------+
| ip | varchar(15) | NO | PRI | NULL | |
| port | char(5) | NO | PRI | NULL | |
| service_name | varchar(10) | NO | | NULL | |
+--------------+-------------+------+-----+---------+-------+
3 rows in set (0.00 sec)
mysql> insert into service values
-> (‘172.16.45.10‘,‘3306‘,‘mysqld‘),
-> (‘172.16.45.11‘,‘3306‘,‘mariadb‘)
-> ;
Query OK, 2 rows affected (0.00 sec)
Records: 2 Duplicates: 0 Warnings: 0
mysql> insert into service values (‘172.16.45.10‘,‘3306‘,‘nginx‘);
ERROR 1062 (23000): Duplicate entry ‘172.16.45.10-3306‘ for key ‘PRIMARY‘
//alter刪除主鍵約束 alter table t3 drop primary key; //alter添加主鍵約束 alter table t3 add primary key(name, pwd); //alter 修改列為主鍵 alter table t3 modify id int primary key;
auto_increment
約束字段為自動增長,被約束的字段必須同時被key約束
#不指定id,則自動增長
create table student(
id int primary key auto_increment,
name varchar(20),
sex enum(‘male‘,‘female‘) default ‘male‘
);
mysql> desc student;
+-------+-----------------------+------+-----+---------+----------------+
| Field | Type | Null | Key | Default | Extra |
+-------+-----------------------+------+-----+---------+----------------+
| id | int(11) | NO | PRI | NULL | auto_increment |
| name | varchar(20) | YES | | NULL | |
| sex | enum(‘male‘,‘female‘) | YES | | male | |
+-------+-----------------------+------+-----+---------+----------------+
mysql> insert into student(name) values
-> (‘egon‘),
-> (‘alex‘)
-> ;
mysql> select * from student;
+----+------+------+
| id | name | sex |
+----+------+------+
| 1 | egon | male |
| 2 | alex | male |
+----+------+------+
#也可以指定id
mysql> insert into student values(4,‘asb‘,‘female‘);
Query OK, 1 row affected (0.00 sec)
mysql> insert into student values(7,‘wsb‘,‘female‘);
Query OK, 1 row affected (0.00 sec)
mysql> select * from student;
+----+------+--------+
| id | name | sex |
+----+------+--------+
| 1 | egon | male |
| 2 | alex | male |
| 4 | asb | female |
| 7 | wsb | female |
+----+------+--------+
#對於自增的字段,在用delete刪除後,再插入值,該字段仍按照刪除前的位置繼續增長
mysql> delete from student;
Query OK, 4 rows affected (0.00 sec)
mysql> select * from student;
Empty set (0.00 sec)
mysql> insert into student(name) values(‘ysb‘);
mysql> select * from student;
+----+------+------+
| id | name | sex |
+----+------+------+
| 8 | ysb | male |
+----+------+------+
#應該用truncate清空表,比起delete一條一條地刪除記錄,truncate是直接清空表,在刪除大表時用它
mysql> truncate student;
Query OK, 0 rows affected (0.01 sec)
mysql> insert into student(name) values(‘egon‘);
Query OK, 1 row affected (0.01 sec)
mysql> select * from student;
+----+------+------+
| id | name | sex |
+----+------+------+
| 1 | egon | male |
+----+------+------+
1 row in set (0.00 sec)
步長increment與起始偏移量offset:auto_increment_increment,auto_increment_offset
foreign key
員工信息表有三個字段:工號 姓名 部門
公司有3個部門,但是有1個億的員工,那意味著部門這個字段需要重復存儲,部門名字越長,越浪費
解決方法:
我們完全可以定義一個部門表
然後讓員工信息表關聯該表,如何關聯,即foreign key
#表類型必須是innodb存儲引擎,且被關聯的字段,即references指定的另外一個表的字段,必須保證唯一
create table department(
id int primary key,
name varchar(20) not null
)engine=innodb;
#dpt_id外鍵,關聯父表(department主鍵id),同步更新,同步刪除
create table employee(
id int primary key,
name varchar(20) not null,
dpt_id int,
constraint fk_name foreign key(dpt_id)
references department(id)
on delete cascade
on update cascade
)engine=innodb;
#先往父表department中插入記錄
insert into department values
(1,‘歐德博愛技術有限事業部‘),
(2,‘艾利克斯人力資源部‘),
(3,‘銷售部‘);
#再往子表employee中插入記錄
insert into employee values
(1,‘egon‘,1),
(2,‘alex1‘,2),
(3,‘alex2‘,2),
(4,‘alex3‘,2),
(5,‘李坦克‘,3),
(6,‘劉飛機‘,3),
(7,‘張火箭‘,3),
(8,‘林子彈‘,3),
(9,‘加特林‘,3)
;
#刪父表department,子表employee中對應的記錄跟著刪
mysql> delete from department where id=3;
mysql> select * from employee;
+----+-------+--------+
| id | name | dpt_id |
+----+-------+--------+
| 1 | egon | 1 |
| 2 | alex1 | 2 |
| 3 | alex2 | 2 |
| 4 | alex3 | 2 |
+----+-------+--------+
#更新父表department,子表employee中對應的記錄跟著改
mysql> update department set id=22222 where id=2;
mysql> select * from employee;
+----+-------+--------+
| id | name | dpt_id |
+----+-------+--------+
| 1 | egon | 1 |
| 3 | alex2 | 22222 |
| 4 | alex3 | 22222 |
| 5 | alex1 | 22222 |
+----+-------+--------+
兩張表之間的關系
分析步驟: #1、先站在左表的角度去找 是否左表的多條記錄可以對應右表的一條記錄,如果是,則證明左表的一個字段foreign key 右表一個字段(通常是id) #2、再站在右表的角度去找 是否右表的多條記錄可以對應左表的一條記錄,如果是,則證明右表的一個字段foreign key 左表一個字段(通常是id) #3、總結: #多對一: 如果只有步驟1成立,則是左表多對一右表 如果只有步驟2成立,則是右表多對一左表 #多對多 如果步驟1和2同時成立,則證明這兩張表時一個雙向的多對一,即多對多,需要定義一個這兩張表的關系表來專門存放二者的關系 #一對一: 如果1和2都不成立,而是左表的一條記錄唯一對應右表的一條記錄,反之亦然。這種情況很簡單,就是在左表foreign key右表的基礎上,將左表的外鍵字段設置成unique即可
數據庫設計
1.概念
1.有效存儲數據
2.滿足用戶的多種需求
2.關系
1-1 :最少需要1張表
1-n :最少需要2張表
n-n :最少需要3張表
3.數據庫三範式
1.保證每列的原子性
2.保證每列都與主鍵相關
3.保證每列都和主鍵直接相關,而不能是間接相關
三範式的詳解:http://www.cnblogs.com/wangfengming/p/7929118.html
索引
1.概念:相當於書的目錄,快速找到數據
好處:可以幫助你提高查詢效率,數據量越大越明顯
缺點: 新增和刪除數據時,效率較低
2.索引方法:
1.hash 是以key-value 的形式進行索引存儲
2.BTree 是以二叉樹方式進行索引存儲。(默認存儲索引類型)
3.索引分類
1. 普通索引 create INDEX name_index on person(name);
2. 唯一索引 create unique INDEX name_age on person(name,age);
3. 主鍵索引 alter table person MODIFY id int PRIMARY key;
4. 組合索引 create unique INDEX name_age on person(name,age);
5. 全文索引 full text :原理是分詞查找
創建 普通索引
CREATE index aaa on ren(p_name) 添加普通索引 註意:index :表示索引 aaa:表示索引的別名, on:表示給哪個表添加索引 ren:表名稱,(添加索引的字段,多個字段以","間隔) alter table ren add index aaa(p_name)
創建 唯一索引
CREATE UNIQUE index age on ren(p_age) 添加唯一索引 註意:unique index :表示唯一索引 aaa:表示索引的別名, on:表示給哪個表添加索引 ren:表名稱,(添加索引的字段,多個字段以","間隔)
創建 主鍵索引
alter table 表名 add primary key(id); 添加之間索引 註意:主鍵索引只能有一個
創建 組合索引
create index id_name on ren (id,name) 添加組合索引 註意: 如上創建組合索引之後,查詢: id and name-- 使用索引 id -- 使用索引 name -- 不使用索引
數據庫 設計 和多表查詢