我們使用SQL查詢不能只使用很簡單、最基礎的SELECT語句查詢。如果想從多個表查詢比較復雜的信息,就會使用高級查詢實現。常見的高級查詢包括多表連接查詢、內連接查詢、外連接查詢與組合查詢等
以一個簡單的學生信息表(學生ID、學生姓名、學生性別)與一個科目表(科目ID、科目)還有學生成績表(學生ID、科目ID、分數)為例子
創建表
create table scores(
id int auto_increment primary key,
stuid int,
subid int,
score decimal(5,2),
);
create table students(
id int auto_increment primary key,
sname varchar(20),
sex char(1)
);
create table subjects(
id int auto_increment primary key,
stitle varchar(20)
);
學生列的數據不是在這里新建的,而應該從學生表引用過來,關系也是一條數據;根據范式要求應該存儲學生的編號,而不是學生的姓名等其它信息
同理,科目表也是關系列,引用科目表中的數據
外鍵
思考:怎么保證關系列數據的有效性呢?任何整數都可以嗎?
答:必須是學生表中id列存在的數據,可以通過外鍵約束進行數據的有效性驗證為stuid添加外鍵約束
alter table scores
add constraint stu_sco foreign key(stuid) references students(id);
此時插入或者修改數據時,如果stuid的值在students表中不存在則會報錯
在創建表時可以直接創建約束
外鍵的級聯操作
- 在刪除students表的數據時,如果這個id值在scores中已經存在,則會拋異常
- 推薦使用邏輯刪除,還可以解決這個問題
- 可以創建表時指定級聯操作,也可以在創建表后再修改外鍵的級聯操作
alter table scores
add constraint stu_sco foreign key(stuid) references students(id)
on delete cascade;
在創建表時可以直接創建約束,添加級聯操作
create table scores(
id int auto_increment primary key,
stuid int,
subid int,
score decimal(5,2),
#這里我在創建表時直接創建約束,添加級聯操作。后面就不用在添加了
foreign key(stuid) references students(id) on delete cascade,
foreign key(subid) references subjects(id) on delete cascade
);
級聯操作的類型包括:
restrict(限制):默認值,拋異常
cascade(級聯):如果主表的記錄刪掉,則從表中相關聯的記錄都將被刪除
set null:將外鍵設置為空
no action:什么都不做
連接查詢
-- 建表和數據S
create table students(
id int auto_increment primary key,
sname varchar(20)
);
create table subjects(
id int auto_increment primary key,
stitle varchar(20)
);
create table scores(
id int auto_increment primary key,
stuid int,
subid int,
score decimal(5,2),
foreign key(stuid) references students(id) on delete cascade,
foreign key(subid) references subjects(id) on delete cascade
);
insert into subjects(stitle) values('語文');
insert into subjects(stitle) values('數學');
insert into subjects(stitle) values('英語');
insert into students(sname,sex) values('小明','男');
insert into students(sname,sex) values('小美','女');
insert into students(sname,sex) values('小壯','男');
insert into students(sname,sex) values('小敏','女');
insert into scores(stuid,subid,score) values(1,1,88);
insert into scores(stuid,subid,score) values(1,2,95);
insert into scores(stuid,subid,score) values(2,1,89);
insert into scores(stuid,subid,score) values(2,3,95);
insert into scores(stuid,subid,score) values(3,1,92);
insert into scores(stuid,subid,score) values(3,2,85);
insert into scores(stuid,subid,score) values(4,2,82);
insert into scores(stuid,subid,score) values(4,3,99);
/*查詢學生的編號,姓名,科目的名稱,成績*/
select t1.id,t1.sname,t2.stitle,t3.score
from students t1,subjects t2,scores t3
where t3.stuid=t1.id and t3.subid=t2.id;
select t1.id,t1.sname,t2.stitle,t3.score
from scores t3
inner join students t1
on t3.stuid = t1.id
inner join subjects t2
on t3.subid = t2.id;
內連接(INNER JOIN)
INNER JOIN ...表 ON ...條件
-- 查詢學生的姓名、平均分
select students.sname,avg(scores.score)
from scores
inner join students on scores.stuid=students.id
group by students.sname;
/*查詢學生的編號,姓名,科目的名稱,成績*/
-- 方法一
select t1.id,t1.sname,t2.stitle,t3.score
from students t1,subjects t2,scores t3
where t3.stuid=t1.id and t3.subid=t2.id;
-- 方法二
select t1.id,t1.sname,t2.stitle,t3.score
from scores t3
inner join students t1
on t3.stuid = t1.id
inner join subjects t2
on t3.subid = t2.id;
-- 方法三
select t1.id,t1.sname,t2.stitle,t3.score
from students t1
inner join scores t3
on t3.stuid = t1.id
inner join subjects t2
on t3.subid = t2.id;
-- 查詢學生的姓名、平均分
select students.sname,avg(scores.score)
from scores
inner join students on scores.stuid=students.id
group by students.sname;
-- 查詢男生的姓名、總分
select students.sname,sum(scores.score)
from scores
inner join students on scores.stuid=students.id
where students.sex='男'
group by students.sname;
-- 查詢科目的名稱、平均分
select subjects.stitle,avg(scores.score)
from scores
inner join subjects on scores.subid=subjects.id
group by subjects.stitle;
外聯結
1、左外連接(LEFT OUTER JOIN)
概述:指將左表的所有記錄與右表符合條件的記錄,返回的結果除內連接的結果,還有左表不符合條件的記錄,并在右表相應列中填NULL。
2、右外連接(RIGHT OUTER JOIN)
概述:與左外連接相反,指將右表的所有記錄與左表符合條件的記錄,返回的結果除內連接的結果,還有右表不符合條件的記錄,并在左表相應列中填NULL。
學生姓名來源于students表,科目名稱來源于subjects,分數來源于scores表
當查詢結果來源于多張表時,需要使用連接查詢
當查詢結果來源于多張表時,需要使用連接查詢
關鍵:找到表間的關系,當前的關系是
students表的id---scores表的stuid
subjects表的id---scores表的subid
select students.sname,subjects.stitle,scores.score
from scores
inner join students on scores.stuid=students.id
inner join subjects on scores.subid=subjects.id;
- 連接查詢分類如下:
- 表A inner join 表B:表A與表B匹配的行會出現在結果中
- 表A left join 表B:表A與表B匹配的行會出現在結果中,外加表A中獨有的數據,未對應的數據使用null填充
- 表A right join 表B:表A與表B匹配的行會出現在結果中,外加表B中獨有的數據,未對應的數據使用null填充
- 在查詢或條件中推薦使用“表名.列名”的語法
- 如果多個表中列名不重復可以省略“表名.”部分
- 如果表的名稱太長,可以在表名后面使用' as 簡寫名'或' 簡寫名',為表起個臨時的簡寫名稱
用一個部門表,員工表演示一下內外連接查詢
-- 部門表,員工表
drop table if exists emp;
drop table if exists dept;
create table dept(
id int auto_increment primary key,
dname varchar(20)
);
create table emp(
id int auto_increment primary key,
ename varchar(20),
did int not null,
mgr int,
foreign key(did) references dept(id),
foreign key(mgr) references emp(id)
);
insert into dept(dname) values('研發部');
insert into dept(dname) values('人事部');
insert into dept(dname) values('財務部');
insert into emp(ename,did,mgr) values('老王',1,null);
insert into emp(ename,did,mgr) values('老張',1,1);
insert into emp(ename,did,mgr) values('老趙',1,1);
insert into emp(ename,did,mgr) values('小紅',2,3);
insert into emp(ename,did,mgr) values('小麗',2,3);
/*查詢員工額編號,姓名,上級的姓名*/
-- 1
select t1.id,t1.ename,t2.ename
from emp t1,emp t2
where t1.mgr = t2.id;
-- 2inner join
select t1.id,t1.ename,t2.ename
from emp t1
inner join emp t2
on t1.mgr = t2.id;
-- 3左外連接
select t1.id,t1.ename,t2.ename
from emp t1
left join emp t2
on t1.mgr = t2.id;
-- 4右外連接
select t1.id,t1.ename,t2.ename
from emp t1
right join emp t2
on t1.mgr = t2.id;
自關聯
概述:指用表的別名實現表自身的連接。
在員工表中mgr列中代表了他們的上級
/*查詢員工額編號,姓名,上級的姓名*/
select 員工表.id,員工表.ename 員工,上級表.ename 上級 from emp 員工表,emp 上級表
where 員工表.mgr = 上級表.id;
子查詢
查詢支持嵌套使用
查詢各學生的語文、數學、英語的成績
-- 查詢各學生的語文、數學、英語的成績
select sname,
(select sco.score from scores sco inner join subjects sub on sco.subid=sub.id
where sub.stitle='語文' and stuid=stu.id) as 語文,
(select sco.score from scores sco inner join subjects sub on sco.subid=sub.id
where sub.stitle='數學' and stuid=stu.id) as 數學,
(select sco.score from scores sco inner join subjects sub on sco.subid=sub.id
where sub.stitle='英語' and stuid=stu.id) as 英語
from students stu;
內置函數
字符串函數
-- 查看字符的ascii碼值ascii(str),str是空串時返回0
select ascii('a');
-- 查看ascii碼值對應的字符char(數字)
select char(97);
-- 拼接字符串concat(str1,str2...)
select concat(12,34,'ab');
-- 包含字符個數length(str)
select length('abc');
-- 截取字符串
-- left(str,len)返回字符串str的左端len個字符
-- right(str,len)返回字符串str的右端len個字符
-- substring(str,pos,len)返回字符串str的位置pos起len個字符
select substring('abc123',2,3);
-- 去除空格
-- ltrim(str)返回刪除了左空格的字符串str
-- rtrim(str)返回刪除了右空格的字符串str
-- trim([方向 remstr from str)返回從某側刪除remstr后的字符串str,
-- 方向詞包括both、leading、trailing,表示兩側、左、右
select trim(' bar ');
select trim(leading 'x' FROM 'xxxbarxxx');
select trim(both 'x' FROM 'xxxbarxxx');
select trim(trailing 'x' FROM 'xxxbarxxx');
-- 返回由n個空格字符組成的一個字符串space(n)
select space(10);
-- 替換字符串replace(str,from_str,to_str)
select replace('abc123','123','def');
-- 大小寫轉換,函數如下
-- lower(str)
-- upper(str)
select lower('aBcD');
數學函數
-- 求絕對值abs(n)
select abs(-32);
-- 求m除以n的余數mod(m,n),同運算符%
select mod(10,3);
select 10%3;
-- 地板floor(n),表示不大于n的最大整數
select floor(2.3);
-- 天花板ceiling(n),表示不小于n的最大整數
select ceiling(2.3);
-- 求四舍五入值round(n,d),n表示原數,d表示小數位置,默認為0
select round(1.6);
-- 求x的y次冪pow(x,y)
select pow(2,3);
-- 獲取圓周率PI()
select PI();
-- 隨機數rand(),值為0-1.0的浮點數
select rand();
-- 還有其它很多三角函數,使用時可以查詢文檔
日期時間函數
獲取子值,語法如下
year(date)返回date的年份(范圍在1000到9999)
month(date)返回date中的月份數值
day(date)返回date中的日期數值
hour(time)返回time的小時數(范圍是0到23)
minute(time)返回time的分鐘數(范圍是0到59)
second(time)返回time的秒數(范圍是0到59)
select year('2016-12-21');
日期計算,使用+-運算符,數字后面的關鍵字為year、month、day、hour、minute、second
select '2016-12-21'+interval 1 day;
獲取年%Y,返回4位的整數
獲取年%y,返回2位的整數
獲取月%m,值為1-12的整數
獲取日%d,返回整數
獲取時%H,值為0-23的整數
獲取時%h,值為1-12的整數
獲取分%i,值為0-59的整數
獲取秒%s,值為0-59的整數
/*日期-->字符串*/
select date_format('2017-10-20','%Y年%m月%d日')
/*字符串-->日期*/
select str_to_date('2017年10月20日','%Y年%m月%d日')
當前日期current_date()
select current_date();
當前時間current_time()
select current_time();
當前日期時間now()
select now();