还在更新中,若是有错误欢迎指出,参考了 Oracle手册,数据库系统概念第6版,部分博客 和 教授上课讲述 的内容html
数据库是学业课,同时居然爬虫也学了,那么爬取大量数据时用来管理和处理海量数据的数据库怎么能少了呢,因此写一篇关于SQL语言的学习笔记,可能部分语法在不一样的商业数据库软件有些出入,不过大部分应该是统一的,本文语法大部分以 Oracle 或 mySQL 为准(固然主要应该仍是 mySQL),在不一样的地方会分别给出属于不一样数据库的不一样写法
说到数据库,最多见的就是关系数据库了,SQL关系数据库提供了 4 类语言供用户使用和修改数据库,分别是:前端
其中不少分类把 DQL 和 DML 合在一块儿称为 DML ,那样就是 3 类
也有人在这三类基础上又加上了 TCL —— 事务控制语言,那仍是 4 类程序员
那么为何要把SQL语言分类?
由于这些语言使用的人群不一样,一个数据库须要有程序员为他写一个对外界用户交互的程序(或者说前端),这个时候程序员接触到的更多的是 DML 和 DQL,就是说只须要查询和适当的修改数据库里的数据便可;固然一个数据库还须要一个超级管理员来维护,这个时候超级管理员就能够接触到 DCL 和 DDL,用来修改数据库和分配权限;最后若是一个数据库没有前端,直接让外部用户来操做的话,那咱们指望的就是让用户使用一部分 DQL ,仅提供部分查询功能供用户使用web
另外就是关系数据库其实是由不少个表构成的,因此咱们大部分操做围绕 关系 和 关系的集合——表 进行,做为前驱知识可能会用到 实际上并不会用到 关系代数sql
返回目录
数据库
表 (table) 做为关系数据库的基础,几乎全部操做都围绕表进行,对此,应先创建一个关系表来存储数据oracle
create table <table_name>( <属性名_1> <type_1>, <属性名_2> <type_2>, ... <属性名_n> <type_n>, <完整性约束_1>, ... <完整性约束_n> );
其中属性名的类型有如下几种:
以 SQL Server 为准:app
其它数据库的能够查看 SQL数据类型svg
另外若是想约束一个属性值不能为空,能够在后面加上 not null 限定
更多的完整性约束语句待更函数
create table Table1( name varchar(8) not null, age int, id nchar(18) not null, primary key (id) //选取 id 做为主键 );
用来建立和使用数据库
create database <database_name>; //建立一个名为 database_name 的数据库 use <database_name>; //使用这个数据库
还有一个删除操做 delete 属于 DML
delete 比 drop 弱一点
delete from <table_name>; //会删除表中全部关系,保留一个空表 drop table <table_name>; //直接删掉表
alter table <table_name> add <属性名> <type>; //将会在表后添加一列属性 alter table <table_name> drop <属性名>; //删除那一列属性
在表中插入一条数据,能够有空值
insert into <table_name> values (<信息_1>, <信息_2> ... <信息_n>); //例子 insert into Table1 values ("李三", 24, "114514"); //注意顺序要跟建表属性顺序一致 insert into Table1 values ("李三", null, "114514"); insert into Table1 (name, id, age) values ("张四", "415411", 42); //或者指定顺序,比较麻烦
另外值得注意的是在 mySQL 里字符串用双引号是没问题的,但在 Oracle 中须要用单引号(双引号另有别的意思)
建议看完这个直接去看 select (DQL),而后回来看 update
//在 insert 中也可使用 select 语句 insert into Table1 select * from Table2;
Table7:
name | age | id | sex | salary |
---|---|---|---|---|
李三 | 24 | 114514 | 男 | 3500.00 |
张四 | 42 | 415411 | 女 | 3000.00 |
王五 | 99 | 666666 | 男 | 6666.00 |
弟弟 | 2 | 123123 | 男 | 1500.00 |
妹妹 | 99 | 123124 | 女 | 1500.00 |
//将全部年龄 大于50岁的人 工资增加10% update Table7_1 set salary = 1.1*salary where age > 50;
如今咱们又有一个表,记录了这些人在另外一处填写的出生日期,众所周知,别人在写年龄的时候总喜欢往年轻了写,而小孩写年龄的时候总会瞎写 (不是) ,但有了出生日期之后咱们就能够更正他们的年龄
Table7_1:
id | birthday |
---|---|
114514 | 1995-01-11 |
415411 | 1969-02-03 |
666666 | 1953-11-11 |
123124 | 2011-10-01 |
注意这个表里的 birthday 是 date 类数据,是 SQL 提供给使用者的一种数据类型,能够方便的计算和存储日期和时间,声明的时候类型写 date 就行
//在 mySQL 中想要插入一个 date 数据很简单 mySQL: insert into <table_name> values ('2019-10-01'); //在 Oracle 中这样是不行的,要用 to_date(a, b) 函数, a 是你要输入的日期,b 是格式 //一般 year 是4个y,mouth 是两个m,day 是两个d Oracle: insert into <table_name> values (to_date('20191001','yyyymmdd')); insert into <table_name> values (to_date('2019-10-01','yyyy-mm-dd')); //而从 date 中提取年份等 mySQL: year(date) //就能够直接提取年份了,返回数字类型, mouth 和 day 同理 Oracle: to_char(date,'yyyy') //用to_char(),返回字符串 cast(to_char(date,'yyyy') as int) //这样能够返回 int 参与数值计算
//以 2019 年为准更新正确的年龄,且不要更新 id 为 666666 的那我的(不要问为何XD) A 语句: update Table7 a set age = ( select 2019 - year(birthday) from Table7_1 b where a.id = b.id and a.id <> '666666' ); B 语句: update Table7 a set age = ( select 2019 - year(birthday) from Table7_1 b where a.id = b.id ) where a.id <> '666666'; C 语句://利用 case then else end 语句,不如 D 语句 update Table7 a set age = ( case when a.id in (select id from Table7_1) then (select 2019 - year(birthday) from Table7_1 b where a.id = b.id) else (select age from (select b.age from Table7 b where a.id = b.id) c) //mySQL 这里须要重命名并重建表,由于不能让原表 = 原表,只能让原表 = 原表的复制,略蠢 XD //Oracle 直接写 else (select b.age from Table7 b where a.id = b.id) 就行 end ) where id <> '666666'; D 语句: update Table7 a set age = ( select 2019 - year(birthday) from Table7_1 b where a.id = b.id ) where id <> '666666' and id in (select id from Table7_1);
A 语句执行结果:
能够看到 id 为 666666 的被设为了 null
由于 update set 的原理是这样的
由于我在 set 内部执行了 where a.id <> ‘666666’ 在更新 Table7 中 id=‘666666’ 那一行时,内部 where 断定都不符合条件,返回了 null ,外部没有 where ,默认为更新,就把 null 更新了上去,因此这个断定应该放在外部 where 中
name | age | id | sex | salary |
---|---|---|---|---|
李三 | 24 | 114514 | 男 | 3500.00 |
张四 | 50 | 415411 | 女 | 3000.00 |
王五 | null | 666666 | 男 | 6666.00 |
弟弟 | null | 123123 | 男 | 1500.00 |
妹妹 | 8 | 123124 | 女 | 1500.00 |
B 语句执行结果:
的确解决了上一个问题,可是仔细看 Table7_1 会发现 弟弟 没有对应的生日,会被返回一个 null,解决方法有两种:
一种是 C ,在内部 select 处让他无法返回 null (很麻烦,仅仅是为了引出 case then else end 语句)
一种是 D ,在外部 where 限定让 null 的值不更新 (简单点)
name | age | id | sex | salary |
---|---|---|---|---|
李三 | 24 | 114514 | 男 | 3500.00 |
张四 | 50 | 415411 | 女 | 3000.00 |
王五 | 99 | 666666 | 男 | 6666.00 |
弟弟 | null | 123123 | 男 | 1500.00 |
妹妹 | 8 | 123124 | 女 | 1500.00 |
case 语句:
跟 if else 相似
//使用方法 case <name> when <值_1> then <执行_1> when <值_2> then <执行_2> ... //能够有多个when... then... else <执行_n> end //上面的意思就是 name 知足等于那个值,就执行对应 then 的语句,若是都不知足就执行 else //也能够这么写 case when <条件_1> then <执行_1> when <条件_2> then <执行_2> ... //能够有多个when... then... else <执行_n> end //上面的意思就是知足哪一个条件,就执行对应 then 的语句,若是都不知足就执行 else
没啥好说的,运行原理跟 update set 差很少,一行行检索,where 限定知足就删除,不知足就保留,若是没有where就是全删(默认where 恒为真)
delete from <table_name> where <条件>;
select 语句通常由三个部分组成
//以上文Table1为例: //我如今要找出全部年龄大于 20 岁的人 name 和 id select name, id from Table1 where age > 20; //如何查询一个表的全部元素? select * from <table_name>;
另外值得注意的是,select 返回的结果默认 all 属性,也就是说不去重复
若是想要去重须要 distinct 限定
PS: 实际操做时能不用 * 就不用 * ,带 * 的 select 没法很好的利用引索
//全部年龄大于 20 岁的人 name 和 id,且他们 id 不相同 select name, distinct id from Table1 where age > 20;
具体理解顺序能够这么理解
Table1:
name | age | id |
---|---|---|
李三 | 24 | 114514 |
张四 | 42 | 415411 |
Table2:
name | age | id |
---|---|---|
王五 | 99 | 666666 |
弟弟 | 2 | 123123 |
//我要查询两个表中年龄大于 25 岁的人 name 和 id //错误写法: select name, distinct id from Table1, Table2 where age > 25; //正确写法: select name, id from( //用 union all 语句将二者合并 select * from Table1 union all select * from Table2 ) a // mySQL 必定将这个要重命名,否则会报错,由于 mySQL 强制要求全部派生出来的表(嵌套子查询)都要重命名 // 而 Oracle 则不用 where age > 25;
Table1, Table2:
Table1.name | Table1.age | Table1.id | Table2.name | Table2.age | Table2.id |
---|---|---|---|---|---|
李三 | 24 | 114514 | 王五 | 99 | 666666 |
李三 | 24 | 114514 | 张四 | 42 | 415411 |
张四 | 42 | 415411 | 王五 | 99 | 666666 |
张四 | 42 | 415411 | 弟弟 | 2 | 123123 |
实际上给出的是两个表的 笛卡尔积
并运算: union all 表示返回合并结果不去重,union 表示返回结果去重
差运算: except all 和 except ,作
操做,也就是说若 A 中列在 B 中出现则去掉,最后结果是 A 的子集
PS: Oracle 使用 minus 代替 except,而 mySQL 不支持 except ,代替方法能够看下文
交运算: intersect all 和 intersect,等价于
PS:这个 mySQL 也不支持
值得注意的是集合运算只能在两表列数相同,且列属性彻底一致时使用,否则会报错
//求 A 和 B 的交,在 mySQL 中代替 intersect select * from A where (A.<属性名1>, A.<属性名2>... A.<属性名n>) in (select * from B); //求 A 和 B 的差,在 mySQL 中代替 except 或 minus select * from A where (A.<属性名1>, A.<属性名2>... A.<属性名n>) not in (select * from B);
分为如下几种,我认为 偷懒 这个图片写的很好
固然,SQL语言也提供了 natural join(天然连接) 的操做,这个操做会自动检索同名的列属性进行笛卡儿积合并,由于是自动检索,最后不会出现同名属性,因此若是使用天然连接,不须要在属性前加限定词
Table3:
id | sex |
---|---|
123123 | 女 |
114514 | 男 |
这个时候用 natural join(天然连接)
select * from Table1 natural join Table3; //返回结果不一样于下列语句 select * from Table1 a join Table3 b on a.id = b.id; //或者也能够用 where 代替 on,但不推荐(慢一点,并且乱) select * from Table1 a join Table3 b where a.id = b.id;
第一个语句会返回以下结果,将 id 相同的合并在一块儿
name | age | id | sex |
---|---|---|---|
李三 | 24 | 114514 | 男 |
而用第二个或第三个语句会返回
name | age | Table1.id | Table3.id | sex |
---|---|---|---|---|
李三 | 24 | 114514 | 114514 | 男 |
natrual 字段会自动将相同词条合并,因此说 natural join 实际上比单纯的 join + on 要快,由于 join 是先生成笛卡儿积再用 on 筛选,而 natural join 边生成边筛选
若是想用 join 而且合并相同属性,能够在 select 处进行处理,跟 natrual join 效果同样(但麻烦了)
select a.name, a.age, a.id, b.sex //剔除多余的id列,只保留a的id列 from Table1 a join Table3 b on a.id = b.id;
有时候咱们须要保留原表信息,那么就会用到 left join, right join 和 full join
select * from Table1 natural left join Table3; //也能够写成 select a.name, a.age, a.id, b.sex from Table1 a left join Table3 b on a.id = b.id; //其它同理
Left join:
name | age | id | sex |
---|---|---|---|
李三 | 24 | 114514 | 男 |
张四 | 42 | 415411 | null |
Right join:
name | age | id | sex |
---|---|---|---|
李三 | 24 | 114514 | 男 |
null | null | 123123 | 女 |
Full join:
name | age | id | sex |
---|---|---|---|
李三 | 24 | 114514 | 男 |
null | null | 123123 | 女 |
张四 | 42 | 415411 | null |
值得注意的是 mySQL 是不支持 full join 的 (Oracle 能够)
若是你使用 mySQL 能够用下列语句代替 full join
(select * from Table1 natural left join Table3) union (select * from Table1 natural right join Table3);
若是你想对某一个或某几个元素进行排序,可使用 order by 后缀
//对 Table1 和 Table2 的人按 age 排序,若是年龄相同按 id 排序,且知足 age 大于 10 select * from( select * from Table1 union all select * from Table2 ) a where age > 10 order by age, id;
返回结果:
name | age | id |
---|---|---|
李三 | 24 | 114514 |
张四 | 42 | 415411 |
王五 | 99 | 666666 |
order by 默认升序,在属性名后加 desc 能够降序
order by age desc, id; //表明先用 age 降序排序,若是 age 相同再用 id 升序排序
如何找到 Table1 和 Table2 中 age 最大的那我的
咱们能够用下列语句:
//先建立一个 Table4 储存 Table1 和 Table2 的并 create table Table4 as select * from( select * from Table1 union all select * from Table2 ) a; //找到其中年龄最大的,mySQL 的写法,由于 mySQL 没有差运算,因此能够这么写 select a.name, a.age from Table4 a where (a.name, a.age) not in( select b.name, b.age from Table4 a, Table4 b where a.age > b.age ); //原理是先作一个此表与本身的笛卡儿积,若是其中有左面年龄大于右面的状况,那就说明右面的年龄必不是最小值,从原表剔除,最后剩下的必定是最大值 //若是有差运算(以 Oracle 为例)也能够这么写,效率没有上面高 select name, age from( (select a.name, a.age from Table4 a, Table4 b) minus (select b.name, b.age from Table4 a, Table4 b where a.age > b.age) );
作起来很麻烦,因此SQL提供给了咱们一类分组函数,下面是几个比较经常使用的
//使用分组函数max,能够看到简单了不少 select name, age, sex from Table4, (select max(age) Max_age from Table4) b where age = b.Max_age; //更好的写法(省空间和时间): select name, age, sex from Table4 where age in (select max(age) Max_age from Table4); //注意,下面是错误的写法,这样 name 没法对应正确的最大年龄,会默认第一我的的姓名 select name, max(age) Max_age, sex from Table3;
一般分组函数须要配合 group by 后缀使用,这个后缀决定了你的分组函数是以什么为基准进行计算的,好比说什么都不加,那就意味着以全表数据为准,举个例子
Table5:
name | age | id | sex |
---|---|---|---|
李三 | 24 | 114514 | 男 |
张四 | 42 | 415411 | 女 |
王五 | 99 | 666666 | 男 |
弟弟 | 2 | 123123 | 男 |
//找 Table5 中男性和女性的最大年龄者(包括姓名) select name, age, sex from Table5 where age in (select max(age) Max_age from Table5 group by sex); //找 Table5 中对应性别的最大年龄 select max(age) Max_age, sex from Table5 group by sex;
以第二个为例,因为不须要找到对应的姓名,因此理解起来比较轻松
group by sex 意思是告诉这个 select 在执行分组函数时以 sex 为组,意思就是以不一样的 sex 男和女分组,对每一个组分别进行找 max(age) 操做
第二个最后返回结果:
Max_age | sex |
---|---|
42 | 女 |
99 | 男 |
若是你的 group by 选择了多个属性名,那么会根据这些属性名一块儿分组,好比说
group by sex,name 就是找 sex 相同,再找 name 也相同的那一组人
另外值得注意的是 order by 应该写在 group by 的后面
Table6:
company | id | salary |
---|---|---|
Baidu | 114514 | 3500 |
Baidu | 666666 | 6666 |
Baidu | 123123 | 1500 |
HuaWei | 415411 | 3000 |
HuaWei | 100000 | 2019 |
XiaoMi | 100001 | 9102 |
DaMi | 100002 | 3 |
我如今想知道 平均薪水 (avg_salary) 大于 2500 的全部公司
这时候因为是对分组函数的结果进行选择,应使用 having 语句
//错误写法 select company, round(avg(salary), 2) avg_salary from Table6 where round(avg(salary),2) > 2500 group by company; //正确写法 select company, round(avg(salary), 2) avg_salary from Table6 group by company having round(avg(salary),2) > 2500; //round(a, b) 函数是对 a 进行小数位截取,保留 b 位小数
company | avg_salary |
---|---|
Baidu | 3888.67 |
HuaWei | 2509.50 |
XiaoMi | 9102.00 |
是否还记得咱们找 Table4 中的年龄最大者
Table4_1:
name | id | age | sex |
---|---|---|---|
李三 | 24 | 114514 | 男 |
张四 | 42 | 415411 | 女 |
王五 | 99 | 666666 | 男 |
弟弟 | 2 | 123123 | 男 |
妹妹 | 99 | 123124 | 女 |
例1:我如今想换种方式找最大的年龄:
select name, age from Table4_1 where age >= all ( select age from Table4_1 );
> all 是指找一个比这个集合中全部元素都大的
> some 是指找一个比这个集合至少某一个元素大的
其余运算符 <, <>, = 等同理
例2:找到全部年龄有重复的人
select name, age from Table4_1 a where a.age = some ( select b.age from Table3 b where b.name <> a.name ); //其实这个 =some 换成 in 也行 //相似的,能够用来找表中重复的数据,可是这个效率不高,下面效率高 select * from Table4_1 where age in( select age from Table3 group by age having count(*)>1 );
对于 select 来讲 具体运行顺序:
PS:值得注意的是在汇集函数中使用 distinct 是能够去重的,虽然 distinct 在 汇集函数 以后进行,但能够理解成汇集函数里是一个 子select 语句,是对这个选出来的子表进行 distinct 操做以后再汇集,也符合顺序
select count(distinct <属性A>) from <table_name>; //找出表中不一样的 属性A 列数量
这个PS的内容我不太肯定,若是有错误欢迎在评论指正Orz
写法顺序:
select <属性集> from <表集合> on <条件集合> //有三个和三个以上的表用 on 这么写: //from ((A join B on <条件集合>) join C on <条件集合>) join D on <条件集合> where <条件集合> group by <属性集> having <条件集合> order by <属性集>
有了这些知识,再去看 update(DDL) 就很轻松啦
(应该是这样的