笔记一 1: 数据库概述 1.1: 什么是数据库 数据库是一个专业的存储和管理数据的仓库 扩展内容:数据库的分类 早期: 层次式数据库,网络型数据库 现在: 关系型数据库,非关系型数据库 现在市面上大部分用的还是关系型数据库 1.2: 什么是关系型数据库 底层以二维表的形式保存数据的库,就是关系型数据库 常见的关系型数据库有哪些? Oracle: 甲骨文公司提供的一款数据库产品,收费的,之前在Java中的市场份额超过50%。 主要适用于一些大型或者超大型应用系统。 SQL Server: 微软提供的一款数据库产品,收费,主要适用于一些中型或者大型的应用系统 MySQL: 瑞典的一个公司(MySQLAB)提供的一款数据库产品,特点是小巧轻量,简单易用,适用于一些小型或中型的应用系统,如果做mysql集群,也可以用于一些大型或者超大型系统。免费! mysql被甲骨文收购了! DB2: IBM公司提供的一款数据库产品, 用于金融/银行等系统较多, 收费! SQLite: 迷你数据库, 用于嵌入式设备(手机/智能家居等产品) ... 1.3: 数据库相关名字解释 数据库服务器: 其实就是你安装的哪个mysql软件,将mysql安装在计算机上,那么这台计算机就可以作为数据库服务器使用,可以实现数据的存和取。一个数据库服务器中可以包含多个数据库。 比如:装好的mysql服务器中自带了四个数据库 +--------------------+ | Database | +--------------------+ | information_schema | | mysql | | performance_schema | | test | +--------------------+ 数据库: 数据库就是存储数据的仓库,通常情况下一个网站(系统)中的所有数据会存放在一个数据库中 京东网站的所有数据 db_jd 淘宝网站的所有数据 db_taobao 百度网站的所有数据 db_baidu ... 表: 数据库中的数据是安装类型存放的, 一类数据往往存储在一张表中, 一个数据库中可以创建多张表! 京东网站的用户数据 tb_user 京东网站的商品数据 tb_product 京东网站的订单数据 tb_order ... 表记录: 一张表中可以包含多行表记录, 一张表中用于存储一类信息, 一行表记录就用于存储某一个具体的数据 数据库中的表 java中的类(student) 表记录 对象 1.4: SQL语言 SQL语言是一门操作关系型数据库的通用的语言(学会了SQL可以操作所有的关系型数据库) SQL语言可以操作的有: 1)查看库、创建库、删除库、修改库(了解) 2)创建表、删除表、修改表(扩展内容中)、查看表 3)新增表记录、删除表记录、修改表记录、查询表记录(数据) 4)存储过程/视图/索引等也可以操作 SQL语言是一门通用的操作关系型数据库的语言,但每个数据库厂商为了增强自己数据库的功能,都提供了少量的"方言"(独有的SQL语句),SQL语言通用,但方言不通用! 1.5: 如何连接mysql服务器(cmd窗口) 方式一: mysql -u用户名 -p密码 mysql中默认有一个超级管理员(具有所有权限),用户名就是root 方式二: mysql -u用户名 -p 在下一行键入密码 方式三: mysql -u用户名 -p -h主机名或ip地址 -P端口 -h: 后面跟的是主机名或ip地址,如果不写-h,默认连接localhost(127.0.0.1) -P: 后面跟的是端口, 如果不写-P,默认端口是3306 退出连接mysql服务器: exit quit 或者直接关闭窗口 扩展内容: SQL注释 -- 单行注释(注意--后面的空格不要省去) #单行注释 /* 多行注释 */ 扩展内容: \c: 取消当前sql语句的执行 ---------------------------------- 1.数据库及表操作 1.1.创建、删除、查看数据库 ---------------------------------- -- 01.查看mysql服务器中所有数据库 show databases; -- 02.进入某一数据库(进入数据库后,才能操作库中的表和表记录) -- 语法:USE 库名; use mysql; -- 进入mysql数据库 use test; -- 进入test数据库 -- 查看已进入的库 select database(); -- 03.查看当前数据库中的所有表 -- 先进入某一个库,再查看当前库中的所有表 use test; show tables; -- 04.删除mydb1库 -- 语法:DROP DATABASE 库名; drop database test; -- 删除test库 drop database mydb1; -- 删除mydb1库,如果删除的库不存在,会报错! -- 思考:当删除的库不存在时,如何避免错误产生? drop database if exists mydb1; -- 删除mydb1库,如果存在则删除mydb1,如果不存在则不执行删除操作,也不会报错 -- 05.重新创建mydb1库,指定编码为utf8 -- 语法:CREATE DATABASE 库名 CHARSET 编码; create database mydb1 charset utf8; -- 需要注意的是,mysql中不支持横杠(-),所以utf-8要写成utf8; -- 如果不存在则创建mydb1; create database if not exists mydb1 charset utf8; -- 如果不存在则创建mydb1,如果已存在,则不执行创建操作,也就不会报错了! -- 06.查看建库时的语句(并验证数据库库使用的编码) -- 语法:SHOW CREATE DATABASE 库名; show create database mydb1; ---------------------------------- 1.2.创建、删除、查看表 ---------------------------------- -- 07.进入mydb1库,删除stu学生表(如果存在) -- 语法:DROP TABLE 表名; use mydb1; drop table if exists stu; -- 08.创建stu学生表(编号[数值类型]、姓名、性别、出生年月、考试成绩[浮点型]),建表的语法: CREATE TABLE 表名( 列名 数据类型, 列名 数据类型, ... 列名 数据类型 ); 创建stu表的SQL语句如下: create table stu( id int, name varchar(50), -- 50表示最多存50个字符 gender varchar(10), birthday date, score double ); -- mysql中也有数据类型,这里先使用,后面再做说明! -- 09.查看stu学生表结构 -- 语法:desc 表名 desc stu; ---------------------------------- 2.新增、更新、删除表记录 ---------------------------------- -- 10.往学生表(stu)中插入记录(数据) select * from stu; -- 语法:INSERT INTO 表名(列名1,列名2,列名3...) VALUES(值1,值2,值3...); -- 创建mydb1库时一定要指定编码utf8,这样在库中创建的表也是utf8编码 -- 如果是在cmd中执行插入记录的语句,先 set names gbk; 再插入记录! -- mysql中推荐使用单引号包裹字符串和日期(有些版本的数据库双引号包裹会报错!) -- 一个cmd窗口只需要设置一次(set names gbk;)编码 -- 注意: 如果是要给表中的所有列都插入值,列名可以省略不写, 值的个数和顺序必须和表中创建的列的个数和顺序保持一致! -- value后面只能跟一个括号,只能一次插入一条记录,values后面可以跟多个括号,用逗号分隔,一次可以插入多条数据! insert into stu(id, name, gender, birthday, score) value(1, 'tom', 'male', '1985-10-11', 86); insert into stu values(2, '陈子枢', '男', '1978-10-12', 75); insert into stu values(3, '程晓宇', '女', '2000-6-12', 80); -- 11.查询stu表所有学生的信息 -- 语法:SELECT 列名 | * FROM 表名 select * from stu; -- 12.修改stu表中所有学生的成绩,加10分特长分 -- 修改语法: UPDATE 表名 SET 列=值,列=值,列=值...[WHERE子句]; update stu set score=score+10; -- mysql不支持复合运算符 score+=10 错! -- 13.修改stu表中编号为1的学生成绩,将成绩改为83分。 update stu set score=83 where id=1; -- 只修改编号为1的学生的成绩 update stu set id=22, name='刘沛霞', gender='女', birthday='1985-7-7', score=88 where id=2; 提示:where子句用于对记录进行筛选过滤,保留符合条件的记录,将不符合条件的记录剔除。 -- 14.删除stu表中所有的记录 -- 删除记录语法: DELETE FROM 表名 [where子句] -- 仅删除符合条件的 delete from stu where id<2; -- 仅删除复合条件的记录 delete from stu; -- 没有where默认删除所有记录 ---------------------------------- 3.查询表记录 3.1.基础查询 -- 准备数据: 以下练习将使用db10库中的表及表记录,请先进入db10数据库!! ---------------------------------- -- 15.查询emp表中的所有员工,显示姓名,薪资,奖金 select * from emp; -- *是通配符,表示查询所有的列 select name,sal,bonus from emp; -- 查询指定的列(只会显示查询的列) -- 16.查询emp表中的所有部门和职位 select dept, job from emp; -- 上面的查询结果中有很多重复的记录,如何剔除重复记录,只保留一条? -- distinct用于去除重复记录,只保留一行! select distinct dept, job from emp; 3.2.WHERE子句查询 -- 17.查询emp表中【薪资大于3000】的所有员工,显示员工姓名、薪资 select name,sal from emp where sal>3000; -- 18.查询emp表中【总薪资(薪资+奖金)大于3500】的所有员工,显示员工姓名、总薪资 select name,sal+bonus from emp where sal+bonus > 3500; -- null和任何数据运算结果都是null,这里应该将null值看做零来处理 -- 方式一: 将所有的奖金为null的列值更新为0 update emp set bonus=0 where bonus is null; -- 方式二: 在查询时将null值看做0来处理(这种方式对表中的数据不产生任何影响) -- ifnull(列名, 值)函数: 判断指定的列是否包含null值,如果有null值,用第二个值替换null值 ifnull(bonus, 0) -- 对bonus列中的null进行处理,将null值用第二个参数0进行替换 select name,sal+ifnull(bonus,0) from emp where sal+ifnull(bonus,0) > 3500; -- 注意查看上面查询结果中的表头,如何将表头中的 sal+bonus 修改为 "总薪资" -- 使用`as`可以为表头指定别名(另外as可以省略) select name as 姓名,sal+ifnull(bonus,0) as 总薪资 from emp where sal+ifnull(bonus,0) > 3500; -- as可以省略 select name 姓名,sal+ifnull(bonus,0) 总薪资 from emp where sal+ifnull(bonus,0) > 3500; -- 19.查询emp表中【薪资在3000和4500之间】的员工,显示员工姓名和薪资 select name,sal from emp where sal>3000 and sal<4500; -- and是并的意思(相当于java中的&),要求表中的记录要同时满足and两边的条件才算满足条件! select name,sal from emp where sal>=3000 and sal<=4500; -- 也可以使用`between 值1 and 值2` 来完成, 表示判断某个列的值是否在值1和值2之间(包含值1也包含值2) select name,sal from emp where sal between 3000 and 4500; -- 20.查询emp表中【薪资为 1400、1600、1800】的员工,显示员工姓名和薪资 select name,sal from emp where sal=1400 or sal=1600 or sal=1800; -- sal in(1400,1600,1800),意思时只要员工的薪资等于in括号里面的任何一个值就算满足条件 select name,sal from emp where sal in(1400,1600,1800); -- 21.查询薪资不为1400、1600、1800的员工,显示员工姓名和薪资 select name,sal from emp where !(sal=1400 or sal=1600 or sal=1800); select name,sal from emp where not(sal=1400 or sal=1600 or sal=1800); select name,sal from emp where sal not in(1400,1600,1800); -- 22.(自己完成) 查询emp表中薪资大于4000和薪资小于2000的员工,显示员工姓名、薪资。 select name, sal from emp where sal>4000 or sal<2000; -- 23.(自己完成) 查询emp表中薪资大于3000并且奖金小于600的员工,显示员工姓名、薪资、奖金。 select name, sal, bonus from emp where sal>3000 and bonus<600; -- 查询结果有误差 -- 对null值进行处理 select name, sal, bonus from emp where sal>3000 and ifnull(bonus,0)<600; -- 24.查询没有部门的员工(即部门列为null值) select * from emp where dept is null; -- 判断一个列中的值是不是null不能用 = (等号), 而是用 is 判断 -- 思考:如何查询有部门的员工(即部门列不为null值) select * from emp where not(dept is null); select * from emp where dept is not null; 3.3.模糊查询(like) like可以用于做模糊查询(可以按照指定的模式进行搜索),需要结合%和_使用 select * from emp where name like '王海涛'; select * from emp where name='王海涛'; 在通过like进行模糊查询时,如果不配合%以及_使用,和=作用相同 %: 是通配符,可以表示0个或多个任意字符 _: 是通配符,只能表示1个任意字符 -- 25.查询emp表中姓名中以"刘"字开头的员工,显示员工姓名。 select name from emp where name like '刘%'; -- 查询姓名列中以'刘'开头的名字, %表示'刘'的后面可以是0个或者1个或者多个任意字符 -- 26.查询emp表中姓名中包含"涛"字的员工,显示员工姓名。 select name from emp where name like '%涛%'; -- %涛%,可以匹配三种情况,1)当第一个%匹配0个字符时,表示以'涛'开头 2)第二个%匹配0个字符时,表示以'涛'结尾, 3)当前后两个%至少匹配一个字符时,包含涛在中间的某一个位置(既不在开头,也不再结尾的情况) -- 27.查询emp表中姓名以"刘"开头,并且姓名为两个字的员工,显示员工姓名。 select name from emp where name like '刘_'; select name from emp where name like '刘__'; -- 刘_,可以匹配姓名中以'刘'开头,并且刘的后面只能有一个字符的情况 -- 刘__,可以匹配姓名中以'刘'开头,并且刘的后面只能有两个字符的情况 3.4.多行函数查询 多行函数也叫做聚合函数(聚集函数), 常见的多行函数有: (多行函数会默认过滤null值,即不统计null值) count(列名 | *): count(列名): 表示统计当前列的值有多少个(不统计null值) count(*): 以行为单位,统计查询结果中有多少行记录 max(列名): 表示统计当前这一列中所有值中的最大值 min(列名): 表示统计当前这一列中所有值中的最小值 sum(列名): 表示统计当前这一列中所有值的和(也就是说会将这一列中所有的值加在一起返回) avg(列名): 表示统计当前这一列中所有值的平均值(这一列中所有值的和 / 不是null值的个数 ) -- 28.统计[emp表中薪资大于3000的员工]个数 select count(*) from emp where sal>3000; select count(*), name from emp where sal>3000; -- 在进行统计时,不要添加额外的列,因为没有任何意义(这里的name只会将第一行的name显示出来) -- 29.求emp表中的最高薪资 select max(sal) from emp; select min(sal) from emp; select max(sal), name from emp; -- 在进行统计时,不要添加额外的列,因为没有任何意义(这里的name也是只会将第一行的name显示出来) -- 这里的最高薪资和姓名没有任何关系, 薪资是所有薪资中的最大值,但name就是结果中的第一行的name -- 30.统计emp表中所有员工的薪资总和(不包含奖金) select sum(sal) from emp; -- 所有员工薪资总和 select sum(bonus) from emp; -- 所有员工奖金总和 -- 虽然bonus列中有null值,但是在通过多行函数统计时,遇到null会直接剔除,不会参与统计! -- 31.统计emp表员工的平均薪资(不包含奖金) select avg(sal) from emp; -- 所有薪资的平均值 select avg(bonus) from emp; -- 所有奖金的平均值 select sum(bonus) / count(bonus) from emp; -- 所有奖金的平均值 -- 统计emp表员工的平均总薪资(包含奖金) select avg(sal+bonus) from emp; -- 错!,因为韩少云的薪资(5000)加上奖金(null),结果是null,会被avg直接过滤 select avg(sal)+avg(bonus) from emp; -- 错!,avg(sal)由于sal中没有null值,是总薪资/12, 而avg(bonus)由于bonus中有一个null值,是总奖金/11 select avg(sal+ifnull(bonus,0)) from emp; 不分组使用多行函数 和 分了组使用多行函数的区别? ------------------------------------- select count(*) from emp; -- 如果没有分组,其实在使用count统计时会默认将整个查询结果当成一个组,这样的话,统计这一个组的人数,返回的就是一个数值; select count(*) from emp group by dept; -- 如果分了组(按照部门分了3组), 再使用count统计时会根据每个组来进行统计,有多少个组,就会统计出多少个结果(现有3个组,因此会统计出三个组的人数) select count(*),dept from emp -- 如果没有分组, 再通过多行函数进行统计时, 不要显示额外的列, 因为没有任何意义 -- (比如,上面的dept只会将第一行的dept显示出来, 和前面统计的人数没有任何关系) select count(*),dept from emp group by dept; -- 如果分了组, 再通过多行函数进行统计时, 可以将进行分组的列和多行函数一起显示 -- 比如:按照dept分了组(分成了3组,此时每组中的dept是相同), 可以将dept这一列的值显示出来, 因为是根据这个列分的组,每组中的这个列的值也是相同的,所以显示这个组中的dept列的任意一个值都是一样的。 ------------------------------------- 3.5.分组查询 语法:SELECT 列 | * FROM 表名 [WHERE子句] GROUP BY 列; -- 32.对emp表,按照部门对员工进行分组,查看分组后效果。 select id,name,dept from emp group by dept; -- 如何证明上面查询的结果是三组,而不是三条记录? -- 可以通过多行函数对分组后的数据进行统计,分成几组,就会统计出几个结果。 select count(*) from emp group by dept; -- 33.对emp表按照职位进行分组,并统计每个职位的人数,显示职位和对应人数 select id,name,job from emp group by job; +----+--------+------+ | id | name | job | +----+--------+------+ | 10 | 韩少云 | CEO | | 4 | 陈子枢 | 总监 | | 1 | 王海涛 | 讲师 | +----+--------+------+ -- 显示职位和对应人数 select count(*),job from emp group by job; +----------+------+ | count(*) | job | +----------+------+ | 1 | CEO | | 2 | 总监 | | 9 | 讲师 | +----------+------+ -- 34.对emp表按照部门进行分组,求每个部门的最高薪资(不包含奖金),显示部门名称和最高薪资 select id,name,sal,dept from emp group by dept; +----+--------+------+--------+ | id | name | sal | dept | +----+--------+------+--------+ | 10 | 韩少云 | 5000 | NULL | | 1 | 王海涛 | 1800 | 培优部 | | 6 | 王克晶 | 3700 | 就业部 | +----+--------+------+--------+ -- 显示部门名称和最高薪资 select max(sal), dept from emp group by dept; +----------+--------+ | max(sal) | dept | +----------+--------+ | 5000 | NULL | | 4500 | 培优部 | | 4850 | 就业部 | +----------+--------+ 3.6.排序查询 语法:SELECT 列名 FROM 表名 [where子句] [group by 列] ORDER BY 列名 [ASC|DESC] ASC(默认)升序,即从低到高;DESC 降序,即从高到低。 -- 35.对emp表中所有员工的薪资进行升序(从低到高)排序,显示员工姓名、薪资。 select name, sal from emp order by sal; -- 默认是asc,就是升序 select name, sal from emp order by sal asc; -- 默认是asc,就是升序 -- 36.对emp表中所有员工的奖金进行降序(从高到低)排序,显示员工姓名、奖金。 select name, bonus from emp order by bonus desc; -- 降序必须写desc -- 按照奖金降序排序,如果奖金相同,再按照薪资降序排序 select name, bonus, sal from emp order by bonus desc, sal desc; 3.7.分页查询 在mysql中,通过limit进行分页查询,查询公式为: limit (页码-1)*每页显示记录数, 每页显示记录数 -- 37.查询emp表中的所有记录,分页显示:每页显示3条记录,返回所有页的数据 -- 查询emp表中的记录,每页3条,查询第1页 select * from emp limit 0, 3; -- 查询emp表中的记录,每页3条,查询第2页 select * from emp limit 3, 3; -- 查询emp表中的记录,每页3条,查询第3页 select * from emp limit 6, 3; -- 查询emp表中的记录,每页3条,查询第4页 select * from emp limit 9, 3; -- 38.求emp表中薪资最高的前3名员工的信息,显示姓名和薪资 -- 根据薪资降序排序(从高到低), 第一条就是薪资最高的记录 select name, sal from emp order by sal desc; -- 在上面查询的基础上, 将第一条取出来(每页显示1条,查询第1页) select name, sal from emp order by sal desc limit 0,1; -- 根据薪资降序排序, 每页显示3条, 查询第一页就是薪资最高的前3名 select name, sal from emp order by sal desc limit 0,3; 3.8.其他函数 curdate() -- 获取当前日期: 年月日 curtime() -- 获取当前时间: 时分秒 sysdate()/now() -- 获取当前日期+时间, 年月日 时分秒 year('2020-8-10'): 返回日期中的年份, 2020 month('2020-8-10'): 返回日期中的月份, 8 day('2020-8-10'): 返回日期中的天数, 10 hour('2020-8-10 12:34:56'): 返回时间中的小时, 12 minute('2020-8-10 12:34:56'): 返回时间中的分钟数, 34 second('2020-8-10 12:34:56'): 返回时间中的秒值, 56 ------------------------------------------ concat(s1,s2,...sn): 将 s1、s2、...sn 拼接在一起返回 例如: name('王海涛'),birthday('1995-03-25'),sal(2450) select concat('王海涛', '1995-03-25', 2450); concat_ws(x,s1,s2,...sn): 将 s1、s2、...sn 拼接在一起,并且每两个拼接时会通过x作为分隔符进行拼接,再返回 select concat_ws(',' ,'王海涛', '1995-03-25', 2450); -- 39.查询emp表中所有【在1993和1995年之间出生】的员工,显示姓名、出生日期。 select name, birthday from emp where birthday between 1993 and 1995; -- 错误,由于birthday是日期类型(年月日格式),而1993和1995都是数值,没法比较 -- 方式一: 将1993和1995两个数值转成日期格式,再和birthday进行比较! select name, birthday from emp where birthday between '1993-1-1' and '1995-12-31'; -- 方式二: 将birthday中的年份用year函数提取出来,再和1993以及1995进行比较 select name, birthday from emp where year(birthday) between 1993 and 1995; -- 40.查询emp表中本月过生日的所有员工 -- 首先通过month函数从当前日期中获取本月是几月: month( now() ) -- 再通过month函数从员工的birthday中获取出生月份: month( birthday ) select * from emp where month( now() ) = month( birthday ); -- 41.查询emp表中员工的姓名和薪资(薪资格式为: xxx(元) ) select name, concat(sal, '(元)') from emp; -- 补充练习:查询emp表中员工的姓名和薪资(薪资格式为: xxx/元 ) select name, concat(sal, '/元') from emp; ---------------------------------- 补充内容1: mysql的数据类型 1.1:数值类型 mysql中提供了多种数值类型,其中包括: tinyint、smallint、int、bigint、float、double、decimal等 其中较为常用的就是 int、double 1.2.字符串类型 char类型: 定长字符串, char(n), n的范围是: 0~255个字符 char类型之所以叫做定长字符串,是因为一旦确定了n的最大字符数,不管存的数据是多少,该数据占用的空间就是n个字符。例如:name char(10), 存入'张三丰', 存入了3个字符,剩余的空间会用空格补全. 因此char类型可能会浪费空间! 所以char类型适合存储长度固定的数据, 比如: student_id char(11), 用这个列存储所有学生的编号. idcard char(18), 用这个列存储所有人的身份证号. char类型相比varchar类型速度要快一些,因为char类型只需要判断一个数据是否能存入该列中,而不需要将剩余的空间留给别的数据使用! varchar类型: 变长字符串, varchar(n), n的范围是: 0~?个字符 varchar类型之所以叫变长字符串,是因为n只是限制该列中最多能存的字符数, 如果你实际存的数据量小于n,剩余的空间还可以留给别的数据使用。例如:name char(10), 存入'张三丰', 存入了3个字符,剩余的7个空间会留给别的数据使用! 因此varchar类型不会浪费空间! 所以varchar类型适合存储长度不固定的数据(长度固定的数据我们会使用char类型) varchar类型最大能存的数据量是 0~65535个字节 latin1编码中,1个字符对应1个字节, n的最大值约是 65535/1 (max = 65532) 测试表: create table t2(id int, name varchar(60000)) charset latin1; gbk编码中,1个字符对应2个字节, n的最大值约是 65535/2 (max = 32766) 测试表: create table t3(id int, name varchar(40000)) charset gbk; utf8编码中,1个字符对应3个字节, n的最大值约是 65535/3 (max = 21844) 测试表: create table t4(id int, name varchar(20000)) charset utf8; create table t5(id int, name varchar(5)); 面试题: char和varchar有什么区别? 1) char和varchar存的数量是不同的, char类型最多能存255个字符, varchar类型最多能存65535个字节 2) char类型如果存的数据量小于最大长度, 剩余的空间会使用空格填充, 因此可能会浪费空间 所以char类型适合存储长度固定的数据, 这样既不会浪费空间, 效率还比varchar略高 3) varchar类型如果存的数据量小于最大长度, 剩余的空间会留给别的数据使用 所以varchar类型适合存储长度不固定的数据, 这样虽然没有char存储效率高, 但至少不会浪费空间。 1.3: 日期类型 date: 日期类型, 格式是: 年月日 time: 时间类型, 格式是: 时分秒 datetime: 日期+时间,格式是: 年月日 时分秒 timestamp: 时间戳, 格式和datetime相同, 也是: 年月日 时分秒, 和datetime不同的是: 1) 范围上: datetime范围是: 1000~9999(年份) timestamp范围是: 1970到2038年 2) 实际存的数据: datetime实际存的就是一个`年月日 时分秒`格式的日期+时间 而timestamp实际存储的是这个从1970年1月1日到这个日期+时间的时间毫秒值 create_time timestamp, 2018-2-3 14:45:56, 实际存储的是 1970年1月1日到2018-2-3 14:45:56时间的时间毫秒值 3) 在使用上: timestamp可以设置自动获取当前时间作为值插入到表中, 而datetime不可以. 2018-2-3 14:45:56 补充内容2: mysql的字段约束 ---------------------------------- use mydb1; drop table if exists stu; create table stu( id int primary key, -- id是主键,值不能为空且不能重复 name varchar(50), -- 50表示最多存50个字符 gender varchar(10), birthday date, score double ); insert into stu value('a', '张飞', '男', '1980-1-2', 80); insert into stu value('b', '刘备', '男', '1981-2-2', 90); insert into stu value('c', '关羽', '男', '1982-3-2', 85); ---------------------------------- 2.1: 主键约束 如果一个列添加了主键约束, 那么这个列的值就必须是非空的且不能重复 主键通常用于唯一的表示一行表记录(就像人的身份证号一样) 一张表中通常都会有且只有一个主键 添加主键约束的格式: create table stu( id int primary key, ... ); ---------------------------------- 如果id是主键并且是数值类型,为了方便维护,可以设置主键自增策略,设置方法: create table stu( id int primary key auto_increment, ... ); 在设置完主键自增之后,表中会维护一个AUTO_INCREMENT的值,这个值从1开始,如果插入主键时没有给主键赋值,就会从AUTO_INCREMENT这里获取一个值再作为主键插入到表中。再用完之后,会自动将AUTO_INCREMENT的值加1 2.3: 非空约束 如果一个列添加了非空约束, 那么这个列的值就不能为空(null), 但可以重复 添加非空约束的格式: create table stu( ... gender varchar(10) not null, ... ); 2.4: 唯一约束 如果一个列添加了唯一约束, 那么这个列的值就不能重复, 但可以为空(null) 比如: 网站绑定的邮箱, 前期可以不绑定, 即可以为null, 但一旦绑定, 这个邮箱是不能和其他账号的邮箱重复的。 添加唯一约束的格式: create table stu( ... email varchar(50) unique, ... ); create table stu( ... username varchar(50) unique not null, -- 用户名既不能重复,也不能为空 ... ); ---------------------------------- 主键约束 和 (非空+唯一约束) 有什么区别: 1) 主键约束 和 非空+唯一 特点是相同的, 都是不能为空且不能重复 2) 主键约束除了非空且不能重复之外, 还可以表示唯一一行表记录, 即作为表记录的唯一标识。 补充内容3: mysql的外键约束 外键约束不同于主键、非空、唯一约束,外键约束是用于表示两张表的对应关系 1、如何保存部门(dept) 和 员工(emp)的对应关系? 可以在员工表中添加一个列(比如: dept_id)用于保存部门的编号, 就可以保存员工和部门的对应关系(可以将dept_id设置为外键, 当然也可以不这样做) 2、添加外键和不添加外键有什么区别? 1)如果不添加外键:对于数据库来说,dept_id这个列就是一个普通的列,数据库也不会知道 dept 和 emp两张表存在任何关系,自然也不会帮我们去维护这层关系。 假如,现在要删除dept表中的某一个部门(4号部门),删除后就会造成emp表中的赵六和刘能找不到部门,而赵六和刘能后面对应的dept_id值为4, 这个数据也变成了冗余数据。 这样会破坏数据库中数据的完整性和一致性! 2)如果将dept_id添加为外键:将dept_id添加为外键就等同于:通知数据库 dept 和 emp 两张表存在对应关系, 而且emp表中的dept_id列要参考dept表中的id列。 这样数据库就会帮我们维护这两张表的对应关系,如果此时删除dept表中的某一个部门(4号部门),数据库会首先检查这个4号部门在emp表中还有没有对应的员工,如果有,数据库就会阻止我们删除!这样就保证了数据的完整性和一致性。 如果非要删除,可以将4号部门里面的员工记录转移到其他部门或者删除,只要保证所删除部门中没有对应的员工即可! 补充内容4: 表关系 1对多(多对1): 在这种关系中,往往会在多的一方添加列,保存一的一方的主键(可以设置外键,当然也可以不设置,看需求),比如: 部门表(1) 员工表(*), 在员工表(*)中添加列(dept_id)保存部门的编号 班级表(1) 学生表(*), 在学生表(*)中添加列(class_id)保存班级的编号 1对1: 在这种关系中,可以在任意一方添加列保存另一方的主键(可以设置外键,当然也可以不设置,看需求), 比如: 班级表(1) 教室表(1), 在班级表(1)添加列(room_id)来保存教室的编号 班级表(1) 教室表(1), 在教室表(1)添加列(class_id)来保存班级的编号 多对多: 在这种关系中,在任何一方添加列保存另一方的主键都不合适 此时可以再创建一张表,在这种表中分别添加两个列(stuid,teaid), 分别用于保存学生表的主键和教师表的主键,以此来保存学生和教师的对应关系! ---------------------------------- 4.多表查询 4.1.连接查询 -- 准备数据: 以下练习将使用db30库中的表及表记录,请先进入db30数据库!!! ---------------------------------- -- 42.查询部门和部门对应的员工信息 select * from dept,emp; 以上查询有一个名字叫做笛卡尔积查询 笛卡尔积查询: 其实就是同时查询两张表,其中一张表有m条记录,另外一张表有n条记录,查询的结果是m*n条,但这种查询结果中包含大量错误的数据,所以我们一般不会直接使用这种查询。 在笛卡尔积查询的基础上,通过where子句将错误的数据剔除,只保留正确的数据,这就是连接查询。 select * from dept,emp where emp.dept_id=dept.id; 内连接查询: select * from dept inner join emp on emp.dept_id=dept.id; ---------------------------------- 4.2.连接查询 ---------------------------------- -- 43.查询【所有部门】及部门对应的员工,如果某个部门下没有员工,员工显示为null 如果两张表在连接查询时,要求查询出其中一张表的所有数据,此时可以使用左外连接查询或者右外连接查询。 select * from dept left join emp on emp.dept_id=dept.id; -- 如果要查询部门表(dept)中的所有数据,而部门表(dept)在左边,此时可以使用左外连接查询,就可以查询出所有的部门信息(而员工信息只显示和部门对应的) 【左外连接查询】:可以将左边表中的所有记录都查询出来,右边表只显示和左边相对应的数据,如果左边表中某些记录在右边没有对应的数据,右边显示为null即可。 -- 44.查询【所有员工】及员工所属部门,如果某个员工没有所属部门,部门显示为null select * from dept right join emp on emp.dept_id=dept.id; 【右外连接查询】:可以将右边表中的所有记录都查询出来,左边表只显示和右边相对应的数据,如果右边表中某些记录在左边没有对应的数据,可以显示为null。 -- 查询所有部门以及所有员工, 如果部门没有对应员工,可以对应null,如果员工没有对应部门,也可以对应null,这种情况应使用全外连接查询。 -- 但,mysql不支持全外连接查询!但可以通过union来模拟这种查询! union关键字是用于将两个查询结果上下合并在一起显示,并且会去除重复记录。 union all关键字是用于将两个查询结果上下合并在一起显示,不会去除重复记录 能够使用union和union all合并结果的查询语句,必须符合: 1)两条SQL语句查询的结果列数必须相同 2)两条SQL语句查询的结果列名必须相同(低版本mysql要求) select * from dept left join emp on emp.dept_id=dept.id union select * from dept right join emp on emp.dept_id=dept.id; select * from dept left join emp on emp.dept_id=dept.id union all select * from dept right join emp on emp.dept_id=dept.id; ---------------------------------- 4.3.子查询练习 -- 准备数据:以下练习将使用db40库中的表及表记录,请先进入db40数据库!!! ---------------------------------- -- 45.列出薪资比'王海涛'的薪资高的所有员工,显示姓名、薪资 -- 求出'王海涛'的薪资是多少 select sal from emp where name='王海涛';#2450 -- 求出薪资比'王海涛'的薪资还高的所有员工 select name, sal from emp where sal>(select sal from emp where name='王海涛'); -- 将一个SQL语句的执行结果作为另外一条SQL语句的条件来执行, 这就是子查询! -- 46.列出与'刘沛霞'从事相同职位的所有员工,显示姓名、职位 -- 求出'刘沛霞'从事的职位 select job from emp where name='刘沛霞'; -- 推销员 -- 求出和'刘沛霞'具有相同职位的员工 select name, job from emp where job=(select job from emp where name='刘沛霞'); ---------------------------------- 4.4.多表查询练习 ---------------------------------- -- 47.列出在'培优部'任职的员工,假定不知道'培优部'的部门编号,显示部门名称,员工名称。 /* 查询的列:select dept.name, emp.name 查询的表:from dept, emp 筛选条件:where emp.dept_id=dept.id and dept.name='培优部' */ SELECT dept.name, emp.name FROM dept, emp WHERE emp.dept_id=dept.id AND dept.name='培优部'; ---------------------------------- SELECT dept.name, emp.name FROM dept INNER JOIN emp ON emp.dept_id=dept.id WHERE dept.name='培优部'; -- 48.(自查询)列出所有员工及其直接上级,显示员工姓名、上级编号,上级姓名 /* emp e1(员工表) emp e2(上级表) 查询的列: SELECT e1.name, e1.topid, e2.name 查询的表: FROM emp e1, emp e2 筛选条件: WHERE e1.topid=e2.id */ SELECT e1.name, e1.topid, e2.name FROM emp e1, emp e2 WHERE e1.topid=e2.id; -- 49.列出最低薪资大于1500的各种职位,显示职位和该职位的最低薪资 /* 列出职位: 1)求出各种职位的最低薪资 先按照职位进行分组,职位相同为一组,再用min(sal)求每组中的最低薪资,也就是每种职位的最低薪资 select job, min(sal) from emp group by job; 2)求出有哪些职位的最低薪资是大于1500的 select job, min(sal) from emp group by job where min(sal)>1500;-- 错误写法! 1)where应在放在from子句后, group by子句前 2)where中不能使用多行函数(列别名也不能用在where中) 3)where是在分组之前之前,先过滤掉一些记录,再基于剩余的记录进行分组, 而本地是先分组,再过滤,所以不能使用where,应该用having */ select job, min(sal) from emp group by job having min(sal)>1500; ---------------------------------- where和having的区别? 1)where和having都是用于对表中的记录进行筛选过滤 2)where用于在分组之前对记录进行筛选过滤,而having用于对分组之后的记录进行筛选过滤 3)where子句中不能使用多行函数 和 列别名,但可以使用表别名! select name as 姓名, sal as 薪资 from emp e; -- 其中上面的'姓名','薪资'都是列别名, e是表别名 4)having子句中可以使用多行函数 和 列别名 以及 表别名! ---------------------------------- -- 50.列出在每个部门就职的员工数量、平均工资。显示部门编号、员工数量,平均薪资。 select dept_id, count(*), avg(sal) from emp group by dept_id; select dept_id 部门编号, count(*) 员工数量, avg(sal) 平均薪资 from emp group by dept_id; -- 51.列出受雇日期早于直接上级的所有员工,显示员工编号、员工姓名、部门名称、上级编号、上级姓名。 /* emp e1(员工表), emp e2(上级表) 查询的列:SELECT e1.id, e1.name, d.name, e2.id, e2.name 查询的表:FROM emp e1, emp e2, dept d 连接条件:WHERE e1.topid=e2.id AND e1.dept_id=d.id 筛选条件: AND e1.hdate < e2.hdate */ SELECT e1.id, e1.name, d.name, e2.id, e2.name FROM emp e1, emp e2, dept d WHERE e1.topid=e2.id AND e1.dept_id=d.id AND e1.hdate < e2.hdate; ---------------------------------- 补充内容5: set names gbk; set names gbk;(这个命令是用在cmd窗口中)用来通知数据库服务器, 当前cmd窗口发送给服务器的数据的GBK的,那么服务器就会按照GBK编码来接收 cmd窗口发送过来的数据, 再将GBK的数据转换成utf8编码的数据存入数据库中! 这个命令只能用在cmd窗口! 而且是每次新开一个cmd窗口都需要重新设置一次! 像Navicat/SQLYog等工具不需要设置该命令,因为这些工具底层已经设置过编码了! 补充内容6: 数据库的备份和恢复 6.1.备份数据库 1、备份单个数据库 在cmd窗口(未登录、未连接到mysql服务器的界面)中,可以通过如下命令对指定的数据库进行备份: mysqldump -u用户名 -p密码 库名 > 备份文件的位置 示例1: 对db40库中的数据(表,表记录)进行备份,备份到 d:/db40.sql 文件中 mysqldump -uroot -proot db40 > d:/db40.sql 注意: 1)如果输入命令回车之后没有提示错误,就说明备份成功了! 2)备份单个数据库,其实只会备份这个库中的表和表记录,并不会备份库本身! 2、备份多个数据库(比如备份两个数据库) 在cmd窗口(未登录、未连接到mysql服务器的界面)中 mysqldump -u用户名 -p密码 --databases 库名1 库名2 ... > 备份文件的位置 示例2: 对db20 和 db40 库中的数据进行备份,备份到 d:/db2040.sql 文件中 mysqldump -uroot -proot --databases db20 db40 > d:/db2040.sql 注意: 1)如果输入命令回车之后没有提示错误,就说明备份成功了! 2)备份多个数据库,不仅会备份这个库中的表和表记录,同时会备份库本身! 或者如果想备份mysql服务器中的所有的库以及库中的表和表记录,可以通过如下命令: mysqldump -uroot -proot -A > d:/dball.sql 输入完后回车如果没有提示错误(error是错误,警告不是错误可以忽略),即表示备份成功! 6.2.恢复数据库 1、恢复数据库方式一(单个数据库): 在cmd窗口中(未登录的状态下),可以通过如下命令对指定的数据库进行恢复 mysql -u用户名 -p密码 库名 < 备份文件的位置 示例1: 将 d:/db40.sql 文件中的数据恢复到 db60 库中 1) 先在cmd窗口中(已登录的状态下), 先创建db60库 create database db60 charset utf8; 2) 再回到cmd窗口中(未登录的状态下), 执行下面恢复的命令 mysql -uroot -proot db60 < d:/db40.sql 2、恢复数据库方式二(多个数据库) 在cmd窗口中(已登录的状态下),可以通过source命令来执行指定位置的sql文件中的sql语句: source sql文件的位置 示例2: 将 d:/db40.sql 文件中的数据恢复到 db80 库中 1) 先创建db80库, 并进入到db80库 create database db80 charset utf8; use db80; 2) 再通过source命令执行 d:/db40.sql 文件中的sql语句 source d:/db40.sql 示例3: 将 d:/db2040.sql 文件中的数据恢复回来 1) 将db20,db40库删除(模拟数据丢失) drop database db20; drop database db40; 2) 再通过source命令执行 d:/db2040.sql 文件中的sql语句 source d:/db2040.sql 补充内容7: navicat软件的使用 1)下发的navicat软件是绿色解压版,解压之后进入到目录,找到navicat.exe双击即可启动程序 2)点击左上角的连接图标,输入连接名(就是个自定义的名字),输入主机或ip地址,以及端口,再输入用户名和密码,点击左下角的连接测试,如果连接成功,点击确定即可! 3)使用navicat进行如下操作: 数据库操作: 创建库, 修改库, 删除库 表操作: 创建表, 修改表, 删除表 表记录操作: 新增表记录, 修改表记录, 删除表记录, 查询表记录 在navict中执行单条sql语句,或者执行多条/所有sql语句