MySql的前戏
在学习Mysql以前,咱们先来想一下一开始作的登陆注册案例,当时咱们把用户的信息保存到一个文件中:css
#用户名 |密码
root|123321
alex|123123
上面文件内容的规则是我本身定义的,你要想用我这个程序,必须按照个人规则去执行,但凡不是这个规则,就没有交流的余地。html
在一开始的时候文件格式的规定是没有规范的,后面学到模块的时候逐渐知道了目录规则,咱们会把文件放到db目录下。node
相似下面目录结构:python

对于上面db目录中,是能够存放多个文件的,而且文件中能够有多行数据。mysql
那么问你们一个问题,若是说上面那个软件是我写好的,在一台服务器或者一台机器上安装了我写好的软件。redis
如今有张三和李四两我的都安装了我这个软件,张三注册了一个用户,李四也注册了一个用户,那么本身注册的用户应该是本身用的。那么这两个用户是不共享的,算法
因此这个软件证实我写的不太好,它不能共享,这种相似单机的软件是很差的。sql
而后呢,我如今将我写的这个软件中目录修改一下,将db目录移除。如今将单机程序(本身的DB)改变成也是单机程序(公用DB)。mongodb
那么以前给你们介绍过,凡是交互操做,是离不开scoket的,那么如今我这边有个公用DB和一个scoket服务端,而后在张三和李四那边有scoket客户端。张三和李四此时输入用户名和密码,如今我这边scoket服务端某个目录接收用户输入的用户名和密码,看一下该用户名存在不存在,若是存在返回true,若是不存在返回false。也就是说重点的内容咱们把db目录放到某台服务器上。数据库
那么上面这个例子呢,其实就给你们慢慢的引出来mysql了,mysql呢其实就一个软件,这个软件安装到某台电脑上或者某台服务器上,那么如今只要我告诉它建立一个文件目录或者建立文件,那么mysql就自动帮咱们建立了,好比说咱们再在文件中添加一条数据,那么它也帮咱们完成。
总结一句话:其实mysql就是用于管理咱们的文件的一个软件。
那么对于mysql这个软件来讲有两个软件
---服务器软件
- socket服务端
- 本地文件操做
- 解析指令(mysql语句)
---客户端软件
- socket客户端
- 发送指令
- 解析指令(mysql语句)
解释:
对于服务器软件中的socket服务器是一直开着,客户端得须要链接,而且还有建立文件、删除文件等等的操做
对于客户端软件中的scoket客户端,咱们得须要发送指令去命令scoket服务端对文件进行操做。
你们应该知道ftp的项目,上传项目和下载项目的命令是不同的。那么对于mysql中的客户端和服务端也会有些指令的操做,那么在这两个端中应该是有解析指令的过程,这个指令只有mysql这个软件知道,这种指令就是mysql语句。
再想一想咱们写的html、css、js.这些比如就是咱们客户端写好的语法,而后浏览器充当了服务端的角色去解析咱们的的语法,最后来渲染出来结果。
接下来咱们要学习这些技能:
- 安装 服务端和客户端
- 链接
- 学习SOL语句规则:命令服务端作任何的操做
什么是数据(Data)
描述事物的符号记录称为数据,描述事物的符号既能够是数字,也能够是文字、图片,图像、声音、语言等,数据由多种表现形式,它们均可以通过数字化后存入计算机
在计算机中描述一个事物,就须要抽取这一事物的典型特征,组成一条记录,就至关于文件里的一行内容,如:
1 xiaomage,male,18,1999,山东,计算机系,2017,oldboy
单纯的一条记录并无任何意义,若是咱们按逗号做为分隔,依次定义各个字段的意思,至关于定义表的标题

这样经过表格咱们很清楚的知道了某人的详细信息。
什么是数据库(DataBase,简称DB)
数据库即存放数据的仓库,只不过这个仓库是在计算机存储设备上,并且数据是按必定的格式存放的
过去人们将数据存放在文件柜里,如今数据量庞大,已经再也不适用
数据库是长期存放在计算机内、有组织、可共享的数据便可。
数据库中的数据按必定的数据模型组织、描述和储存,具备较小的冗余度、较高的数据独立性和易扩展性,并可为各类 用户共享
什么是数据库管理系统(DataBase Management System)
在了解了Data与DB的概念后,如何科学地组织和存储数据,如何高效获取和维护数据成了关键
这就用到了一个系统软件---数据库管理系统
如MySQL、Oracle、SQLite、Access、MS SQL Server
mysql主要用于大型门户,例如搜狗、新浪等,它主要的优点就是开放源代码,由于开放源代码这个数据库是免费的,他如今是甲骨文公司的产品。
oracle主要用于银行、铁路、飞机场等。该数据库功能强大,软件费用高。也是甲骨文公司的产品。
sql server是微软公司的产品,主要应用于大中型企业,如联想、方正等。
数据库服务器、数据管理系统、数据库、表与记录的关系(重点理解!!!)
记录:1 xxx 324245234 22(多个字段的信息组成一条记录,即文件中的一行内容)
表:student,scholl,class_list(即文件)
数据库:oldboy_stu(即文件夹)
数据库管理系统:如mysql(是一个软件)
数据库服务器:一台计算机(对内存要求比较高)
总结:
数据库服务器-:运行数据库管理软件
数据库管理软件:管理-数据库
数据库:即文件夹,用来组织文件/表
表:即文件,用来存放多行内容/多条记录
本节掌握内容:
-
mysql的安装、启动
-
mysql破解密码
-
统一字符编码
MySQL是一个关系型数据库管理系统,由瑞典MySQL AB 公司开发,目前属于 Oracle 旗下公司。MySQL 最流行的关系型数据库管理系统,在 WEB 应用方面MySQL是最好的 RDBMS (Relational Database Management System,关系数据库管理系统) 应用软件之一。
mysql是什么?
mysql就是一个基于socket编写的C/S架构的软件
数据库管理软件分类
分两大类:
关系型:如sqllite,db2,oracle,access,sql server,MySQL,注意:sql语句通用
非关系型:mongodb,redis,memcache
能够简单的理解为:
关系型数据库须要有表结构
非关系型数据库是key-value存储的,没有表结构
mysql的下载安装
想要使用MySQL来存储并保存数据,则须要作几件事情:
a. 安装MySQL服务端
b. 安装MySQL客户端
b. 【客户端】链接【服务端】
c. 【客户端】发送命令给【服务端MySQL】服务的接受命令并执行相应操做(增删改查等)
下载地址:
window版本
一、官网去下载

二、针对操做系统的不一样下载不一样的版本

3.解压
若是想要让MySQL安装在指定目录,那么就将解压后的文件夹移动到指定目录,如:C:\mysql-5.6.40-winx64
4.添加环境变量
【右键计算机】--》【属性】--》【高级系统设置】--》【高级】--》【环境变量】--》【在第二个内容框中找到 变量名为Path 的一行,双击】 --> 【将MySQL的bin目录路径追加到变值值中,用 ; 分割】
5.初始化
1
|
mysqld
-
-
initialize
-
insecure
|
6.启动mysql服务
7.启动mysql客户端并链接mysql服务端(新开一个cmd窗口)
mysql -u root -p # 链接MySQL服务器
上一步解决了一些问题,但不够完全,由于在执行【mysqd】启动MySQL服务器时,当前终端会被hang住,那么作一下设置便可解决此问题,即将MySQL服务制做成windows服务
注意:--install前,必须用mysql启动命令的绝对路径
# 制做MySQL的Windows服务,在终端执行此命令:
"c:\mysql-5.6.40-winx64\bin\mysqld" --install
# 移除MySQL的Windows服务,在终端执行此命令:
"c:\mysql-5.7.16-winx64\bin\mysqld" --remove
注册成服务以后,之后再启动和关闭MySQL服务时,仅需执行以下命令:
# 启动MySQL服务
net start mysql
# 关闭MySQL服务
net stop mysql
windows下登陆设置密码
打开终端,输入mysql

输入mysql提供的函数:select user(); # 查看当前登陆的帐号

当前登陆的默认帐号为ODBC@localhost
若是想切到root帐号登陆
执行命令:
再查看当前用户:

管理员为root(拥有最高权限,管理员帐号),密码为空,以无密码的方式登陆了管理员帐号,是很是危险的一件事情,因此要为管理员帐号设置密码
设置管理员root帐号密码为123
C:\Users\mjj>mysqladmin -uroot -p password "123" #设置初始密码 因为原密码为空,所以-p能够不用

ps:⚠️不用管它。翻译为:在命令行界面使用密码不安全,暴露在终端当中。
而后再登陆帐号,不输入密码则会出现以下结果:

再次执行以下操做:

再查看一下当前登陆的帐号:

若是想将原始密码123,设置新密码为456
C:\Users\mjj>mysqladmin -uroot -p"123" password "456" #修改mysql密码,由于已经有密码了,因此必须输入原密码才能设置新密码
依次执行以下操做:
mysql -uroot -p456
select user();
密码忘记——破解密码
跳过受权方式,直接登陆!!
0.以管理员身份打开cmd

2.停掉mysql服务端
C:\WINDOWS\system32>net stop mysql
MySQL 服务正在中止.
MySQL 服务已成功中止。
3.执行以下命令跳过受权表
#跳过受权表
C:\WINDOWS\system32>mysqld --skip-grant-tables
2018-06-09 17:12:38 0 [Warning] Insecure configuration for --secure-file-priv: Current value does not restrict location of generated files. Consider setting it to a valid, non-empty path.
2018-06-09 17:12:38 0 [Note] mysqld (mysqld 5.6.40) starting as process 6052 ...4.
4.再次查看

5. 如今能够任意的更改密码,执行以下命令
update mysql.user set authentication_string =password('') where User='root';
6.刷新权限,执行命令

7.退出mysql。执行命令:exit,
8.让用户去加载权限,以管理员身份进入cmd,查看当前mysql进程

9.杀死当前的进程,执行以下命令
taskkill /F /PID 6052 # 杀死当前的进程pid

10.再次执行以下操做,还原

统一字符编码
进入mysql客户端,执行\s

为了统一字符编码,请执行以下操做:
(1)my.ini文件是mysql的配置文件,
在C:\mysql-5.6.40-winx64文件下建立my.ini文件
(2)将以下代码拷贝保存。
#mysql5.5以上:修改方式为
[mysqld]
character-set-server=utf8
collation-server=utf8_general_ci
[client]
default-character-set=utf8
[mysql]
default-character-set=utf8
(3)以管理员身份重启服务,执行以下命令
C:\Windows\system32>net stop MySQL
MySQL 服务正在中止..
MySQL 服务已成功中止。
C:\Windows\system32>net start MySQL
MySQL 服务正在启动 .
MySQL 服务已经启动成功。
(4)在cmd中输入mysql进入mysql环境,执行\s,显示以下信息,表示成功

本节课先对mysql的基本语法初体验。
操做文件夹(库)
增
create database db1 charset utf8;
查
# 查看当前建立的数据库
show create database db1;
# 查看全部的数据库
show databases;
改
alter database db1 charset gbk;
删
操做文件(表)
use db1; #切换文件夹
select database(); #查看当前所在文件夹
增
create table t1(id int,name char);
查
#查看当前的这张t1表
show create table t1;
# 查看全部的表
show tables;
# 查看表的详细信息
desc t1;
改
# modify修改的意思
alter table t1 modify name char(6);
# 改变name为大写的NAME
alter table t1 change name NAMA char(7);
删
操做文件内容(记录)
增
# 插入一条数据,规定id,name数据leilei
insert t1(id,name) values(1,"mjj01"),(2,"mjj02"),(3,"mjj03");
查
select id from db1.t1;
select id,name from db1.t1;
select * from db1.t1;
改
update db1.t1 set name='zhangsan';
update db1.t1 set name='alex' where id=2;
删
delete from t1;
delete from t1 where id=2;
本节重点:
1、系统数据库
执行以下命令,查看系统库
nformation_schema: 虚拟库,不占用磁盘空间,存储的是数据库启动后的一些参数,如用户表信息、列信息、权限信息、字符信息等
performance_schema: MySQL 5.5开始新增一个数据库:主要用于收集数据库服务器性能参数,记录处理查询请求时发生的各类事件、锁等现象
mysql: 受权库,主要存储系统用户的权限信息
test: MySQL数据库系统自动建立的测试数据库
2、建立数据库
一、求救语法:
二、建立数据库语法
CREATE DATABASE 数据库名 charset utf8;
三、数据库命名规则:
能够由字母、数字、下划线、@、#、$
区分大小写
惟一性
不能使用关键字如 create select
不能单独使用数字
最长128位
# 基本上跟python或者js的命名规则同样
3、数据库相关操做
#查看数据库
show databases;
#查看当前库
show create database db1;
#查看所在的库
select database();
#选择数据库
use 数据库名
#删除数据库
DROP DATABASE 数据库名;
# 修改数据库
alter database db1 charset utf8;
4、了解内容
SQL语言主要用于存取数据、查询数据、更新数据和管理关系数据库系统,SQL语言由IBM开发。SQL语言分为3种类型:
1、DDL语句 数据库定义语言: 数据库、表、视图、索引、存储过程,例如CREATE DROP ALTER
2、DML语句 数据库操纵语言: 插入数据INSERT、删除数据DELETE、更新数据UPDATE、查询数据SELECT
3、DCL语句 数据库控制语言: 例如控制用户的访问权限GRANT、REVOKE
本节掌握
1、存储引擎(了解)
前几节咱们知道mysql中创建的库===》文件夹,库中的表====》文件
现实生活中咱们用来存储数据的文件有不一样的类型,每种文件类型对应各自不一样的处理机制:好比处理文本用txt类型,处理表格用excel,处理图片用png等
数据库中的表也应该有不一样的类型,表的类型不一样,会对应mysql不一样的存取机制,表类型又称为存储引擎。
ps: 存储引擎说白了就是如何存储数据、如何为存储的数据创建索引和如何更新、查询数据等技术的实现方法。由于在关系数据库中数据的存储是以表的形式存储的,因此存储引擎也能够称为表类型(即存储和操做此表的类型)
在Oracle 和SQL Server等数据库中只有一种存储引擎,全部数据存储管理机制都是同样的。而MySql
数据库提供了多种存储引擎。用户能够根据不一样的需求为数据表选择不一样的存储引擎,用户也能够根据
本身的须要编写本身的存储引擎

SQL 解析器、SQL 优化器、缓冲池、存储引擎等组件在每一个数据库中都存在,但不是每 个数据库都有这么多存储引擎。MySQL 的插件式存储引擎可让存储引擎层的开发人员设 计他们但愿的存储层,例如,有的应用须要知足事务的要求,有的应用则不须要对事务有这 么强的要求 ;有的但愿数据能持久存储,有的只但愿放在内存中,临时并快速地提供对数据 的查询。
2、mysql支持的存储引擎
mysql> show engines\G;# 查看全部支持的引擎
mysql> show variables like 'storage_engine%'; # 查看正在使用的存储引擎
一、InnoDB 存储引擎
支持事务,其设计目标主要面向联机事务处理(OLTP)的应用。其
特色是行锁设计、支持外键,并支持相似 Oracle 的非锁定读,即默认读取操做不会产生锁。 从 MySQL 5.5.8 版本开始是默认的存储引擎。
InnoDB 存储引擎将数据放在一个逻辑的表空间中,这个表空间就像黑盒同样由 InnoDB 存储引擎自身来管理。从 MySQL 4.1(包括 4.1)版本开始,能够将每一个 InnoDB 存储引擎的 表单独存放到一个独立的 ibd 文件中。此外,InnoDB 存储引擎支持将裸设备(row disk)用 于创建其表空间。
InnoDB 经过使用多版本并发控制(MVCC)来得到高并发性,而且实现了 SQL 标准 的 4 种隔离级别,默认为 REPEATABLE 级别,同时使用一种称为 netx-key locking 的策略来 避免幻读(phantom)现象的产生。除此以外,InnoDB 存储引擎还提供了插入缓冲(insert buffer)、二次写(double write)、自适应哈希索引(adaptive hash index)、预读(read ahead) 等高性能和高可用的功能。
对于表中数据的存储,InnoDB 存储引擎采用了汇集(clustered)的方式,每张表都是按 主键的顺序进行存储的,若是没有显式地在表定义时指定主键,InnoDB 存储引擎会为每一 行生成一个 6 字节的 ROWID,并以此做为主键。
InnoDB 存储引擎是 MySQL 数据库最为经常使用的一种引擎,Facebook、Google、Yahoo 等 公司的成功应用已经证实了 InnoDB 存储引擎具有高可用性、高性能以及高可扩展性。对其 底层实现的掌握和理解也须要时间和技术的积累。若是想深刻了解 InnoDB 存储引擎的工做 原理、实现和应用,能够参考《MySQL 技术内幕:InnoDB 存储引擎》一书。
二、MyISAM 存储引擎
不支持事务、表锁设计、支持全文索引,主要面向一些 OLAP 数 据库应用,在 MySQL 5.5.8 版本以前是默认的存储引擎(除 Windows 版本外)。数据库系统 与文件系统一个很大的不一样在于对事务的支持,MyISAM 存储引擎是不支持事务的。究其根 本,这也并不难理解。用户在全部的应用中是否都须要事务呢?在数据仓库中,若是没有 ETL 这些操做,只是简单地经过报表查询还须要事务的支持吗?此外,MyISAM 存储引擎的 另外一个不同凡响的地方是,它的缓冲池只缓存(cache)索引文件,而不缓存数据文件,这与 大多数的数据库都不相同。
三、NDB 存储引擎
年,MySQL AB 公司从 Sony Ericsson 公司收购了 NDB 存储引擎。 NDB 存储引擎是一个集群存储引擎,相似于 Oracle 的 RAC 集群,不过与 Oracle RAC 的 share everything 结构不一样的是,其结构是 share nothing 的集群架构,所以能提供更高级别的 高可用性。NDB 存储引擎的特色是数据所有放在内存中(从 5.1 版本开始,能够将非索引数 据放在磁盘上),所以主键查找(primary key lookups)的速度极快,而且可以在线添加 NDB 数据存储节点(data node)以便线性地提升数据库性能。因而可知,NDB 存储引擎是高可用、 高性能、高可扩展性的数据库集群系统,其面向的也是 OLTP 的数据库应用类型。
四、Memory 存储引擎
正如其名,Memory 存储引擎中的数据都存放在内存中,数据库重 启或发生崩溃,表中的数据都将消失。它很是适合于存储 OLTP 数据库应用中临时数据的临时表,也能够做为 OLAP 数据库应用中数据仓库的维度表。Memory 存储引擎默认使用哈希 索引,而不是一般熟悉的 B+ 树索引。
五、Infobright 存储引擎
第三方的存储引擎。其特色是存储是按照列而非行的,所以很是 适合 OLAP 的数据库应用。其官方网站是 http://www.infobright.org/,上面有很多成功的数据 仓库案例可供分析。
六、NTSE 存储引擎
网易公司开发的面向其内部使用的存储引擎。目前的版本不支持事务, 但提供压缩、行级缓存等特性,不久的未来会实现面向内存的事务支持。
七、BLACKHOLE
黑洞存储引擎,能够应用于主备复制中的分发主库。
MySQL 数据库还有不少其余存储引擎,上述只是列举了最为经常使用的一些引擎。若是 你喜欢,彻底能够编写专属于本身的引擎,这就是开源赋予咱们的能力,也是开源的魅 力所在。
指定表类型/存储引擎
create table t1(id int)engine=innodb;# 默认不写就是innodb
小练习:
建立四张表,分别使用innodb,myisam,memory,blackhole存储引擎,进行插入数据测试
create table t1(id int)engine=innodb;
create table t2(id int)engine=myisam;
create table t3(id int)engine=memory;
create table t4(id int)engine=blackhole;
查看data文件下db1数据库中的文件:

#.frm是存储数据表的框架结构
# .ibd是mysql数据文件
#.MYD是MyISAM表的数据文件的扩展名
#.MYI是MyISAM表的索引的扩展名
#发现后两种存储引擎只有表结构,无数据
#memory,在重启mysql或者重启机器后,表内数据清空
#blackhole,往表内插入任何数据,都至关于丢入黑洞,表内永远不存记录
3、表介绍
表至关于文件,表中的一条记录就至关于文件的一行内容,不一样的是,表中的一条记录有对应的标题,称为表的字段

id,name,sex,age,birth称为字段,其他的,一行内容称为一条记录
4、建立表
语法:
create table 表名(
字段名1 类型[(宽度) 约束条件],
字段名2 类型[(宽度) 约束条件],
字段名3 类型[(宽度) 约束条件]
);
#注意:
1. 在同一张表中,字段名是不能相同
2. 宽度和约束条件可选
3. 字段名和类型是必须的
1.建立数据库
create database db2 charset utf8;
2.使用数据库
3.建立a1表
create table a1(
id int,
name varchar(50),
age int(3)
);
4.插入表的记录
insert into a1 values
(1,'mjj',18),
(2,'wusir',28);
ps:以;做为mysql的结束语
5.查询表的数据和结构
(1)查询a1表中的存储数据
mysql> select * from a1;
+------+-------+------+
| id | name | age |
+------+-------+------+
| 1 | mjj | 18 |
| 2 | wusir | 28 |
+------+-------+------+
2 rows in set (0.02 sec)
mysql>
(2)查看a1表的结构
mysql> desc a1;
+-------+-------------+------+-----+---------+-------+
| Field | Type | Null | Key | Default | Extra |
+-------+-------------+------+-----+---------+-------+
| id | int(11) | YES | | NULL | |
| name | varchar(50) | YES | | NULL | |
| age | int(3) | YES | | NULL | |
+-------+-------------+------+-----+---------+-------+
3 rows in set (0.16 sec)
(3)查看表的详细结构
mysql> show create table a1\G;
*************************** 1. row ***************************
Table: a1
Create Table: CREATE TABLE `a1` (
`id` int(11) DEFAULT NULL,
`name` varchar(50) DEFAULT NULL,
`age` int(3) DEFAULT NULL
) ENGINE=InnoDB DEFAULT CHARSET=utf8
1 row in set (0.00 sec)
6.复制表
(1)新建立一个数据库db3
mysql> create database db3 charset utf8;
Query OK, 1 row affected (0.00 sec)
(2)使用db3
mysql> use db3;
Database changed
#这是上个建立的db2数据库中的a1表
mysql> select * from db2.a1;
+------+-------+------+
| id | name | age |
+------+-------+------+
| 1 | mjj | 18 |
| 2 | wusir | 28 |
+------+-------+------+
(3)复制db2.a1的表结构和记录
# 这就是复制表的操做(既复制了表结构,又复制了记录)
mysql> create table b1 select * from db2.a1;
Query OK, 2 rows affected (0.03 sec)
(4)查看db3.b1中的数据和表结构
#再去查看db3文件夹下的b1表发现 跟db2文件下的a1表数据同样
mysql> select * from db3.b1;
+------+-------+------+
| id | name | age |
+------+-------+------+
| 1 | mjj | 18 |
| 2 | wusir | 28 |
+------+-------+------+
2 rows in set (0.00 sec)
ps1:若是只要表结构,不要记录
#在db2数据库下新建立一个b2表,给一个where条件,条件要求不成立,条件为false,只拷贝表结构
mysql> create table b2 select * from db2.a1 where 1>5;
Query OK, 0 rows affected (0.05 sec)
Records: 0 Duplicates: 0 Warnings: 0
查看表结构:
# 查看表结构
mysql> desc b2;
+-------+-------------+------+-----+---------+-------+
| Field | Type | Null | Key | Default | Extra |
+-------+-------------+------+-----+---------+-------+
| id | int(11) | YES | | NULL | |
| name | varchar(50) | YES | | NULL | |
| age | int(3) | YES | | NULL | |
+-------+-------------+------+-----+---------+-------+
3 rows in set (0.02 sec)
#查看表结构中的数据,发现是空数据
mysql> select * from b2;
Empty set (0.00 sec)
ps2:还有一种作法,使用like(只拷贝表结构,不拷贝记录)
mysql> create table b3 like db2.a1;
Query OK, 0 rows affected (0.01 sec)
mysql> desc b3;
+-------+-------------+------+-----+---------+-------+
| Field | Type | Null | Key | Default | Extra |
+-------+-------------+------+-----+---------+-------+
| id | int(11) | YES | | NULL | |
| name | varchar(50) | YES | | NULL | |
| age | int(3) | YES | | NULL | |
+-------+-------------+------+-----+---------+-------+
3 rows in set (0.02 sec)
mysql> select * from db3.b3;
Empty set (0.00 sec)
7.删除表:
介绍
存储引擎决定了表的类型,而表内存放的数据也要有不一样的类型,每种数据类型都有本身的宽度,但宽度是可选的
详细参考连接:http://www.runoob.com/mysql/mysql-data-types.html
mysql经常使用数据类型归纳:
#1. 数字:
整型:tinyinit int bigint
小数:
float :在位数比较短的状况下不精准
double :在位数比较长的状况下不精准
0.000001230123123123
存成:0.000001230000
decimal:(若是用小数,则用推荐使用decimal)
精准
内部原理是以字符串形式去存
#2. 字符串:
char(10):简单粗暴,浪费空间,存取速度快
root存成root000000
varchar:精准,节省空间,存取速度慢
sql优化:建立表时,定长的类型往前放,变长的日后放
好比性别 好比地址或描述信息
>255个字符,超了就把文件路径存放到数据库中。
好比图片,视频等找一个文件服务器,数据库中只存路径或url。
#3. 时间类型:
最经常使用:datetime
#4. 枚举类型与集合类型
1、数值类型
整数类型:TINYINT SMALLINT MEDIUMINT INT BIGINT
做用:存储年龄,等级,id,各类号码等
========================================
tinyint[(m)] [unsigned] [zerofill]
小整数,数据类型用于保存一些范围的整数数值范围:
有符号:
-128 ~ 127
无符号:
~ 255
PS: MySQL中无布尔值,使用tinyint(1)构造。
========================================
int[(m)][unsigned][zerofill]
整数,数据类型用于保存一些范围的整数数值范围:
有符号:
-2147483648 ~ 2147483647
无符号:
~ 4294967295
========================================
bigint[(m)][unsigned][zerofill]
大整数,数据类型用于保存一些范围的整数数值范围:
有符号:
-9223372036854775808 ~ 9223372036854775807
无符号:
~ 18446744073709551615
验证1:有符号和无符号tinyint
============有符号tinyint==============
# 建立数据库db4
create database db4 charset utf8;
# 切换到当前db4数据库
mysql> use db4;
# 建立t1 规定x字段为tinyint数据类型(默认是有符号的)
mysql> create table t1(x tinyint);
# 验证,插入-1这个数
mysql> insert into t1 values(-1);
# 查询 表记录,查询成功(证实默认是有符号类型)
mysql> select * from t1;
+------+
| x |
+------+
| -1 |
+------+
#执行以下操做,会发现报错。由于有符号范围在(-128,127)
mysql> insert into t1 values(-129),(128);
ERROR 1264 (22003): Out of range value for column 'x' at row 1
============无符号tinyint==============
# 建立表时定义记录的字符为无符号类型(0,255) ,使用unsigned
mysql> create table t2(x tinyint unsigned);
# 报错,超出范围
mysql> insert into t2 values(-129);
ERROR 1264 (22003): Out of range value for column 'x' at row 1
# 插入成功
mysql> insert into t2 values(255);
Query OK, 1 row affected (0.00 sec)
验证2:int类型后面的存储是显示宽度,而不是存储宽度
mysql> create table t3(id int(1) unsigned);
#插入255555记录也是能够的
mysql> insert into t3 values(255555);
mysql> select * from t3;
+--------+
| id |
+--------+
| 255555 |
+--------+
ps:以上操做还不可以验证,再来一张表验证用zerofill 用0填充
# zerofill 用0填充
mysql> create table t4(id int(5) unsigned zerofill);
mysql> insert into t4 value(1);
Query OK, 1 row affected (0.00 sec)
#插入的记录是1,可是显示的宽度是00001
mysql> select * from t4;
+-------+
| id |
+-------+
| 00001 |
+-------+
1 row in set (0.00 sec)
注意:为该类型指定宽度时,仅仅只是指定查询结果的显示宽度,与存储范围无关,存储范围以下
其实咱们彻底不必为整数类型指定显示宽度,使用默认的就能够了
默认的显示宽度,都是在最大值的基础上加1

int的存储宽度是4个Bytes,即32个bit,即2**32
无符号最大值为:4294967296-1
有符号最大值:2147483648-1
有符号和无符号的最大数字须要的显示宽度均为10,而针对有符号的最小值则须要11位才能显示彻底,因此int类型默认的显示宽度为11是很是合理的
最后:整形类型,其实没有必要指定显示宽度,使用默认的就ok
2、浮点型
定点数类型: DEC等同于DECIMAL
浮点类型:FLOAT DOUBLE
做用:存储薪资、身高、体重、体质参数等
语法:
-------------------------FLOAT-------------------
FLOAT[(M,D)] [UNSIGNED] [ZEROFILL]
#参数解释:单精度浮点数(非准确小数值),M是全长,D是小数点后个数。M最大值为255,D最大值为30
#有符号:
-3.402823466E+38 to -1.175494351E-38,
1.175494351E-38 to 3.402823466E+38
#无符号:
1.175494351E-38 to 3.402823466E+38
#精确度:
**** 随着小数的增多,精度变得不许确 ****
-------------------------DOUBLE-----------------------
DOUBLE[(M,D)] [UNSIGNED] [ZEROFILL]
#参数解释: 双精度浮点数(非准确小数值),M是全长,D是小数点后个数。M最大值为255,D最大值为30
#有符号:
-1.7976931348623157E+308 to -2.2250738585072014E-308
2.2250738585072014E-308 to 1.7976931348623157E+308
#无符号:
2.2250738585072014E-308 to 1.7976931348623157E+308
#精确度:
****随着小数的增多,精度比float要高,但也会变得不许确 ****
======================================
--------------------DECIMAL------------------------
decimal[(m[,d])] [unsigned] [zerofill]
#参数解释:准确的小数值,M是整数部分总个数(负号不算),D是小数点后个数。 M最大值为65,D最大值为30。
#精确度:
**** 随着小数的增多,精度始终准确 ****
对于精确数值计算时须要用此类型
decaimal可以存储精确值的缘由在于其内部按照字符串存储。
验证三种类型建表:
#1验证FLOAT类型建表:
mysql> create table t5(x float(256,31));
ERROR 1425 (42000): Too big scale 31 specified for column 'x'. Maximum is 30.
mysql> create table t5(x float(256,30));
ERROR 1439 (42000): Display width out of range for column 'x' (max = 255)
mysql> create table t5(x float(255,30)); #建表成功
Query OK, 0 rows affected (0.03 sec)
#2验证DOUBLE类型建表:
mysql> create table t6(x double(255,30)); #建表成功
Query OK, 0 rows affected (0.03 sec)
#3验证deimal类型建表:
mysql> create table t7(x decimal(66,31));
ERROR 1425 (42000): Too big scale 31 specified for column 'x'. Maximum is 30.
mysql> create table t7(x decimal(66,30));
ERROR 1426 (42000): Too big precision 66 specified for column 'x'. Maximum is 65.
mysql> create table t7(x decimal(65,30)); #建表成功
Query OK, 0 rows affected (0.00 sec)
验证三种类型的精度:
# 分别对三张表插入相应的记录
mysql> insert into t5 values(1.1111111111111111111111111111111);#小数点后31个1
Query OK, 1 row affected (0.01 sec)
mysql> insert into t6 values(1.1111111111111111111111111111111);
Query OK, 1 row affected (0.01 sec)
mysql> insert into t7 values(1.1111111111111111111111111111111);
Query OK, 1 row affected, 1 warning (0.00 sec)
# 查询结果
mysql> select * from t5; #随着小数的增多,精度开始不许确
+----------------------------------+
| x |
+----------------------------------+
| 1.111111164093017600000000000000 |
+----------------------------------+
1 row in set (0.00 sec)
mysql> select * from t6; #精度比float要准确点,但随着小数的增多,一样变得不许确
+----------------------------------+
| x |
+----------------------------------+
| 1.111111111111111200000000000000 |
+----------------------------------+
1 row in set (0.00 sec)
mysql> select * from t7; #精度始终准确,d为30,因而只留了30位小数
+----------------------------------+
| x |
+----------------------------------+
| 1.111111111111111111111111111111 |
+----------------------------------+
1 row in set (0.00 sec)
3、日期类型
DATE TIME DATETIME TIMESTAMP YEAR
做用:存储用户注册时间,文章发布时间,员工入职时间,出生时间,过时时间等
语法:
YEAR
YYYY(1901/2155)
DATE
YYYY-MM-DD(1000-01-01/9999-12-31)
TIME
HH:MM:SS('-838:59:59'/'838:59:59')
DATETIME
YYYY-MM-DD HH:MM:SS(1000-01-01 00:00:00/9999-12-31 23:59:59 Y)
TIMESTAMP
YYYYMMDD HHMMSS(1970-01-01 00:00:00/2037 年某时)
验证:
一、year
mysql> create table t8(born_year year);#不管year指定何种宽度,最后都默认是year(4)
Query OK, 0 rows affected (0.03 sec)
#插入失败,超出范围(1901/2155)
mysql> insert into t8 values
-> (1900),
-> (1901),
-> (2155),
-> (2156);
ERROR 1264 (22003): Out of range value for column 'born_year' at row 1
mysql> select * from t8;
Empty set (0.01 sec)
mysql> insert into t8 values
-> (1905),
-> (2018);
Query OK, 2 rows affected (0.00 sec) #插入记录成功
Records: 2 Duplicates: 0 Warnings: 0
mysql> select * from t8;
+-----------+
| born_year |
+-----------+
| 1905 |
| 2018 |
+-----------+
2 rows in set (0.00 sec)
二、date、year、datetime
#建立t9表
mysql> create table t9(d date,t time,dt datetime);
Query OK, 0 rows affected (0.06 sec)
#查看表的结构
mysql> desc t9;
+-------+----------+------+-----+---------+-------+
| Field | Type | Null | Key | Default | Extra |
+-------+----------+------+-----+---------+-------+
| d | date | YES | | NULL | |
| t | time | YES | | NULL | |
| dt | datetime | YES | | NULL | |
+-------+----------+------+-----+---------+-------+
3 rows in set (0.14 sec)
# 调用mysql自带的now()函数,获取当前类型指定的时间 以下结构
mysql> insert into t9 values(now(),now(),now());
Query OK, 1 row affected, 1 warning (0.01 sec)
mysql> select * from t9;
+------------+----------+---------------------+
| d | t | dt |
+------------+----------+---------------------+
| 2018-06-09 | 09:35:20 | 2018-06-09 09:35:20 |
+------------+----------+---------------------+
1 row in set (0.00 sec)
三、timestamp(了解便可)
mysql> create table t10(time timestamp);
Query OK, 0 rows affected (0.06 sec)
mysql> insert into t10 values();
Query OK, 1 row affected (0.00 sec)
mysql> insert into t10 values(null);
Query OK, 1 row affected (0.00 sec)
mysql> select * from t10;
+------+
| time |
+------+
| NULL |
| NULL |
+------+
mysql> insert into t10 values(now());
Query OK, 1 row affected (0.01 sec)
mysql> select * from t10;
+---------------------+
| time |
+---------------------+
| 2018-06-09 09:44:48 |
+---------------------+
1 row in set (0.01 sec)
datetime与timestamp的区别
在实际应用的不少场景中,MySQL的这两种日期类型都可以知足咱们的须要,存储精度都为秒,但在某些状况下,会展示出他们各自的优劣。
下面就来总结一下两种日期类型的区别。
1.DATETIME的日期范围是1001——9999年,TIMESTAMP的时间范围是1970——2038年。
2.DATETIME存储时间与时区无关,TIMESTAMP存储时间与时区有关,显示的值也依赖于时区。在mysql服务器,
操做系统以及客户端链接都有时区的设置。
3.DATETIME使用8字节的存储空间,TIMESTAMP的存储空间为4字节。所以,TIMESTAMP比DATETIME的空间利用率更高。
4.DATETIME的默认值为null;TIMESTAMP的字段默认不为空(not null),默认值为当前时间(CURRENT_TIMESTAMP),
若是不作特殊处理,而且update语句中没有指定该列的更新值,则默认更新为当前时间。
注意事项
============注意啦,注意啦,注意啦===========
#1. 单独插入时间时,须要以字符串的形式,按照对应的格式插入
#2. 插入年份时,尽可能使用4位值
#3. 插入两位年份时,<=69,以20开头,好比50, 结果2050
>=70,以19开头,好比71,结果1971
create table t12(y year);
insert into t12 values (50),(71);
select * from t12;
+------+
| y |
+------+
| 2050 |
| 1971 |
+------+
综合练习:
建立一张学生表(student),要求有id,姓名,出生年份,出生的年月日,进班的时间,以及来老男孩学习的如今具体时间。
mysql> create table student(
-> id int,
-> name varchar(20),
-> born_year year,
-> birth date,
-> class_time time,
-> reg_time datetime
-> );
Query OK, 0 rows affected (0.02 sec)
mysql> insert into student values
-> (1,'alex',"1995","1995-11-11","11:11:11","2017-11-11 11:11:11"),
-> (2,'egon',"1997","1997-12-12","12:12:12","2017-12-12 12:12:12"),
-> (3,'wsb',"1998","1998-01-01","13:13:13","2017-01-01 13:13:13");
Query OK, 3 rows affected (0.00 sec)
Records: 3 Duplicates: 0 Warnings: 0
mysql> select * from student;
+------+------+-----------+------------+------------+---------------------+
| id | name | born_year | birth | class_time | reg_time |
+------+------+-----------+------------+------------+---------------------+
| 1 | alex | 1995 | 1995-11-11 | 11:11:11 | 2017-11-11 11:11:11 |
| 2 | egon | 1997 | 1997-12-12 | 12:12:12 | 2017-12-12 12:12:12 |
| 3 | wsb | 1998 | 1998-01-01 | 13:13:13 | 2017-01-01 13:13:13 |
+------+------+-----------+------------+------------+---------------------+
rows in set (0.00 sec)
本节重点
- 掌握char类型和varchar类型
- 掌握枚举类型和集合类型
字符类型
#官网:https://dev.mysql.com/doc/refman/5.7/en/char.html
#注意:char和varchar括号内的参数指的都是字符的长度
#char类型:定长,简单粗暴,浪费空间,存取速度快
字符长度范围:0-255(一个中文是一个字符,是utf8编码的3个字节)
存储:
存储char类型的值时,会往右填充空格来知足长度
例如:指定长度为10,存>10个字符则报错,存<10个字符则用空格填充直到凑够10个字符存储
检索:
在检索或者说查询时,查出的结果会自动删除尾部的空格,除非咱们打开pad_char_to_full_length SQL模式(设置SQL模式:SET sql_mode = 'PAD_CHAR_TO_FULL_LENGTH';
查询sql的默认模式:select @@sql_mode;)
#varchar类型:变长,精准,节省空间,存取速度慢
字符长度范围:0-65535(若是大于21845会提示用其余类型 。mysql行最大限制为65535字节,字符编码为utf-8:https://dev.mysql.com/doc/refman/5.7/en/column-count-limit.html)
存储:
varchar类型存储数据的真实内容,不会用空格填充,若是'ab ',尾部的空格也会被存起来
强调:varchar类型会在真实数据前加1-2Bytes的前缀,该前缀用来表示真实数据的bytes字节数(1-2Bytes最大表示65535个数字,正好符合mysql对row的最大字节限制,即已经足够使用)
若是真实的数据<255bytes则须要1Bytes的前缀(1Bytes=8bit 2**8最大表示的数字为255)
若是真实的数据>255bytes则须要2Bytes的前缀(2Bytes=16bit 2**16最大表示的数字为65535)
检索:
尾部有空格会保存下来,在检索或者说查询时,也会正常显示包含空格在内的内容
官网解释以下:

验证:
验证以前了解两个函数:
length():查看字节数
char_length():查看字符数
1.char填充空格来知足固定长度,可是在查询时却会很不要脸地删除尾部的空格(装做本身好像没有浪费过空间同样),而后修改sql_mode让其现出原形。
# 建立t1表,分别指明字段x为char类型,字段y为varchar类型
mysql> create table t1(x char(5),y varchar(4));
Query OK, 0 rows affected (0.16 sec)
# char存放的是5个字符,而varchar存4个字符
mysql> insert into t1 values('你瞅啥 ','你瞅啥 ');
Query OK, 1 row affected (0.01 sec)
# 在检索时char很不要脸地将本身浪费的2个字符给删掉了,装的好像本身没浪费过空间同样,而varchar很老实,存了多少,就显示多少
mysql> select x,char_length(x),y,char_length(y) from t1;
+-----------+----------------+------------+----------------+
| x | char_length(x) | y | char_length(y) |
+-----------+----------------+------------+----------------+
| 你瞅啥 | 3 | 你瞅啥 | 4 |
+-----------+----------------+------------+----------------+
1 row in set (0.02 sec)
#略施小计,让char现原形
mysql> SET sql_mode = 'PAD_CHAR_TO_FULL_LENGTH';
Query OK, 0 rows affected (0.00 sec)
#查看当前mysql的mode模式
mysql> select @@sql_mode;
+-------------------------+
| @@sql_mode |
+-------------------------+
| PAD_CHAR_TO_FULL_LENGTH |
+-------------------------+
1 row in set (0.00 sec)
#原形毕露了吧。。。。
mysql> select x,char_length(x) y,char_length(y) from t1;
+-------------+------+----------------+
| x | y | char_length(y) |
+-------------+------+----------------+
| 你瞅啥 | 5 | 4 |
+-------------+------+----------------+
1 row in set (0.00 sec)
# 查看字节数
#char类型:3个中文字符+2个空格=11Bytes
#varchar类型:3个中文字符+1个空格=10Bytes
mysql> select x,length(x),y,length(y) from t1;
+-------------+-----------+------------+-----------+
| x | length(x) | y | length(y) |
+-------------+-----------+------------+-----------+
| 你瞅啥 | 11 | 你瞅啥 | 10 |
+-------------+-----------+------------+-----------+
1 row in set (0.02 sec)
总结:
#经常使用字符串系列:char与varchar
注:虽然varchar使用起来较为灵活,可是从整个系统的性能角度来讲,char数据类型的处理速度更快,有时甚至能够超出varchar处理速度的50%。所以,用户在设计数据库时应当综合考虑各方面的因素,以求达到最佳的平衡
#其余字符串系列(效率:char>varchar>text)
TEXT系列 TINYTEXT TEXT MEDIUMTEXT LONGTEXT
BLOB 系列 TINYBLOB BLOB MEDIUMBLOB LONGBLOB
BINARY系列 BINARY VARBINARY
text:text数据类型用于保存变长的大字符串,能够组多到65535 (2**16 − 1)个字符。
mediumtext:A TEXT column with a maximum length of 16,777,215 (2**24 − 1) characters.
longtext:A TEXT column with a maximum length of 4,294,967,295 or 4GB (2**32 − 1) characters.
枚举类型和集合类型
字段的值只能在给定范围中选择,如单选框,多选框
enum 单选 只能在给定的范围内选一个值,如性别 sex 男male/女female
set 多选 在给定的范围内能够选择一个或一个以上的值(爱好1,爱好2,爱好3...)
mysql> create table consumer(
-> id int,
-> name varchar(50),
-> sex enum('male','female','other'),
-> level enum('vip1','vip2','vip3','vip4'),#在指定范围内,多选一
-> fav set('play','music','read','study') #在指定范围内,多选多
-> );
Query OK, 0 rows affected (0.03 sec)
mysql> insert into consumer values
-> (1,'赵云','male','vip2','read,study'),
-> (2,'赵云2','other','vip4','play');
Query OK, 2 rows affected (0.00 sec)
Records: 2 Duplicates: 0 Warnings: 0
mysql> select * from consumer;
+------+---------+-------+-------+------------+
| id | name | sex | level | fav |
+------+---------+-------+-------+------------+
| 1 | 赵云 | male | vip2 | read,study |
| 2 | 赵云2 | other | vip4 | play |
+------+---------+-------+-------+------------+
2 rows in set (0.00 sec)
本节重点:
- not null 与 default
- unique
- primary
- auto_increment
- foreign key
1、介绍
约束条件与数据类型的宽度同样,都是可选参数
做用:用于保证数据的完整性和一致性
主要分为:
PRIMARY KEY (PK) #标识该字段为该表的主键,能够惟一的标识记录
FOREIGN KEY (FK) #标识该字段为该表的外键
NOT NULL #标识该字段不能为空
UNIQUE KEY (UK) #标识该字段的值是惟一的
AUTO_INCREMENT #标识该字段的值自动增加(整数类型,并且为主键)
DEFAULT #为该字段设置默认值
UNSIGNED #无符号
ZEROFILL #使用0填充
说明:
#1. 是否容许为空,默认NULL,可设置NOT NULL,字段不容许为空,必须赋值
#2. 字段是否有默认值,缺省的默认值是NULL,若是插入记录时不给字段赋值,此字段使用默认值
sex enum('male','female') not null default 'male'
#必须为正值(无符号) 不容许为空 默认是20
age int unsigned NOT NULL default 20
# 3. 是不是key
主键 primary key
外键 foreign key
索引 (index,unique...)
2、not null 与default
是否可空,null表示空,非字符串
not null - 不可空
null - 可空
默认值,建立列时能够指定默认值,当插入数据时若是未主动设置,则自动添加默认值
create table tb1(
nid int not null defalut 2,
num int not null
);
验证1:
mysql> create table t11(id int);# id字段默承认觉得空
Query OK, 0 rows affected (0.05 sec)
mysql> desc t11;
+-------+---------+------+-----+---------+-------+
| Field | Type | Null | Key | Default | Extra |
+-------+---------+------+-----+---------+-------+
| id | int(11) | YES | | NULL | |
+-------+---------+------+-----+---------+-------+
row in set (0.03 sec)
mysql> insert into t11 values(); #给t11表插一个空的值
Query OK, 1 row affected (0.00 sec)
#查询结果以下
mysql> select * from t11;
+------+
| id |
+------+
| NULL |
+------+
row in set (0.00 sec)
默认值能够为空
验证2:
mysql> create table t12(id int not null);#设置字段id不为空
Query OK, 0 rows affected (0.03 sec)
mysql> desc t12;
+-------+---------+------+-----+---------+-------+
| Field | Type | Null | Key | Default | Extra |
+-------+---------+------+-----+---------+-------+
| id | int(11) | NO | | NULL | |
+-------+---------+------+-----+---------+-------+
row in set (0.01 sec)
mysql> insert into t12 values();#不能插入空
ERROR 1364 (HY000): Field 'id' doesn't have a default value
设置not null,插入值时不能为空
验证3:
# 第一种状况
mysql> create table t13(id int default 1);
Query OK, 0 rows affected (0.03 sec)
mysql> desc t13;
+-------+---------+------+-----+---------+-------+
| Field | Type | Null | Key | Default | Extra |
+-------+---------+------+-----+---------+-------+
| id | int(11) | YES | | 1 | |
+-------+---------+------+-----+---------+-------+
row in set (0.01 sec)
mysql> insert into t13 values();
Query OK, 1 row affected (0.00 sec)
mysql> select * from t13;
+------+
| id |
+------+
| 1 |
+------+
row in set (0.00 sec)
# 第二种状况
mysql> create table t14(id int not null default 2);
Query OK, 0 rows affected (0.02 sec)
mysql> desc t14;
+-------+---------+------+-----+---------+-------+
| Field | Type | Null | Key | Default | Extra |
+-------+---------+------+-----+---------+-------+
| id | int(11) | NO | | 2 | |
+-------+---------+------+-----+---------+-------+
row in set (0.01 sec)
mysql> select * from t14;
+----+
| id |
+----+
| 2 |
+----+
row in set (0.00 sec)
设置id字段有默认值后,则不管id字段是null仍是not null,均可以插入空,插入空默认填入default指定的默认值
小练习:
建立学生表student2,设置每一个字段的约束条件。
mysql> create table student2(
-> id int not null,
-> name varchar(50) not null,
-> age int(3) unsigned not null default 18,
-> sex enum('male','female') default 'male',
-> fav set('smoke','drink','tangtou') default 'drink,tangtou'
-> );
Query OK, 0 rows affected (0.01 sec)
# 只插入了not null约束条件的字段对应的值
mysql> insert into student2(id,name) values(1,'mjj');
Query OK, 1 row affected (0.00 sec)
# 查询结果以下
mysql> select * from student2;
+----+------+-----+------+---------------+
| id | name | age | sex | fav |
+----+------+-----+------+---------------+
| 1 | mjj | 18 | male | drink,tangtou |
+----+------+-----+------+---------------+
1 row in set (0.00 sec)
三、unique
中文翻译:不一样的。在mysql中称为单列惟一
举例说明:建立公司部门表(每一个公司都有惟一的一个部门)。
mysql> create table department(
-> id int,
-> name char(10)
-> );
Query OK, 0 rows affected (0.01 sec)
mysql> insert into department values(1,'IT'),(2,'IT');
Query OK, 2 rows affected (0.00 sec)
Records: 2 Duplicates: 0 Warnings: 0
mysql> select * from department;
+------+------+
| id | name |
+------+------+
| 1 | IT |
| 2 | IT |
+------+------+
rows in set (0.00 sec)
# 发现: 同时插入两个IT部门也是能够的,但这是不合理的,因此咱们要设置name字段为unique 解决这种不合理的现象。
验证以前重复插入记录的操做是可行的,可是不符合场景
接下来,使用约束条件unique,来对公司部门的字段进行设置。
#第一种建立unique的方式
#例子1:
create table department(
id int,
name char(10) unique
);
mysql> insert into department values(1,'it'),(2,'it');
ERROR 1062 (23000): Duplicate entry 'it' for key 'name'
#例子2:
create table department(
id int unique,
name char(10) unique
);
insert into department values(1,'it'),(2,'sale');
#第二种建立unique的方式
create table department(
id int,
name char(10) ,
unique(id),
unique(name)
);
insert into department values(1,'it'),(2,'sale');
联合惟一:
# 建立services表
mysql> create table services(
-> id int,
-> ip char(15),
-> port int,
-> unique(id),
-> unique(ip,port)
-> );
Query OK, 0 rows affected (0.05 sec)
mysql> desc services;
+-------+----------+------+-----+---------+-------+
| Field | Type | Null | Key | Default | Extra |
+-------+----------+------+-----+---------+-------+
| id | int(11) | YES | UNI | NULL | |
| ip | char(15) | YES | MUL | NULL | |
| port | int(11) | YES | | NULL | |
+-------+----------+------+-----+---------+-------+
3 rows in set (0.01 sec)
#联合惟一,只要两列记录,有一列不一样,既符合联合惟一的约束
mysql> insert into services values
-> (1,'192,168,11,23',80),
-> (2,'192,168,11,23',81),
-> (3,'192,168,11,25',80);
Query OK, 3 rows affected (0.01 sec)
Records: 3 Duplicates: 0 Warnings: 0
mysql> select * from services;
+------+---------------+------+
| id | ip | port |
+------+---------------+------+
| 1 | 192,168,11,23 | 80 |
| 2 | 192,168,11,23 | 81 |
| 3 | 192,168,11,25 | 80 |
+------+---------------+------+
3 rows in set (0.00 sec)
mysql> insert into services values (4,'192,168,11,23',80);
ERROR 1062 (23000): Duplicate entry '192,168,11,23-80' for key 'ip'
4.primary key
一个表中能够:
单列作主键
多列作主键(复合主键)
约束:等价于 not null unique,字段的值不为空且惟一
存储引擎默认是(innodb):对于innodb存储引擎来讲,一张表必须有一个主键。
单列主键
# 建立t14表,为id字段设置主键,惟一的不一样的记录
create table t14(
id int primary key,
name char(16)
);
insert into t14 values
(1,'xiaoma'),
(2,'xiaohong');
mysql> insert into t14 values(2,'wxxx');
ERROR 1062 (23000): Duplicate entry '6' for key 'PRIMARY'
# not null + unique的化学反应,至关于给id设置primary key
create table t15(
id int not null unique,
name char(16)
);
mysql> create table t15(
-> id int not null unique,
-> name char(16)
-> );
Query OK, 0 rows affected (0.01 sec)
mysql> desc t15;
+-------+----------+------+-----+---------+-------+
| Field | Type | Null | Key | Default | Extra |
+-------+----------+------+-----+---------+-------+
| id | int(11) | NO | PRI | NULL | |
| name | char(16) | YES | | NULL | |
+-------+----------+------+-----+---------+-------+
2 rows in set (0.02 sec)
复合主键
create table t16(
ip char(15),
port int,
primary key(ip,port)
);
insert into t16 values
('1.1.1.2',80),
('1.1.1.2',81);
验证复合主键的使用
5.auto_increment
约束:约束的字段为自动增加,约束的字段必须同时被key约束
(重点)验证:
# 建立student
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 | |
+-------+-----------------------+------+-----+---------+----------------+
rows in set (0.17 sec)
#插入记录
mysql> insert into student(name) values ('老白'),('小白');
Query OK, 2 rows affected (0.01 sec)
Records: 2 Duplicates: 0 Warnings: 0
mysql> select * from student;
+----+--------+------+
| id | name | sex |
+----+--------+------+
| 1 | 老白 | male |
| 2 | 小白 | male |
+----+--------+------+
rows in set (0.00 sec)
不指定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.01 sec)
mysql> select * from student;
+----+--------+--------+
| id | name | sex |
+----+--------+--------+
| 1 | 老白 | male |
| 2 | 小白 | male |
| 4 | asb | female |
| 7 | wsb | female |
+----+--------+--------+
rows in set (0.00 sec)
# 再次插入一条不指定id的记录,会在以前的最后一条记录继续增加
mysql> insert into student(name) values ('大白');
Query OK, 1 row affected (0.00 sec)
mysql> select * from student;
+----+--------+--------+
| id | name | sex |
+----+--------+--------+
| 1 | 老白 | male |
| 2 | 小白 | male |
| 4 | asb | female |
| 7 | wsb | female |
| 8 | 大白 | male |
+----+--------+--------+
rows in set (0.00 sec)
也能够指定id
mysql> delete from student;
Query OK, 5 rows affected (0.00 sec)
mysql> select * from student;
Empty set (0.00 sec)
mysql> select * from student;
Empty set (0.00 sec)
mysql> insert into student(name) values('ysb');
Query OK, 1 row affected (0.01 sec)
mysql> select * from student;
+----+------+------+
| id | name | sex |
+----+------+------+
| 9 | ysb | male |
+----+------+------+
row in set (0.00 sec)
#应该用truncate清空表,比起delete一条一条地删除记录,truncate是直接清空表,在删除大表时用它
mysql> truncate student;
Query OK, 0 rows affected (0.03 sec)
mysql> insert into student(name) values('xiaobai');
Query OK, 1 row affected (0.00 sec)
mysql> select * from student;
+----+---------+------+
| id | name | sex |
+----+---------+------+
| 1 | xiaobai | male |
+----+---------+------+
row in set (0.00 sec)
mysql>
对于自增的字段,在用delete删除后,再插入值,该字段仍按照删除前的位置继续增加
了解:
查看可用的 开头auto_inc的词
mysql> show variables like 'auto_inc%';
+--------------------------+-------+
| Variable_name | Value |
+--------------------------+-------+
| auto_increment_increment | 1 |
| auto_increment_offset | 1 |
+--------------------------+-------+
rows in set (0.02 sec)
# 步长auto_increment_increment,默认为1
# 起始的偏移量auto_increment_offset, 默认是1
# 设置步长 为会话设置,只在本次链接中有效
set session auto_increment_increment=5;
#全局设置步长 都有效。
set global auto_increment_increment=5;
# 设置起始偏移量
set global auto_increment_offset=3;
#强调:If the value of auto_increment_offset is greater than that of auto_increment_increment, the value of auto_increment_offset is ignored.
翻译:若是auto_increment_offset的值大于auto_increment_increment的值,则auto_increment_offset的值会被忽略
# 设置完起始偏移量和步长以后,再次执行show variables like'auto_inc%';
发现跟以前同样,必须先exit,再登陆才有效。
mysql> show variables like'auto_inc%';
+--------------------------+-------+
| Variable_name | Value |
+--------------------------+-------+
| auto_increment_increment | 5 |
| auto_increment_offset | 3 |
+--------------------------+-------+
rows in set (0.00 sec)
#由于以前有一条记录id=1
mysql> select * from student;
+----+---------+------+
| id | name | sex |
+----+---------+------+
| 1 | xiaobai | male |
+----+---------+------+
row in set (0.00 sec)
# 下次插入的时候,从起始位置3开始,每次插入记录id+5
mysql> insert into student(name) values('ma1'),('ma2'),('ma3');
Query OK, 3 rows affected (0.00 sec)
Records: 3 Duplicates: 0 Warnings: 0
mysql> select * from student;
+----+---------+------+
| id | name | sex |
+----+---------+------+
| 1 | xiaobai | male |
| 3 | ma1 | male |
| 8 | ma2 | male |
| 13 | ma3 | male |
+----+---------+------+
auto_increment_increment和 auto_increment_offset
清空表区分delete和truncate的区别:
delete from t1; #若是有自增id,新增的数据,仍然是以删除前的最后同样做为起始。
truncate table t1;数据量大,删除速度比上一条快,且直接从零开始。
6.foreign key
一 快速理解foreign key
以前建立表的时候都是在一张表中添加记录,好比以下表:

公司有3个部门,可是有1个亿的员工,那意味着部门这个字段须要重复存储,部门名字越长,越浪费。
这个时候,
解决方法:
咱们彻底能够定义一个部门表
而后让员工信息表关联该表,如何关联,即foreign key
咱们能够将上表改成以下结构:

此时有两张表,一张是employee表,简称emp表(关联表,也就从表)。一张是department表,简称dep表(被关联表,也叫主表)。
建立两张表操做:
本节重点:
1、介绍
由于有foreign key的约束,使得两张表造成了三种了关系:
2、重点理解若是找出两张表之间的关系
分析步骤:
#一、先站在左表的角度去找
是否左表的多条记录能够对应右表的一条记录,若是是,则证实左表的一个字段foreign key 右表一个字段(一般是id)
#二、再站在右表的角度去找
是否右表的多条记录能够对应左表的一条记录,若是是,则证实右表的一个字段foreign key 左表一个字段(一般是id)
#三、总结:
#多对一:
若是只有步骤1成立,则是左表多对一右表
若是只有步骤2成立,则是右表多对一左表
#多对多
若是步骤1和2同时成立,则证实这两张表时一个双向的多对一,即多对多,须要定义一个这两张表的关系表来专门存放两者的关系
#一对一:
若是1和2都不成立,而是左表的一条记录惟一对应右表的一条记录,反之亦然。这种状况很简单,就是在左表foreign key右表的基础上,将左表的外键字段设置成unique便可
3、表的三种关系
(1)书和出版社
一对多(或多对一):一个出版社能够出版多本书。看图说话。
关联方式:foreign key

create table press(
id int primary key auto_increment,
name varchar(20)
);
create table book(
id int primary key auto_increment,
name varchar(20),
press_id int not null,
constraint fk_book_press foreign key(press_id) references press(id)
on delete cascade
on update cascade
);
# 先往被关联表中插入记录
insert into press(name) values
('北京工业地雷出版社'),
('人民音乐很差听出版社'),
('知识产权没有用出版社')
;
# 再往关联表中插入记录
insert into book(name,press_id) values
('九阳神功',1),
('九阴真经',2),
('九阴白骨爪',2),
('独孤九剑',3),
('降龙十巴掌',2),
('葵花宝典',3)
;
查询结果:
mysql> select * from book;
+----+-----------------+----------+
| id | name | press_id |
+----+-----------------+----------+
| 1 | 九阳神功 | 1 |
| 2 | 九阴真经 | 2 |
| 3 | 九阴白骨爪 | 2 |
| 4 | 独孤九剑 | 3 |
| 5 | 降龙十巴掌 | 2 |
| 6 | 葵花宝典 | 3 |
+----+-----------------+----------+
rows in set (0.00 sec)
mysql> select * from press;
+----+--------------------------------+
| id | name |
+----+--------------------------------+
| 1 | 北京工业地雷出版社 |
| 2 | 人民音乐很差听出版社 |
| 3 | 知识产权没有用出版社 |
+----+--------------------------------+
rows in set (0.00 sec)
书和出版社(多对一)
(2)做者和书籍的关系
多对多:一个做者能够写多本书,一本书也能够有多个做者,双向的一对多,即多对多。
看图说话。
关联方式:foreign key+一张新的表

# 建立被关联表author表,以前的book表在讲多对一的关系已建立
create table author(
id int primary key auto_increment,
name varchar(20)
);
#这张表就存放了author表和book表的关系,即查询两者的关系查这表就能够了
create table author2book(
id int not null unique auto_increment,
author_id int not null,
book_id int not null,
constraint fk_author foreign key(author_id) references author(id)
on delete cascade
on update cascade,
constraint fk_book foreign key(book_id) references book(id)
on delete cascade
on update cascade,
primary key(author_id,book_id)
);
#插入四个做者,id依次排开
insert into author(name) values('egon'),('alex'),('wusir'),('yuanhao');
# 每一个做者的表明做
egon: 九阳神功、九阴真经、九阴白骨爪、独孤九剑、降龙十巴掌、葵花宝典
alex: 九阳神功、葵花宝典
wusir:独孤九剑、降龙十巴掌、葵花宝典
yuanhao:九阳神功
# 在author2book表中插入相应的数据
insert into author2book(author_id,book_id) values
(1,1),
(1,2),
(1,3),
(1,4),
(1,5),
(1,6),
(2,1),
(2,6),
(3,4),
(3,5),
(3,6),
(4,1)
;
# 如今就能够查author2book对应的做者和书的关系了
mysql> select * from author2book;
+----+-----------+---------+
| id | author_id | book_id |
+----+-----------+---------+
| 1 | 1 | 1 |
| 2 | 1 | 2 |
| 3 | 1 | 3 |
| 4 | 1 | 4 |
| 5 | 1 | 5 |
| 6 | 1 | 6 |
| 7 | 2 | 1 |
| 8 | 2 | 6 |
| 9 | 3 | 4 |
| 10 | 3 | 5 |
| 11 | 3 | 6 |
| 12 | 4 | 1 |
+----+-----------+---------+
rows in set (0.00 sec)
做者与书籍关系(多对多)
(3)用户和博客
一对一:一个用户只能注册一个博客,即一对一的关系。看图说话
关联方式:foreign key+unique

#例如: 一个用户只能注册一个博客
#两张表: 用户表 (user)和 博客表(blog)
# 建立用户表
create table user(
id int primary key auto_increment,
name varchar(20)
);
# 建立博客表
create table blog(
id int primary key auto_increment,
url varchar(100),
user_id int unique,
constraint fk_user foreign key(user_id) references user(id)
on delete cascade
on update cascade
);
#插入用户表中的记录
insert into user(name) values
('alex'),
('wusir'),
('egon'),
('xiaoma')
;
# 插入博客表的记录
insert into blog(url,user_id) values
('http://www.cnblog/alex',1),
('http://www.cnblog/wusir',2),
('http://www.cnblog/egon',3),
('http://www.cnblog/xiaoma',4)
;
# 查询wusir的博客地址
select url from blog where user_id=2;
用户和博客(一对一)
本节重点:
- 插入数据 INSERT
- 更新数据 UPDATE
- 删除数据 DELETE
再来回顾一下以前咱们练过的一些操做,相信你们都对插入数据、更新数据、删除数据有了全面的认识。那么在mysql中其实最重要的不是这三大操做,而是查数据最重要,下节课知晓
1、
在MySQL管理软件中,能够经过SQL语句中的DML语言来实现数据的操做,包括
1.使用INSERT实现数据的插入
2.UPDATE实现数据的更新
3.使用DELETE实现数据的删除
4.使用SELECT查询数据以及。
2、插入数据 INSERT
1. 插入完整数据(顺序插入)
语法一:
INSERT INTO 表名(字段1,字段2,字段3…字段n) VALUES(值1,值2,值3…值n);
语法二:
INSERT INTO 表名 VALUES (值1,值2,值3…值n);
2. 指定字段插入数据
语法:
INSERT INTO 表名(字段1,字段2,字段3…) VALUES (值1,值2,值3…);
3. 插入多条记录
语法:
INSERT INTO 表名 VALUES
(值1,值2,值3…值n),
(值1,值2,值3…值n),
(值1,值2,值3…值n);
4. 插入查询结果
语法:
INSERT INTO 表名(字段1,字段2,字段3…字段n)
SELECT (字段1,字段2,字段3…字段n) FROM 表2
WHERE …;
3、更新数据UPDATE
语法:
UPDATE 表名 SET
字段1=值1,
字段2=值2,
WHERE CONDITION;
示例:
UPDATE mysql.user SET password=password(‘123’)
where user=’root’ and host=’localhost’;
4、删除数据DELETE
语法:
DELETE FROM 表名
WHERE CONITION;
示例:
DELETE FROM mysql.user
WHERE password=’’;
本节重点:
单表查询
语法:
1、单表查询的语法
SELECT 字段1,字段2... FROM 表名
WHERE 条件
GROUP BY field
HAVING 筛选
ORDER BY field
LIMIT 限制条数
2、关键字的执行优先级(重点)
重点中的重点:关键字的执行优先级
from
where
group by
having
select
distinct
order by
limit
1.找到表:from
2.拿着where指定的约束条件,去文件/表中取出一条条记录
3.将取出的一条条记录进行分组group by,若是没有group by,则总体做为一组
4.将分组的结果进行having过滤
5.执行select
6.去重
7.将结果按条件排序:order by
8.限制结果的显示条数
建立公司员工表,表的字段和数据类型
company.employee
员工id id int
姓名 name varchar
性别 sex enum
年龄 age int
入职日期 hire_date date
岗位 post varchar
职位描述 post_comment varchar
薪水 salary double
办公室 office int
部门编号 depart_id int
#建立表,设置字段的约束条件
create table employee(
id int primary key auto_increment,
name varchar(20) not null,
sex enum('male','female') not null default 'male', #大部分是男的
age int(3) unsigned not null default 28,
hire_date date not null,
post varchar(50),
post_comment varchar(100),
salary double(15,2),
office int,#一个部门一个屋
depart_id int
);
# 查看表结构
mysql> desc employee;
+--------------+-----------------------+------+-----+---------+----------------+
| Field | Type | Null | Key | Default | Extra |
+--------------+-----------------------+------+-----+---------+----------------+
| id | int(11) | NO | PRI | NULL | auto_increment |
| emp_name | varchar(20) | NO | | NULL | |
| sex | enum('male','female') | NO | | male | |
| age | int(3) unsigned | NO | | 28 | |
| hire_date | date | NO | | NULL | |
| post | varchar(50) | YES | | NULL | |
| post_comment | varchar(100) | YES | | NULL | |
| salart | double(15,2) | YES | | NULL | |
| office | int(11) | YES | | NULL | |
| depart_id | int(11) | YES | | NULL | |
+--------------+-----------------------+------+-----+---------+----------------+
rows in set (0.08 sec)
#插入记录
#三个部门:教学,销售,运营
insert into employee(name ,sex,age,hire_date,post,salary,office,depart_id) values
('egon','male',18,'20170301','老男孩驻沙河办事处外交大使',7300.33,401,1), #如下是教学部
('alex','male',78,'20150302','teacher',1000000.31,401,1),
('wupeiqi','male',81,'20130305','teacher',8300,401,1),
('yuanhao','male',73,'20140701','teacher',3500,401,1),
('liwenzhou','male',28,'20121101','teacher',2100,401,1),
('jingliyang','female',18,'20110211','teacher',9000,401,1),
('jinxin','male',18,'19000301','teacher',30000,401,1),
('xiaomage','male',48,'20101111','teacher',10000,401,1),
('歪歪','female',48,'20150311','sale',3000.13,402,2),#如下是销售部门
('丫丫','female',38,'20101101','sale',2000.35,402,2),
('丁丁','female',18,'20110312','sale',1000.37,402,2),
('星星','female',18,'20160513','sale',3000.29,402,2),
('格格','female',28,'20170127','sale',4000.33,402,2),
('张野','male',28,'20160311','operation',10000.13,403,3), #如下是运营部门
('程咬金','male',18,'19970312','operation',20000,403,3),
('程咬银','female',18,'20130311','operation',19000,403,3),
('程咬铜','male',18,'20150411','operation',18000,403,3),
('程咬铁','female',18,'20140512','operation',17000,403,3)
;
建立员工表,并插入记录
(1)where 约束
where子句中能够使用
1.比较运算符:>、<、>=、<=、<>、!=
2.between 80 and 100 :值在80到100之间
3.in(80,90,100)值是10或20或30
4.like 'xiaomagepattern': pattern能够是%或者_。%小时任意多字符,_表示一个字符
5.逻辑运算符:在多个条件直接能够使用逻辑运算符 and or not
验证结果:
#1 :单条件查询
mysql> select id,emp_name from employee where id > 5;
+----+------------+
| id | emp_name |
+----+------------+
| 6 | jingliyang |
| 7 | jinxin |
| 8 | xiaomage |
| 9 | 歪歪 |
| 10 | 丫丫 |
| 11 | 丁丁 |
| 12 | 星星 |
| 13 | 格格 |
| 14 | 张野 |
| 15 | 程咬金 |
| 16 | 程咬银 |
| 17 | 程咬铜 |
| 18 | 程咬铁 |
#2 多条件查询
mysql> select emp_name from employee where post='teacher' and salary>10000;
+----------+
| emp_name |
+----------+
| alex |
| jinxin |
+----------+
#3.关键字BETWEEN AND
SELECT name,salary FROM employee
WHERE salary BETWEEN 10000 AND 20000;
SELECT name,salary FROM employee
WHERE salary NOT BETWEEN 10000 AND 20000;
#注意''是空字符串,不是null
SELECT name,post_comment FROM employee WHERE post_comment='';
ps:
执行
update employee set post_comment='' where id=2;
再用上条查看,就会有结果了
#5:关键字IN集合查询
mysql> SELECT name,salary FROM employee WHERE salary=3000 OR salary=3500 OR salary=4000 OR salary=9000 ;
+------------+---------+
| name | salary |
+------------+---------+
| yuanhao | 3500.00 |
| jingliyang | 9000.00 |
+------------+---------+
rows in set (0.00 sec)
mysql> SELECT name,salary FROM employee WHERE salary IN (3000,3500,4000,9000) ;
+------------+---------+
| name | salary |
+------------+---------+
| yuanhao | 3500.00 |
| jingliyang | 9000.00 |
+------------+---------+
mysql> SELECT name,salary FROM employee WHERE salary NOT IN (3000,3500,4000,9000) ;
+-----------+------------+
| name | salary |
+-----------+------------+
| egon | 7300.33 |
| alex | 1000000.31 |
| wupeiqi | 8300.00 |
| liwenzhou | 2100.00 |
| jinxin | 30000.00 |
| xiaomage | 10000.00 |
| 歪歪 | 3000.13 |
| 丫丫 | 2000.35 |
| 丁丁 | 1000.37 |
| 星星 | 3000.29 |
| 格格 | 4000.33 |
| 张野 | 10000.13 |
| 程咬金 | 20000.00 |
| 程咬银 | 19000.00 |
| 程咬铜 | 18000.00 |
| 程咬铁 | 17000.00 |
+-----------+------------+
rows in set (0.00 sec)
#6:关键字LIKE模糊查询
通配符’%’
mysql> SELECT * FROM employee WHERE name LIKE 'jin%';
+----+------------+--------+-----+------------+---------+--------------+----------+--------+-----------+
| id | name | sex | age | hire_date | post | post_comment | salary | office | depart_id |
+----+------------+--------+-----+------------+---------+--------------+----------+--------+-----------+
| 6 | jingliyang | female | 18 | 2011-02-11 | teacher | NULL | 9000.00 | 401 | 1 |
| 7 | jinxin | male | 18 | 1900-03-01 | teacher | NULL | 30000.00 | 401 | 1 |
+----+------------+--------+-----+------------+---------+--------------+----------+--------+-----------+
rows in set (0.00 sec)
通配符'_'
mysql> SELECT age FROM employee WHERE name LIKE 'ale_';
+-----+
| age |
+-----+
| 78 |
+-----+
row in set (0.00 sec)
练习:
1. 查看岗位是teacher的员工姓名、年龄
2. 查看岗位是teacher且年龄大于30岁的员工姓名、年龄
3. 查看岗位是teacher且薪资在9000-1000范围内的员工姓名、年龄、薪资
4. 查看岗位描述不为NULL的员工信息
5. 查看岗位是teacher且薪资是10000或9000或30000的员工姓名、年龄、薪资
6. 查看岗位是teacher且薪资不是10000或9000或30000的员工姓名、年龄、薪资
7. 查看岗位是teacher且名字是jin开头的员工姓名、年薪
#对应的sql语句
select name,age from employee where post = 'teacher';
select name,age from employee where post='teacher' and age > 30;
select name,age,salary from employee where post='teacher' and salary between 9000 and 10000;
select * from employee where post_comment is not null;
select name,age,salary from employee where post='teacher' and salary in (10000,9000,30000);
select name,age,salary from employee where post='teacher' and salary not in (10000,9000,30000);
select name,salary*12 from employee where post='teacher' and name like 'jin%';
where约束
(2)group by 分组查询
#一、首先明确一点:分组发生在where以后,即分组是基于where以后获得的记录而进行的
#二、分组指的是:将全部记录按照某个相同字段进行归类,好比针对员工信息表的职位分组,或者按照性别进行分组等
#三、为什么要分组呢?
取每一个部门的最高工资
取每一个部门的员工数
取男人数和女人数
小窍门:‘每’这个字后面的字段,就是咱们分组的依据
#四、大前提:
能够按照任意字段分组,可是分组完毕后,好比group by post,只能查看post字段,若是想查看组内信息,须要借助于聚合函数
当执行如下sql语句的时候,是以post字段查询了组中的第一条数据,没有任何意义,由于咱们如今想查出当前组的多条记录。
mysql> select * from employee group by post;
+----+--------+--------+-----+------------+-----------------------------------------+--------------+------------+--------+-----------+
| id | name | sex | age | hire_date | post | post_comment | salary | office | depart_id |
+----+--------+--------+-----+------------+-----------------------------------------+--------------+------------+--------+-----------+
| 14 | 张野 | male | 28 | 2016-03-11 | operation | NULL | 10000.13 | 403 | 3 |
| 9 | 歪歪 | female | 48 | 2015-03-11 | sale | NULL | 3000.13 | 402 | 2 |
| 2 | alex | male | 78 | 2015-03-02 | teacher | | 1000000.31 | 401 | 1 |
| 1 | egon | male | 18 | 2017-03-01 | 老男孩驻沙河办事处外交大使 | NULL | 7300.33 | 401 | 1 |
+----+--------+--------+-----+------------+-----------------------------------------+--------------+------------+--------+-----------+
4 rows in set (0.00 sec)
#因为没有设置ONLY_FULL_GROUP_BY,因而也能够有结果,默认都是组内的第一条记录,但其实这是没有意义的
若是想分组,则必需要设置全局的sql的模式为ONLY_FULL_GROUP_BY
mysql> set global sql_mode='ONLY_FULL_GROUP_BY';
Query OK, 0 rows affected (0.00 sec)
#查看MySQL 5.7默认的sql_mode以下:
mysql> select @@global.sql_mode;
+--------------------+
| @@global.sql_mode |
+--------------------+
| ONLY_FULL_GROUP_BY |
+--------------------+
1 row in set (0.00 sec)
mysql> exit;#设置成功后,必定要退出,而后从新登陆方可生效
Bye
继续验证经过group by分组以后,只能查看当前字段,若是想查看组内信息,须要借助于聚合函数
mysql> select * from emp group by post;# 报错
ERROR 1054 (42S22): Unknown column 'post' in 'group statement'
mysql> select post from employee group by post;
+-----------------------------------------+
| post |
+-----------------------------------------+
| operation |
| sale |
| teacher |
| 老男孩驻沙河办事处外交大使 |
+-----------------------------------------+
4 rows in set (0.00 sec)
(3)聚合函数
max()求最大值
min()求最小值
avg()求平均值
sum() 求和
count() 求总个数
#强调:聚合函数聚合的是组的内容,如果没有分组,则默认一组
# 每一个部门有多少个员工
select post,count(id) from employee group by post;
# 每一个部门的最高薪水
select post,max(salary) from employee group by post;
# 每一个部门的最低薪水
select post,min(salary) from employee group by post;
# 每一个部门的平均薪水
select post,avg(salary) from employee group by post;
# 每一个部门的全部薪水
select post,sum(age) from employee group by post;
1. 查询岗位名以及岗位包含的全部员工名字
2. 查询岗位名以及各岗位内包含的员工个数
3. 查询公司内男员工和女员工的个数
4. 查询岗位名以及各岗位的平均薪资
5. 查询岗位名以及各岗位的最高薪资
6. 查询岗位名以及各岗位的最低薪资
7. 查询男员工与男员工的平均薪资,女员工与女员工的平均薪资
小练习
(4)HAVING过滤
HAVING与WHERE不同的地方在于
#!!!执行优先级从高到低:where > group by > having
#1. Where 发生在分组group by以前,于是Where中能够有任意字段,可是绝对不能使用聚合函数。
#2. Having发生在分组group by以后,于是Having中能够使用分组的字段,没法直接取到其余字段,能够使用聚合函数
验证:
验证:
mysql> select * from employee where salary>1000000;
+----+------+------+-----+------------+---------+--------------+------------+--------+-----------+
| id | name | sex | age | hire_date | post | post_comment | salary | office | depart_id |
+----+------+------+-----+------------+---------+--------------+------------+--------+-----------+
| 2 | alex | male | 78 | 2015-03-02 | teacher | | 1000000.31 | 401 | 1 |
+----+------+------+-----+------------+---------+--------------+------------+--------+-----------+
row in set (0.00 sec)
mysql> select * from employee having salary>1000000;
ERROR 1463 (42000): Non-grouping field 'salary' is used in HAVING clause
# 必须使用group by才能使用group_concat()函数,将全部的name值链接
mysql> select post,group_concat(name) from emp group by post having salary > 10000; ##错误,分组后没法直接取到salary字段
ERROR 1054 (42S22): Unknown column 'post' in 'field list'
小练习:
1. 查询各岗位内包含的员工个数小于2的岗位名、岗位内包含员工名字、个数
2. 查询各岗位平均薪资大于10000的岗位名、平均工资
3. 查询各岗位平均薪资大于10000且小于20000的岗位名、平均工资
小练习答案:
# 题1:
mysql> select post,group_concat(name),count(id) from employee group by post;
+-----------------------------------------+-----------------------------------------------------------+-----------+
| post | group_concat(name) | count(id) |
+-----------------------------------------+-----------------------------------------------------------+-----------+
| operation | 程咬铁,程咬铜,程咬银,程咬金,张野 | 5 |
| sale | 格格,星星,丁丁,丫丫,歪歪 | 5 |
| teacher | xiaomage,jinxin,jingliyang,liwenzhou,yuanhao,wupeiqi,alex | 7 |
| 老男孩驻沙河办事处外交大使 | egon | 1 |
+-----------------------------------------+-----------------------------------------------------------+-----------+
rows in set (0.00 sec)
mysql> select post,group_concat(name),count(id) from employee group by post having count(id)<2;
+-----------------------------------------+--------------------+-----------+
| post | group_concat(name) | count(id) |
+-----------------------------------------+--------------------+-----------+
| 老男孩驻沙河办事处外交大使 | egon | 1 |
+-----------------------------------------+--------------------+-----------+
row in set (0.00 sec)
#题2:
mysql> select post,avg(salary) from employee group by post having avg(salary) > 10000;
+-----------+---------------+
| post | avg(salary) |
+-----------+---------------+
| operation | 16800.026000 |
| teacher | 151842.901429 |
+-----------+---------------+
rows in set (0.00 sec)
#题3:
mysql> select post,avg(salary) from employee group by post having avg(salary) > 10000 and avg(salary) <20000;
+-----------+--------------+
| post | avg(salary) |
+-----------+--------------+
| operation | 16800.026000 |
+-----------+--------------+
row in set (0.00 sec)
(5)order by 查询排序
按单列排序
SELECT * FROM employee ORDER BY age;
SELECT * FROM employee ORDER BY age ASC;
SELECT * FROM employee ORDER BY age DESC;
按多列排序:先按照age升序排序,若是年纪相同,则按照id降序
SELECT * from employee
ORDER BY age ASC,
id DESC;
验证多列排序:
SELECT * from employee ORDER BY age ASC,id DESC;
mysql> SELECT * from employee ORDER BY age ASC,id DESC;
+----+------------+--------+-----+------------+-----------------------------------------+--------------+------------+--------+-----------+
| id | name | sex | age | hire_date | post | post_comment | salary | office | depart_id |
+----+------------+--------+-----+------------+-----------------------------------------+--------------+------------+--------+-----------+
| 18 | 程咬铁 | female | 18 | 2014-05-12 | operation | NULL | 17000.00 | 403 | 3 |
| 17 | 程咬铜 | male | 18 | 2015-04-11 | operation | NULL | 18000.00 | 403 | 3 |
| 16 | 程咬银 | female | 18 | 2013-03-11 | operation | NULL | 19000.00 | 403 | 3 |
| 15 | 程咬金 | male | 18 | 1997-03-12 | operation | NULL | 20000.00 | 403 | 3 |
| 12 | 星星 | female | 18 | 2016-05-13 | sale | NULL | 3000.29 | 402 | 2 |
| 11 | 丁丁 | female | 18 | 2011-03-12 | sale | NULL | 1000.37 | 402 | 2 |
| 7 | jinxin | male | 18 | 1900-03-01 | teacher | NULL | 30000.00 | 401 | 1 |
| 6 | jingliyang | female | 18 | 2011-02-11 | teacher | NULL | 9000.00 | 401 | 1 |
| 1 | egon | male | 18 | 2017-03-01 | 老男孩驻沙河办事处外交大使 | NULL | 7300.33 | 401 | 1 |
| 14 | 张野 | male | 28 | 2016-03-11 | operation | NULL | 10000.13 | 403 | 3 |
| 13 | 格格 | female | 28 | 2017-01-27 | sale | NULL | 4000.33 | 402 | 2 |
| 5 | liwenzhou | male | 28 | 2012-11-01 | teacher | NULL | 2100.00 | 401 | 1 |
| 10 | 丫丫 | female | 38 | 2010-11-01 | sale | NULL | 2000.35 | 402 | 2 |
| 9 | 歪歪 | female | 48 | 2015-03-11 | sale | NULL | 3000.13 | 402 | 2 |
| 8 | xiaomage | male | 48 | 2010-11-11 | teacher | NULL | 10000.00 | 401 | 1 |
| 4 | yuanhao | male | 73 | 2014-07-01 | teacher | NULL | 3500.00 | 401 | 1 |
| 2 | alex | male | 78 | 2015-03-02 | teacher | | 1000000.31 | 401 | 1 |
| 3 | wupeiqi | male | 81 | 2013-03-05 | teacher | NULL | 8300.00 | 401 | 1 |
+----+------------+--------+-----+------------+-----------------------------------------+--------------+------------+--------+-----------+
rows in set (0.01 sec)
mysql>
验证多列排序
小练习:
1. 查询全部员工信息,先按照age升序排序,若是age相同则按照hire_date降序排序
2. 查询各岗位平均薪资大于10000的岗位名、平均工资,结果按平均薪资升序排列
3. 查询各岗位平均薪资大于10000的岗位名、平均工资,结果按平均薪资降序排列
# 题目1
select * from employee ORDER BY age asc,hire_date desc;
# 题目2
mysql> select post,avg(salary) from employee group by post having avg(salary) > 10000 order by avg(salary) asc;
+-----------+---------------+
| post | avg(salary) |
+-----------+---------------+
| operation | 16800.026000 |
| teacher | 151842.901429 |
+-----------+---------------+
rows in set (0.00 sec)
# 题目3
mysql> select post,avg(salary) from employee group by post having avg(salary) > 10000 order by avg(salary) desc;
+-----------+---------------+
| post | avg(salary) |
+-----------+---------------+
| teacher | 151842.901429 |
| operation | 16800.026000 |
+-----------+---------------+
rows in set (0.00 sec)
mysql>
小练习答案
(5)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条
小练习:
分页显示,每页5条
# 第1页数据
mysql> select * from employee limit 0,5;
+----+-----------+------+-----+------------+-----------------------------------------+--------------+------------+--------+-----------+
| id | name | sex | age | hire_date | post | post_comment | salary | office | depart_id |
+----+-----------+------+-----+------------+-----------------------------------------+--------------+------------+--------+-----------+
| 1 | egon | male | 18 | 2017-03-01 | 老男孩驻沙河办事处外交大使 | NULL | 7300.33 | 401 | 1 |
| 2 | alex | male | 78 | 2015-03-02 | teacher | | 1000000.31 | 401 | 1 |
| 3 | wupeiqi | male | 81 | 2013-03-05 | teacher | NULL | 8300.00 | 401 | 1 |
| 4 | yuanhao | male | 73 | 2014-07-01 | teacher | NULL | 3500.00 | 401 | 1 |
| 5 | liwenzhou | male | 28 | 2012-11-01 | teacher | NULL | 2100.00 | 401 | 1 |
+----+-----------+------+-----+------------+-----------------------------------------+--------------+------------+--------+-----------+
rows in set (0.00 sec)
# 第2页数据
mysql> select * from employee limit 5,5;
+----+------------+--------+-----+------------+---------+--------------+----------+--------+-----------+
| id | name | sex | age | hire_date | post | post_comment | salary | office | depart_id |
+----+------------+--------+-----+------------+---------+--------------+----------+--------+-----------+
| 6 | jingliyang | female | 18 | 2011-02-11 | teacher | NULL | 9000.00 | 401 | 1 |
| 7 | jinxin | male | 18 | 1900-03-01 | teacher | NULL | 30000.00 | 401 | 1 |
| 8 | xiaomage | male | 48 | 2010-11-11 | teacher | NULL | 10000.00 | 401 | 1 |
| 9 | 歪歪 | female | 48 | 2015-03-11 | sale | NULL | 3000.13 | 402 | 2 |
| 10 | 丫丫 | female | 38 | 2010-11-01 | sale | NULL | 2000.35 | 402 | 2 |
+----+------------+--------+-----+------------+---------+--------------+----------+--------+-----------+
rows in set (0.00 sec)
# 第3页数据
mysql> select * from employee limit 10,5;
+----+-----------+--------+-----+------------+-----------+--------------+----------+--------+-----------+
| id | name | sex | age | hire_date | post | post_comment | salary | office | depart_id |
+----+-----------+--------+-----+------------+-----------+--------------+----------+--------+-----------+
| 11 | 丁丁 | female | 18 | 2011-03-12 | sale | NULL | 1000.37 | 402 | 2 |
| 12 | 星星 | female | 18 | 2016-05-13 | sale | NULL | 3000.29 | 402 | 2 |
| 13 | 格格 | female | 28 | 2017-01-27 | sale | NULL | 4000.33 | 402 | 2 |
| 14 | 张野 | male | 28 | 2016-03-11 | operation | NULL | 10000.13 | 403 | 3 |
| 15 | 程咬金 | male | 18 | 1997-03-12 | operation | NULL | 20000.00 | 403 | 3 |
+----+-----------+--------+-----+------------+-----------+--------------+----------+--------+-----------+
rows in set (0.00 sec)
小练习答案
本节重点:
准备工做:准备两张表,部门表(department)、员工表(employee)
create table department(
id int,
name varchar(20)
);
create table employee(
id int primary key auto_increment,
name varchar(20),
sex enum('male','female') not null default 'male',
age int,
dep_id int
);
#插入数据
insert into department values
(200,'技术'),
(201,'人力资源'),
(202,'销售'),
(203,'运营');
insert into employee(name,sex,age,dep_id) values
('egon','male',18,200),
('alex','female',48,201),
('wupeiqi','male',38,201),
('yuanhao','female',28,202),
('nvshen','male',18,200),
('xiaomage','female',18,204)
;
# 查看表结构和数据
mysql> desc department;
+-------+-------------+------+-----+---------+-------+
| Field | Type | Null | Key | Default | Extra |
+-------+-------------+------+-----+---------+-------+
| id | int(11) | YES | | NULL | |
| name | varchar(20) | YES | | NULL | |
+-------+-------------+------+-----+---------+-------+
2 rows in set (0.19 sec)
mysql> desc employee;
+--------+-----------------------+------+-----+---------+----------------+
| Field | Type | Null | Key | Default | Extra |
+--------+-----------------------+------+-----+---------+----------------+
| id | int(11) | NO | PRI | NULL | auto_increment |
| name | varchar(20) | YES | | NULL | |
| sex | enum('male','female') | NO | | male | |
| age | int(11) | YES | | NULL | |
| dep_id | int(11) | YES | | NULL | |
+--------+-----------------------+------+-----+---------+----------------+
5 rows in set (0.01 sec)
mysql> select * from department;
+------+--------------+
| id | name |
+------+--------------+
| 200 | 技术 |
| 201 | 人力资源 |
| 202 | 销售 |
| 203 | 运营 |
+------+--------------+
4 rows in set (0.02 sec)
mysql> select * from employee;
+----+----------+--------+------+--------+
| 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 | nvshen | male | 18 | 200 |
| 6 | xiaomage | female | 18 | 204 |
+----+----------+--------+------+--------+
6 rows in set (0.00 sec)
ps:观察两张表,发现department表中id=203部门在employee中没有对应的员工,发现employee中id=6的员工在department表中没有对应关系。
1、多表链接查询
两张表的准备工做已完成,好比如今我要查询的员工信息以及该员工所在的部门。从该题中,咱们看出既要查员工又要查该员工的部门,确定要将两张表进行链接查询,多表链接查询。
重点:外连接语法
语法:
SELECT 字段列表
FROM 表1 INNER|LEFT|RIGHT JOIN 表2
ON 表1.字段 = 表2.字段;
(1)先看第一种状况交叉链接:不适用任何匹配条件。生成笛卡尔积(关于笛卡尔积的含义,你们百度自行补脑)。
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 | nvshen | male | 18 | 200 | 200 | 技术 |
| 5 | nvshen | male | 18 | 200 | 201 | 人力资源 |
| 5 | nvshen | male | 18 | 200 | 202 | 销售 |
| 5 | nvshen | male | 18 | 200 | 203 | 运营 |
| 6 | xiaomage | female | 18 | 204 | 200 | 技术 |
| 6 | xiaomage | female | 18 | 204 | 201 | 人力资源 |
| 6 | xiaomage | female | 18 | 204 | 202 | 销售 |
| 6 | xiaomage | female | 18 | 204 | 203 | 运营 |
(2)内链接:只链接匹配的行
#找两张表共有的部分,至关于利用条件从笛卡尔积结果中筛选出了匹配的结果
#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 | nvshen | 18 | male | 技术 |
+----+---------+------+--------+--------------+
5 rows in set (0.00 sec)
#上述sql等同于
mysql> select employee.id,employee.name,employee.age,employee.sex,department.name from employee,department where employee.dep_id=department.id;
(3)外连接之左链接:优先显示左表所有记录
#以左表为准,即找出全部员工信息,固然包括没有部门的员工
#本质就是:在内链接的基础上增长左边有,右边没有的结果
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 | nvshen | 技术 |
| 2 | alex | 人力资源 |
| 3 | wupeiqi | 人力资源 |
| 4 | yuanhao | 销售 |
| 6 | xiaomage | NULL |
+----+----------+--------------+
6 rows in set (0.00 sec)
(4) 外连接之右链接:优先显示右表所有记录
#以右表为准,即找出全部部门信息,包括没有员工的部门
#本质就是:在内链接的基础上增长右边有,左边没有的结果
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 | nvshen | 技术 |
| NULL | NULL | 运营 |
+------+---------+--------------+
6 rows in set (0.00 sec)
(5) 全外链接:显示左右两个表所有记录(了解)
#外链接:在内链接的基础上增长左边有右边没有的和右边有左边没有的结果
#注意:mysql不支持全外链接 full JOIN
#强调:mysql能够使用此种方式间接实现全外链接
语法:select * from employee left join department on employee.dep_id = department.id
union all
select * from employee right join department on employee.dep_id = department.id;
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 | nvshen | male | 18 | 200 | 200 | 技术 |
| 2 | alex | female | 48 | 201 | 201 | 人力资源 |
| 3 | wupeiqi | male | 38 | 201 | 201 | 人力资源 |
| 4 | yuanhao | female | 28 | 202 | 202 | 销售 |
| 6 | xiaomage | female | 18 | 204 | NULL | NULL |
| NULL | NULL | NULL | NULL | NULL | 203 | 运营 |
+------+----------+--------+------+--------+------+--------------+
7 rows in set (0.01 sec)
#注意 union与union all的区别:union会去掉相同的纪录
2、符合条件链接查询
示例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;
3、子查询
#1:子查询是将一个查询语句嵌套在另外一个查询语句中。
#2:内层查询语句的查询结果,能够为外层查询语句提供查询条件。
#3:子查询中能够包含:IN、NOT IN、ANY、ALL、EXISTS 和 NOT EXISTS等关键字
#4:还能够包含比较运算符:= 、 !=、> 、<等
例子:
(1)带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 not in
(select dep_id from employee group by dep_id);
(2)带比较运算符的子查询
#比较运算符:=、!=、>、>=、<、<=、<>
#查询大于全部人平均年龄的员工名与年龄
mysql> select name,age from employee where age > (select avg(age) from employee);
+---------+------+
| name | age |
+---------+------+
| alex | 48 |
| wupeiqi | 38 |
+---------+------+
#查询大于部门内平均年龄的员工名、年龄
思路:
(1)先对员工表(employee)中的人员分组(group by),查询出dep_id以及平均年龄。
(2)将查出的结果做为临时表,再对根据临时表的dep_id和employee的dep_id做为筛选条件将employee表和临时表进行内链接。
(3)最后再将employee员工的年龄是大于平均年龄的员工名字和年龄筛选。
mysql> select t1.name,t1.age from employee as t1
inner join
(select dep_id,avg(age) as avg_age from employee group by dep_id) as t2
on t1.dep_id = t2.dep_id
where t1.age > t2.avg_age;
+------+------+
| name | age |
+------+------+
| alex | 48 |
(3)带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 | nvshen | male | 18 | 200 |
| 6 | xiaomage | 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)
小练习:
查询每一个部门最新入职的那位员工
#建立表
create table employee(
id int not null unique auto_increment,
name varchar(20) not null,
sex enum('male','female') not null default 'male', #大部分是男的
age int(3) unsigned not null default 28,
hire_date date not null,
post varchar(50),
post_comment varchar(100),
salary double(15,2),
office int, #一个部门一个屋子
depart_id int
);
#查看表结构
mysql> desc employee;
+--------------+-----------------------+------+-----+---------+----------------+
| Field | Type | Null | Key | Default | Extra |
+--------------+-----------------------+------+-----+---------+----------------+
| id | int(11) | NO | PRI | NULL | auto_increment |
| name | varchar(20) | NO | | NULL | |
| sex | enum('male','female') | NO | | male | |
| age | int(3) unsigned | NO | | 28 | |
| hire_date | date | NO | | NULL | |
| post | varchar(50) | YES | | NULL | |
| post_comment | varchar(100) | YES | | NULL | |
| salary | double(15,2) | YES | | NULL | |
| office | int(11) | YES | | NULL | |
| depart_id | int(11) | YES | | NULL | |
+--------------+-----------------------+------+-----+---------+----------------+
#插入记录
#三个部门:教学,销售,运营
insert into employee(name,sex,age,hire_date,post,salary,office,depart_id) values
('egon','male',18,'20170301','老男孩驻沙河办事处外交大使',7300.33,401,1), #如下是教学部
('alex','male',78,'20150302','teacher',1000000.31,401,1),
('wupeiqi','male',81,'20130305','teacher',8300,401,1),
('yuanhao','male',73,'20140701','teacher',3500,401,1),
('liwenzhou','male',28,'20121101','teacher',2100,401,1),
('jingliyang','female',18,'20110211','teacher',9000,401,1),
('jinxin','male',18,'19000301','teacher',30000,401,1),
('成龙','male',48,'20101111','teacher',10000,401,1),
('歪歪','female',48,'20150311','sale',3000.13,402,2),#如下是销售部门
('丫丫','female',38,'20101101','sale',2000.35,402,2),
('丁丁','female',18,'20110312','sale',1000.37,402,2),
('星星','female',18,'20160513','sale',3000.29,402,2),
('格格','female',28,'20170127','sale',4000.33,402,2),
('张野','male',28,'20160311','operation',10000.13,403,3), #如下是运营部门
('程咬金','male',18,'19970312','operation',20000,403,3),
('程咬银','female',18,'20130311','operation',19000,403,3),
('程咬铜','male',18,'20150411','operation',18000,403,3),
('程咬铁','female',18,'20140512','operation',17000,403,3)
;
select * from employee as t1
inner join
(select post,max(hire_date) as new_date from employee group by post) as t2
on t1.post=t2.post
where t1.hire_date=t2.new_date;
权限管理
咱们知道咱们的最高权限管理者是root用户,它拥有着最高的权限操做。包括select、update、delete、update、grant等操做。那么通常状况在公司以后DBA工程师会建立一个用户和密码,让你去链接数据库的操做,并给当前的用户设置某个操做的权限(或者全部权限)。那么这时就须要咱们来简单了解一下:
- 如何建立用户和密码
- 给当前的用户受权
- 移除当前用户的权限
若是你想建立一个新的用户,则须要如下操做:
1.进入到mysql数据库下
mysql> use mysql
Database changed
2.对新用户增删改
1.建立用户:
# 指定ip:192.118.1.1的mjj用户登陆
create user 'mjj'@'192.118.1.1' identified by '123';
# 指定ip:192.118.1.开头的mjj用户登陆
create user 'mjj'@'192.118.1.%' identified by '123';
# 指定任何ip的mjj用户登陆
create user 'mjj'@'%' identified by '123';
2.删除用户
drop user '用户名'@'IP地址';
3.修改用户
rename user '用户名'@'IP地址' to '新用户名'@'IP地址';
4.修改密码
set password for '用户名'@'IP地址'=Password('新密码');
3.对当前的用户受权管理
#查看权限
show grants for '用户'@'IP地址'
#受权 mjj用户仅对db1.t1文件有查询、插入和更新的操做
grant select ,insert,update on db1.t1 to "mjj"@'%';
# 表示有全部的权限,除了grant这个命令,这个命令是root才有的。mjj用户对db1下的t1文件有任意操做
grant all privileges on db1.t1 to "mjj"@'%';
#mjj用户对db1数据库中的文件执行任何操做
grant all privileges on db1.* to "mjj"@'%';
#mjj用户对全部数据库中文件有任何操做
grant all privileges on *.* to "mjj"@'%';
#取消权限
# 取消mjj用户对db1的t1文件的任意操做
revoke all on db1.t1 from 'mjj'@"%";
# 取消来自远程服务器的mjj用户对数据库db1的全部表的全部权限
revoke all on db1.* from 'mjj'@"%";
取消来自远程服务器的mjj用户全部数据库的全部的表的权限
revoke all privileges on *.* from 'mjj'@'%';
ps:在公司中,通常状况下是DBA工程师来作这些受权工做。给你一个用户名和密码,你来链接就能够了。
4.MySql备份命令行操做
# 备份:数据表结构+数据
mysqdump -u root db1 > db1.sql -p
# 备份:数据表结构
mysqdump -u root -d db1 > db1.sql -p
#导入现有的数据到某个数据库
#1.先建立一个新的数据库
create database db10;
# 2.将已有的数据库文件导入到db10数据库中
mysqdump -u root -d db10 < db1.sql -p
本节重点:
# PS:在生产环境中操做MySQL数据库仍是推荐使用命令行工具mysql,但在咱们本身开发测试时,能够使用可视化工具Navicat,以图形界面的形式操做MySQL数据库
官网下载:https://www.navicat.com/en/products/navicat-for-mysql
网盘下载:https://pan.baidu.com/s/1bpo5mqj
须要掌握基本的操做
掌握:
#1. 测试+连接数据库
#2. 新建库
#3. 新建表,新增字段+类型+约束
#4. 设计表:外键
#5. 新建查询
#6. 备份库/表
#注意:
批量加注释:ctrl+?键
批量去注释:ctrl+shift+?键
本节重点:
- pymysql的下载和使用
- execute()之sql注入
- 增、删、改:conn.commit()
- 查:fetchone、fetchmany、fetchall
1、pymysql的下载和使用
以前咱们都是经过MySQL自带的命令行客户端工具mysql来操做数据库,那如何在python程序中操做数据库呢?这就用到了pymysql模块,该模块本质就是一个套接字客户端软件,使用前须要事先安装。
(1)pymysql模块的下载
(2)pymysql的使用
数据库和数据都已存在

# 实现:使用Python实现用户登陆,若是用户存在则登陆成功(假设该用户已在数据库中)
import pymysql
user = input('请输入用户名:')
pwd = input('请输入密码:')
# 1.链接
conn = pymysql.connect(host='127.0.0.1', port=3306, user='root', password='', db='db8', charset='utf8')
# 2.建立游标
cursor = conn.cursor()
#注意%s须要加引号
sql = "select * from userinfo where username='%s' and pwd='%s'" %(user, pwd)
print(sql)
# 3.执行sql语句
cursor.execute(sql)
result=cursor.execute(sql) #执行sql语句,返回sql查询成功的记录数目
print(result)
# 关闭链接,游标和链接都要关闭
cursor.close()
conn.close()
if result:
print('登录成功')
else:
print('登陆失败')
2、execute()之sql注入
最后那一个空格,在一条sql语句中若是遇到select * from userinfo where username='mjj' -- asadasdas' and pwd='' 则--以后的条件被注释掉了(注意--后面还有一个空格)
#一、sql注入之:用户存在,绕过密码
mjj' -- 任意字符
#二、sql注入之:用户不存在,绕过用户与密码
xxx' or 1=1 -- 任意字符


解决方法:
# 原来是咱们对sql进行字符串拼接
# sql="select * from userinfo where name='%s' and password='%s'" %(username,pwd)
# print(sql)
# result=cursor.execute(sql)
#改写为(execute帮咱们作字符串拼接,咱们无需且必定不能再为%s加引号了)
sql="select * from userinfo where name=%s and password=%s" #!!!注意%s须要去掉引号,由于pymysql会自动为咱们加上
result=cursor.execute(sql,[user,pwd]) #pymysql模块自动帮咱们解决sql注入的问题,只要咱们按照pymysql的规矩来。
3、增、删、改:conn.commit()
commit()方法:在数据库里增、删、改的时候,必需要进行提交,不然插入的数据不生效。
import pymysql
username = input('请输入用户名:')
pwd = input('请输入密码:')
# 1.链接
conn = pymysql.connect(host='localhost', port=3306, user='root', password='', db='db8', charset='utf8')
# 2.建立游标
cursor = conn.cursor()
# 操做
# 增
# sql = "insert into userinfo(username,pwd) values (%s,%s)"
# effect_row = cursor.execute(sql,(username,pwd))
#同时插入多条数据
#cursor.executemany(sql,[('李四','110'),('王五','119')])
# print(effect_row)#
# 改
# sql = "update userinfo set username = %s where id = 2"
# effect_row = cursor.execute(sql,username)
# print(effect_row)
# 删
sql = "delete from userinfo where id = 2"
effect_row = cursor.execute(sql)
print(effect_row)
#必定记得commit
conn.commit()
# 4.关闭游标
cursor.close()
# 5.关闭链接
conn.close()
4、查:fetchone、fetchmany、fetchall
fetchone():获取下一行数据,第一次为首行;
fetchall():获取全部行数据源
fetchmany(4):获取4行数据
查看一下表内容:
mysql> select * from userinfo;
+----+----------+-----+
| id | username | pwd |
+----+----------+-----+
| 1 | mjj | 123 |
| 3 | 张三 | 110 |
| 4 | 李四 | 119 |
+----+----------+-----+
3 rows in set (0.00 sec)
使用fetchone():
import pymysql
# 1.链接
conn = pymysql.connect(host='localhost', port=3306, user='root', password='', db='db8', charset='utf8')
# 2.建立游标
cursor = conn.cursor()
sql = 'select * from userinfo'
cursor.execute(sql)
# 查询第一行的数据
row = cursor.fetchone()
print(row) # (1, 'mjj', '123')
# 查询第二行数据
row = cursor.fetchone()
print(row) # (3, '张三', '110')
# 4.关闭游标
cursor.close()
# 5.关闭链接
conn.close()
使用fetchall():
import pymysql
# 1.链接
conn = pymysql.connect(host='localhost', port=3306, user='root', password='', db='db8', charset='utf8')
# 2.建立游标
cursor = conn.cursor()
sql = 'select * from userinfo'
cursor.execute(sql)
# 获取全部的数据
rows = cursor.fetchall()
print(rows)
# 4.关闭游标
cursor.close()
# 5.关闭链接
conn.close()
#运行结果
((1, 'mjj', '123'), (3, '张三', '110'), (4, '李四', '119'))
默认状况下,咱们获取到的返回值是元组,只能看到每行的数据,殊不知道每一列表明的是什么,这个时候能够使用如下方式来返回字典,每一行的数据都会生成一个字典:
cursor = conn.cursor(cursor=pymysql.cursors.DictCursor) #在实例化的时候,将属性cursor设置为pymysql.cursors.DictCursor
在fetchone示例中,在获取行数据的时候,能够理解开始的时候,有一个行指针指着第一行的上方,获取一行,它就向下移动一行,因此当行指针到最后一行的时候,就不能再获取到行的内容,因此咱们能够使用以下方法来移动行指针:
cursor.scroll(1,mode='relative') # 相对当前位置移动
cursor.scroll(2,mode='absolute') # 相对绝对位置移动
第一个值为移动的行数,整数为向下移动,负数为向上移动,mode指定了是相对当前位置移动,仍是相对于首行移动
# 1.Python实现用户登陆
# 2.Mysql保存数据
import pymysql
# 1.链接
conn = pymysql.connect(host='localhost', port=3306, user='root', password='', db='db8', charset='utf8')
# 2.建立游标
cursor = conn.cursor(cursor=pymysql.cursors.DictCursor)
sql = 'select * from userinfo'
cursor.execute(sql)
# 查询第一行的数据
row = cursor.fetchone()
print(row) # (1, 'mjj', '123')
# 查询第二行数据
row = cursor.fetchone() # (3, '张三', '110')
print(row)
cursor.scroll(-1,mode='relative') #设置以后,光标相对于当前位置往前移动了一行,因此打印的结果为第二行的数据
row = cursor.fetchone()
print(row)
cursor.scroll(0,mode='absolute') #设置以后,光标相对于首行没有任何变化,因此打印的结果为第一行数据
row = cursor.fetchone()
print(row)
# 4.关闭游标
cursor.close()
# 5.关闭链接
conn.close()
#结果以下
{'id': 1, 'username': 'mjj', 'pwd': '123'}
{'id': 3, 'username': '张三', 'pwd': '110'}
{'id': 3, 'username': '张三', 'pwd': '110'}
{'id': 1, 'username': 'mjj', 'pwd': '123'}
fetchmany():
import pymysql
# 1.链接
conn = pymysql.connect(host='localhost', port=3306, user='root', password='', db='db8', charset='utf8')
# 2.建立游标
cursor = conn.cursor(cursor=pymysql.cursors.DictCursor)
sql = 'select * from userinfo'
cursor.execute(sql)
# 获取2条数据
rows = cursor.fetchmany(2)
print(rows)
# 4.关闭游标
# rows = cursor.fetchall()
# print(rows)
cursor.close()
# 5.关闭链接
conn.close()
#结果以下:
[{'id': 1, 'username': 'mjj', 'pwd': '123'}, {'id': 3, 'username': '张三', 'pwd': '110'}]
1、视图的定义
视图是虚拟表或逻辑表,它被定义为具备链接的SQL SELECT查询语句。由于数据库视图与数据库表相似,它由行和列组成,所以能够根据数据库表查询数据。其内容由查询定义。
可是,视图并不在数据库中以存储的数据值集形式存在,行和列数据来自由定义视图的查询所引用的表,而且在引用视图时动态生成。简单的来讲视图是由其定义结果组成的表;
2、视图的优势
一、数据库视图容许简化复杂查询,经过数据库视图,您只需使用简单的SQL语句,而不是使用具备多个链接的复杂的SQL语句。
二、安全性。通常是这样作的:建立一个视图,定义好该视图所操做的数据。以后将用户权限与视图绑定。这样的方式是使用到了一个特性:grant语句能够针对视图进行授予权限。
3、视图的缺点
一、性能:从数据库视图查询数据可能会很慢,特别是若是视图是基于其余视图建立的。
二、表依赖关系:将根据数据库的基础表建立一个视图。每当更改与其相关联的表的结构时,都必须更改视图。
4、建立视图
语法:
CREATE VIEW 视图名称 AS SQL语句
临时表应用举例:
#两张有关系的表
mysql> select * from course;
+-----+--------+------------+
| cid | cname | teacher_id |
+-----+--------+------------+
| 1 | 生物 | 1 |
| 2 | 物理 | 2 |
| 3 | 体育 | 3 |
| 4 | 美术 | 2 |
+-----+--------+------------+
rows in set (0.00 sec)
mysql> select * from teacher;
+-----+-----------------+
| tid | tname |
+-----+-----------------+
| 1 | 张磊老师 |
| 2 | 李平老师 |
| 3 | 刘海燕老师 |
| 4 | 朱云海老师 |
| 5 | 李杰老师 |
+-----+-----------------+
rows in set (0.00 sec)
#查询李平老师教授的课程名
mysql> select cname from course where teacher_id = (select tid from teacher where tname='李平老师');
+--------+
| cname |
+--------+
| 物理 |
| 美术 |
+--------+
rows in set (0.00 sec)
#子查询出临时表,做为teacher_id等判断依据
select tid from teacher where tname='李平老师'
临时表应用举例
视图的建立:
create view teacher_view as select tid from teacher where tname='李平老师';
#因而查询李平老师教授的课程名的sql能够改写为
mysql> select cname from course where teacher_id = (select tid from teacher_view);
+--------+
| cname |
+--------+
| 物理 |
| 美术 |
+--------+
rows in set (0.00 sec)
5、使用视图
# 往真实表中插入一条数据,查看一下视图,发现视图表也会跟着更新
insert into course(cname,teacher_id) values('张三丰',2);
# 更新一下数据,发现视图的数据也会跟着更新
update course set cname='王五';
不能修改视图的数据,验证为例:
mysql> create view tt as select * from course left join teacher on teacher.tid = course.teacher_id;
Query OK, 0 rows affected (0.02 sec)
mysql> select * from tt;
+-----+--------+------------+------+-----------------+
| cid | cname | teacher_id | tid | tname |
+-----+--------+------------+------+-----------------+
| 1 | 王五 | 1 | 1 | 张磊老师 |
| 2 | 王五 | 2 | 2 | 丽萍老师 |
| 4 | 王五 | 2 | 2 | 丽萍老师 |
| 5 | 王五 | 2 | 2 | 丽萍老师 |
| 6 | 王五 | 2 | 2 | 丽萍老师 |
| 3 | 王五 | 3 | 3 | 王海燕老师 |
+-----+--------+------------+------+-----------------+
6 rows in set (0.01 sec)
mysql> insert into tt values(7,'哈哈',2,4,'张三丰老师');
ERROR 1471 (HY000): The target table tt of the INSERT is not insertable-into
6、修改视图
# 语法:ALTER VIEW 视图名称 AS SQL语句
mysql> alter view teacher_view as select * from course where cid>3;
Query OK, 0 rows affected (0.04 sec)
mysql> select * from teacher_view;
+-----+-------+------------+
| cid | cname | teacher_id |
+-----+-------+------------+
| 4 | xxx | 2 |
| 5 | yyy | 2 |
+-----+-------+------------+
rows in set (0.00 sec)
7、删除视图
# 语法:DROP VIEW 视图名称
DROP VIEW teacher_view
使用触发器能够定制用户对表进行【增、删、改】操做时先后的行为,注意:没有查询
1、建立触发器
# 插入前
CREATE TRIGGER tri_before_insert_tb1 BEFORE INSERT ON tb1 FOR EACH ROW
BEGIN
...
END
# 插入后
CREATE TRIGGER tri_after_insert_tb1 AFTER INSERT ON tb1 FOR EACH ROW
BEGIN
...
END
# 删除前
CREATE TRIGGER tri_before_delete_tb1 BEFORE DELETE ON tb1 FOR EACH ROW
BEGIN
...
END
# 删除后
CREATE TRIGGER tri_after_delete_tb1 AFTER DELETE ON tb1 FOR EACH ROW
BEGIN
...
END
# 更新前
CREATE TRIGGER tri_before_update_tb1 BEFORE UPDATE ON tb1 FOR EACH ROW
BEGIN
...
END
# 更新后
CREATE TRIGGER tri_after_update_tb1 AFTER UPDATE ON tb1 FOR EACH ROW
BEGIN
...
END
例子:用户和日志表。每次建立一个用户以后,就在日志布表中生成这条记录
准备表:
# 建立用户表
create table user(
id int primary key auto_increment,
name varchar(20) not null,
reg_time datetime, # 注册用户的时间
affirm enum('yes','no') # no表示该用户执行失败
);
#建立日志表
create table userLog(
id int primary key auto_increment,
u_name varchar(20) not null,
u_reg_time datetime # 注册用户的时间
);
# 建立触发器 delimiter 默认状况下,delimiter是分号 触发器名称应遵循命名约定
delimiter //
create trigger after_user_insert after insert on user for each row
begin
if new.affirm = 'yes' then
insert into userLog(u_name,u_reg_time) values(new.name,new.reg_time);
end if;
end //
delimiter ;
#往用户表中插入记录,触发触发器,根据if的条件决定是否插入数据
insert into user(name,reg_time,affirm) values ('张三',now(),'yes'),('李四',now(),'yes'),('王五',now(),'no');
# 查看日志表,发现多了两条记录 ,你们应该看到for each row就明白了
mysql> select * from userlog;
+----+--------+---------------------+
| id | u_name | u_reg_time |
+----+--------+---------------------+
| 1 | 张三 | 2018-06-14 17:52:49 |
| 2 | 李四 | 2018-06-14 17:52:49 |
+----+--------+---------------------+
2 rows in set (0.00 sec)[trigger time]_[table name]_[trigger event]
注意:请注意,在为INSERT定义的触发器中,能够仅使用NEW
关键字。不能使用OLD
关键字。可是,在为DELETE
定义的触发器中,没有新行,所以您只能使用OLD
关键字。在UPDATE触发器中,OLD
是指更新前的行,而NEW
是更新后的行
二 使用触发器
触发器没法由用户直接调用,而知因为对表的【增/删/改】操做被动引起的。
三 删除触发器
drop trigger trigger_userLog;
1、数学函数
ROUND(x,y)
返回参数x的四舍五入的有y位小数的值
RAND()
返回0到1内的随机值,能够经过提供一个参数(种子)使RAND()随机数生成器生成一个指定的值。
2、聚合函数(经常使用于GROUP BY从句的SELECT查询中)
AVG(col)返回指定列的平均值
COUNT(col)返回指定列中非NULL值的个数
MIN(col)返回指定列的最小值
MAX(col)返回指定列的最大值
SUM(col)返回指定列的全部值之和
GROUP_CONCAT(col) 返回由属于一组的列值链接组合而成的结果
3、字符串函数
CHAR_LENGTH(str)
返回值为字符串str 的长度,长度的单位为字符。一个多字节字符算做一个单字符。
CONCAT(str1,str2,...)
字符串拼接
若有任何一个参数为NULL ,则返回值为 NULL。
CONCAT_WS(separator,str1,str2,...)
字符串拼接(自定义链接符)
CONCAT_WS()不会忽略任何空字符串。 (然而会忽略全部的 NULL)。
CONV(N,from_base,to_base)
进制转换
例如:
SELECT CONV('a',16,2); 表示将 a 由16进制转换为2进制字符串表示
FORMAT(X,D)
将数字X 的格式写为'#,###,###.##',以四舍五入的方式保留小数点后 D 位, 并将结果以字符串的形式返回。若 D 为 0, 则返回结果不带有小数点,或不含小数部分。
例如:
SELECT FORMAT(12332.1,4); 结果为: '12,332.1000'
INSERT(str,pos,len,newstr)
在str的指定位置插入字符串
pos:要替换位置其实位置
len:替换的长度
newstr:新字符串
特别的:
若是pos超过原字符串长度,则返回原字符串
若是len超过原字符串长度,则由新字符串彻底替换
INSTR(str,substr)
返回字符串 str 中子字符串的第一个出现位置。
LEFT(str,len)
返回字符串str 从开始的len位置的子序列字符。
LOWER(str)
变小写
UPPER(str)
变大写
REVERSE(str)
返回字符串 str ,顺序和字符顺序相反。
SUBSTRING(str,pos) , SUBSTRING(str FROM pos) SUBSTRING(str,pos,len) , SUBSTRING(str FROM pos FOR len)
不带有len 参数的格式从字符串str返回一个子字符串,起始于位置 pos。带有len参数的格式从字符串str返回一个长度同len字符相同的子字符串,起始于位置 pos。 使用 FROM的格式为标准 SQL 语法。也可能对pos使用一个负值。倘若这样,则子字符串的位置起始于字符串结尾的pos 字符,而不是字符串的开头位置。在如下格式的函数中能够对pos 使用一个负值。
mysql> SELECT SUBSTRING('Quadratically',5);
-> 'ratically'
mysql> SELECT SUBSTRING('foobarbar' FROM 4);
-> 'barbar'
mysql> SELECT SUBSTRING('Quadratically',5,6);
-> 'ratica'
mysql> SELECT SUBSTRING('Sakila', -3);
-> 'ila'
mysql> SELECT SUBSTRING('Sakila', -5, 3);
-> 'aki'
mysql> SELECT SUBSTRING('Sakila' FROM -4 FOR 2);
-> 'ki'
4、日期和时间函数
CURDATE()或CURRENT_DATE() 返回当前的日期
CURTIME()或CURRENT_TIME() 返回当前的时间
DAYOFWEEK(date) 返回date所表明的一星期中的第几天(1~7)
DAYOFMONTH(date) 返回date是一个月的第几天(1~31)
DAYOFYEAR(date) 返回date是一年的第几天(1~366)
DAYNAME(date) 返回date的星期名,如:SELECT DAYNAME(CURRENT_DATE);
FROM_UNIXTIME(ts,fmt) 根据指定的fmt格式,格式化UNIX时间戳ts
HOUR(time) 返回time的小时值(0~23)
MINUTE(time) 返回time的分钟值(0~59)
MONTH(date) 返回date的月份值(1~12)
MONTHNAME(date) 返回date的月份名,如:SELECT MONTHNAME(CURRENT_DATE);
NOW() 返回当前的日期和时间
QUARTER(date) 返回date在一年中的季度(1~4),如SELECT QUARTER(CURRENT_DATE);
WEEK(date) 返回日期date为一年中第几周(0~53)
YEAR(date) 返回日期date的年份(1000~9999)
重点:
DATE_FORMAT(date,format) 根据format字符串格式化date值
mysql> SELECT DATE_FORMAT('2009-10-04 22:23:00', '%W %M %Y');
-> 'Sunday October 2009'
mysql> SELECT DATE_FORMAT('2007-10-04 22:23:00', '%H:%i:%s');
-> '22:23:00'
mysql> SELECT DATE_FORMAT('1900-10-04 22:23:00',
-> '%D %y %a %d %m %b %j');
-> '4th 00 Thu 04 10 Oct 277'
mysql> SELECT DATE_FORMAT('1997-10-04 22:23:00',
-> '%H %k %I %r %T %S %w');
-> '22 22 10 10:23:00 PM 22:23:00 00 6'
mysql> SELECT DATE_FORMAT('1999-01-01', '%X %V');
-> '1998 52'
mysql> SELECT DATE_FORMAT('2006-06-00', '%d');
-> '00'
5、加密函数
MD5()
计算字符串str的MD5校验和
PASSWORD(str)
返回字符串str的加密版本,这个加密过程是不可逆转的,和UNIX密码加密过程使用不一样的算法。
6、控制流函数
CASE WHEN[test1] THEN [result1]...ELSE [default] END
若是testN是真,则返回resultN,不然返回default
CASE [test] WHEN[val1] THEN [result]...ELSE [default]END
若是test和valN相等,则返回resultN,不然返回default
IF(test,t,f)
若是test是真,返回t;不然返回f
IFNULL(arg1,arg2)
若是arg1不是空,返回arg1,不然返回arg2
NULLIF(arg1,arg2)
若是arg1=arg2返回NULL;不然返回arg1
内置函数
更多函数:中文猛击这里 OR 官方猛击这里
掌握内置函数中的时间格式化DATE_FORMAT()的用法
官网示例:
mysql> SELECT DATE_FORMAT('2009-10-04 22:23:00', '%W %M %Y');
-> 'Sunday October 2009'
mysql> SELECT DATE_FORMAT('2007-10-04 22:23:00', '%H:%i:%s');
-> '22:23:00'
mysql> SELECT DATE_FORMAT('1900-10-04 22:23:00',
-> '%D %y %a %d %m %b %j');
-> '4th 00 Thu 04 10 Oct 277'
mysql> SELECT DATE_FORMAT('1997-10-04 22:23:00',
-> '%H %k %I %r %T %S %w');
-> '22 22 10 10:23:00 PM 22:23:00 00 6'
mysql> SELECT DATE_FORMAT('1999-01-01', '%X %V');
-> '1998 52'
mysql> SELECT DATE_FORMAT('2006-06-00', '%d');
-> '00'
举例:
# 一、博客园 时间处理
CREATE TABLE blog (
id INT PRIMARY KEY auto_increment,
NAME CHAR (32),
sub_time datetime
);
#二、插入数据
INSERT INTO blog (NAME, sub_time)
VALUES
('第1篇','2015-03-01 11:31:21'),
('第2篇','2015-03-11 16:31:21'),
('第3篇','2016-07-01 10:21:31'),
('第4篇','2016-07-22 09:23:21'),
('第5篇','2016-07-23 10:11:11'),
('第6篇','2016-07-25 11:21:31'),
('第7篇','2017-03-01 15:33:21'),
('第8篇','2017-03-01 17:32:21'),
('第9篇','2017-03-01 18:31:21');
#3. 提取sub_time字段的值,按照格式后的结果即"年月"来分组
SELECT DATE_FORMAT(sub_time,'%Y-%m') as y_m,COUNT(1) as num FROM blog GROUP BY DATE_FORMAT(sub_time,'%Y-%m');
# 结果:
+---------+-----+
| y_m | num |
+---------+-----+
| 2015-03 | 2 |
| 2016-07 | 4 |
| 2017-03 | 3 |
+---------+-----+
3 rows in set (0.00 sec)
1、存储过程的定义
存储过程是存储在数据库目录中的一坨的声明性SQL语句。
Java,Python,PHP等应用程序能够调用存储过程。
MySQL是最受欢迎的开源RDBMS,被社区和企业普遍使用。 然而,在它发布的第一个十年期间,它不支持存储过程,存储函数,触发器和事件。自从MySQL 5.0版本以来,这些功能被添加到MySQL数据库引擎,使其更加灵活和强大。
2、存储过程的优势
#一、一般存储过程有助于提升应用程序的性能。当建立,存储过程被编译以后,就存储在数据库中。 可是,MySQL实现的存储过程略有不一样。 MySQL存储过程按需编译。 在编译存储过程以后,MySQL将其放入缓存中。 MySQL为每一个链接维护本身的存储过程高速缓存。 若是应用程序在单个链接中屡次使用存储过程,则使用编译版本,不然存储过程的工做方式相似于查询。
# 二、存储过程有助于减小应用程序和数据库服务器之间的流量,由于应用程序没必要发送多个冗长的SQL语句,而只能发送存储过程的名称和参数。
#三、存储的程序对任何应用程序都是可重用的和透明的。 存储过程将数据库接口暴露给全部应用程序,以便开发人员没必要开发存储过程当中已支持的功能。
#四、存储的程序是安全的。 数据库管理员能够向访问数据库中存储过程的应用程序授予适当的权限,而不向基础数据库表提供任何权限。
除了这些优势以外,存储过程有其自身的缺点,在数据库中使用它们以前,您应该注意这些缺点。
3、存储过程的缺点
#一、若是使用大量存储过程,那么使用这些存储过程的每一个链接的内存使用量将会大大增长。 此外,若是您在存储过程当中过分使用大量逻辑操做,则CPU使用率也会增长,由于数据库服务器的设计不当于逻辑运算。
#二、存储过程的构造使得开发具备复杂业务逻辑的存储过程变得更加困难。
#三、很难调试存储过程。只有少数数据库管理系统容许您调试存储过程。不幸的是,MySQL不提供调试存储过程的功能。
#四、开发和维护存储过程并不容易。开发和维护存储过程一般须要一个不是全部应用程序开发人员拥有的专业技能。这可能会致使应用程序开发和维护阶段的问题。
MySQL存储过程有本身的优势和缺点。开发应用程序时,您应该决定是否应该或不该该根据业务需求使用存储过程。
4、一个简单的Mysql存储过程示例
delimiter //
create procedure b1()
begin
select * from blog;
end //
delimiter ;
解释:
1.第一个命令是delimiter //,它与存储过程语法无关。 delimter语句将标准分隔符 - 分号(;)更改成://。 在这种状况下,分隔符从分号(;)更改成双斜杠//。为何咱们必须更改分隔符? 由于咱们想将存储过程做为总体传递给服务器,而不是让mysql工具一次解释每一个语句。 在END关键字以后,使用分隔符//来指示存储过程的结束。 最后一个命令(DELIMITER;)将分隔符更改回分号(;)。
2.使用create procedure语句建立一个新的存储过程。在create procedure语句以后指定存储过程的名称。在这个示例中,存储过程的名称为:b1,并把括号放在存储过程的名字以后。
3.begin和end之间的部分称为存储过程的主体。将声明性SQL语句放在主体中以处理业务逻辑。 在这个存储过程当中,咱们使用一个简单的select语句来查询blog表中的数据。
# mysql中调用存储过程
call b1()
#在python中基于pymysql调用
cursor.callproc('b1')
print(cursor.fetchall())
5、声明变量
要在存储过程当中声明变量,能够使用delclare语句,以下
DECLARE variable_name datatype(size) DEFAULT default_value;
下面来更详细地解释上面的语句:
首先,在DECLARE关键字后面要指定变量名。变量名必须遵循MySQL表列名称的命名规则。
其次,指定变量的数据类型及其大小。变量能够有任何MySQL数据类型,如INT,VARCHAR,DATETIME等。
第三,当声明一个变量时,它的初始值为NULL。可是能够使用DEFAULT关键字为变量分配默认值。
实现:
delimiter //
create procedure b2()
begin
DECLARE n int DEFAULT 1;
set n = 5;
select * from blog where id = n;
end //
delimiter ;
# mysql中调用存储过程
call b2();
6、存储过程传参
在现实应用中,开发的存储过程几乎都须要参数。这些参数使存储过程更加灵活和有用。 在MySQL中,参数有三种模式:IN,OUT或INOUT。
IN - 是默认模式。在存储过程当中定义IN参数时,调用程序必须将参数传递给存储过程。 另外,IN参数的值被保护。这意味着即便在存储过程当中更改了IN参数的值,在存储过程结束后仍保留其原始值。换句话说,存储过程只使用IN参数的副本。
OUT - 能够在存储过程当中更改OUT参数的值,并将其更改后新值传递回调用程序。请注意,存储过程在启动时没法访问OUT参数的初始值。
INOUT - INOUT参数是IN和OUT参数的组合。这意味着调用程序能够传递参数,而且存储过程能够修改INOUT参数并将新值传递回调用程序。
在存储过程当中定义参数的语法以下:
MODE param_name param_type(param_size)
根据存储过程当中参数的目的,MODE能够是IN,OUT或INOUT。
param_name是参数的名称。参数的名称必须遵循MySQL中列名的命名规则。
在参数名以后是它的数据类型和大小。和变量同样,参数的数据类型能够是任何有效的MySQL数据类型
ps:若是存储过程有多个参数,则每一个参数由逗号(,)分隔。
# 1.in
delimiter //
create procedure b3(
in blogName varchar(30)
)
begin
select * from blog where NAME = blogName;
end //
delimiter ;
#mysql中调用存储过程
call b3('第5篇');
#python中调用存储过程
cursor.callproc('b3',args = ('第5篇'));
# 2.out
delimiter //
create procedure b4(
in year int,
out count int
)
begin
SELECT COUNT(1) into count FROM blog GROUP BY DATE_FORMAT(sub_time,'%Y') having max(DATE_FORMAT(sub_time,'%Y')) = year ;
set count = 6;
end //
delimiter ;
call b4(2016,@count);
select @count;
#out只能当返回值
# 3.inout
delimiter //
create procedure b5(
inout n1 int
)
begin
select * from blog where id > n1;
end //
delimiter ;
#mysql中调用
set @n = 3;
call b5(@n);
select @n;
#在python中基于pymysql调用
cursor.callproc('b5',(4))
print(cursor.fetchall()) #查询select的查询结果
cursor.execute('select @n1')
print(cursor.fetchall())
# inout:既能够传入又能够返回
事务用于将某些操做的多个SQL做为原子性操做,一旦有某一个出现错误,便可回滚到原来的状态,从而保证数据库数据完整性。
举例说明:
create table user2(
id int primary key auto_increment,
name char(32),
balance int
);
insert into user2(name,balance)
values
('wsb',1000),
('egon',1000),
('ysb',1000);
#原子操做
start transaction;
update user2 set balance=900 where name='wsb'; #买支付100元
update user2 set balance=1010 where name='egon'; #中介拿走10元
update user2 set balance=1090 where name='ysb'; #卖家拿到90元
commit;
#出现异常,回滚到初始状态
start transaction;
update user2 set balance=900 where name='wsb'; #买支付100元
update user2 set balance=1010 where name='egon'; #中介拿走10元
uppdate user2 set balance=1090 where name='ysb'; #卖家拿到90元,出现异常没有拿到
rollback;
mysql> select * from user;
+----+------+---------+
| id | name | balance |
+----+------+---------+
| 1 | wsb | 1000 |
| 2 | egon | 1000 |
| 3 | ysb | 1000 |
+----+------+---------+
rows in set (0.00 sec)
下面是操做:当p_return_code为1时,表示异常,立马回滚。当为2时,出现警告,立马回滚原始状态。0表示成功
delimiter //
create PROCEDURE b6(
OUT p_return_code tinyint
)
BEGIN
DECLARE exit handler for sqlexception
BEGIN
-- ERROR
set p_return_code = 1;
rollback;
END;
DECLARE exit handler for sqlwarning
BEGIN
-- WARNING
set p_return_code = 2;
rollback;
END;
START TRANSACTION;
insert into blog(name,sub_time) values('yyy',now());
COMMIT;
-- SUCCESS
set p_return_code = 0; #0表明执行成功
END //
delimiter ;
set @res=123;
call b6(@res);
select @res;
1、索引的介绍
数据库中专门用于帮助用户快速查找数据的一种数据结构。相似于字典中的目录,查找字典内容时能够根据目录查找到数据的存放位置吗,而后直接获取。
二 、索引的做用
3、常见的几种索引:
- 普通索引
- 惟一索引
- 主键索引
- 联合索引(多列)
- 联合主键索引
- 联合惟一索引
- 联合普通索引
无索引: 从前日后一条一条查询
有索引:建立索引的本质,就是建立额外的文件(某种格式存储,查询的时候,先去格外的文件找,定好位置,而后再去原始表中直接查询。可是建立索引越多,会对硬盘也是有损耗。
创建索引的目的:
a.额外的文件保存特殊的数据结构
b.查询快,可是插入更新删除依然慢
c.建立索引以后,必须命中索引才能有效
无索引和有索引的区别以及创建索引的目的
hash索引和BTree索引
(1)hash类型的索引:查询单条快,范围查询慢
(2)btree类型的索引:b+树,层数越多,数据量指数级增加(咱们就用它,由于innodb默认支持它)
3.1 普通索引
做用:仅有一个加速查找
create table userinfo(
nid int not null auto_increment primary key,
name varchar(32) not null,
email varchar(64) not null,
index ix_name(name)
);
建立表+普通索引
create index 索引的名字 on 表名(列名)
3.2 惟一索引
惟一索引有两个功能:加速查找和惟一约束(可含null)
create table userinfo(
id int not null auto_increment primary key,
name varchar(32) not null,
email varchar(64) not null,
unique index ix_name(name)
);
建立表+惟一索引
create unique index 索引名 on 表名(列名)
3.3 主键索引
主键索引有两个功能: 加速查找和惟一约束(不含null)
create table userinfo(
id int not null auto_increment primary key,
name varchar(32) not null,
email varchar(64) not null,
unique index ix_name(name)
)
or
create table userinfo(
id int not null auto_increment,
name varchar(32) not null,
email varchar(64) not null,
primary key(nid),
unique index ix_name(name)
)
建立表+主键索引
alter table 表名 add primary key(列名);
alter table 表名 drop primary key;
alter table 表名 modify 列名 int, drop primary key;
3.4 组合索引
组合索引是将n个列组合成一个索引
其应用场景为:频繁的同时使用n列来进行查询,如:where name = 'alex' and email = 'alex@qq.com'。
create index 索引名 on 表名(列名1,列名2);
4、索引名词
#覆盖索引:在索引文件中直接获取数据
例如:
select name from userinfo where name = 'alex50000';
#索引合并:把多个单列索引合并成使用
例如:
select * from userinfo where name = 'alex13131' and id = 13131;
6、正确使用索引的状况
数据库表中添加索引后确实会让查询速度起飞,但前提必须是正确的使用索引来查询,若是以错误的方式使用,则即便创建索引也会不奏效。
使用索引,咱们必须知道:
(1)建立索引
(2)命中索引
(3)正确使用索引
准备:
#1. 准备表
create table userinfo(
id int,
name varchar(20),
gender char(6),
email varchar(50)
);
#2. 建立存储过程,实现批量插入记录
delimiter $$ #声明存储过程的结束符号为$$
create procedure auto_insert1()
BEGIN
declare i int default 1;
while(i<3000000)do
insert into userinfo values(i,concat('alex',i),'male',concat('egon',i,'@oldboy'));
set i=i+1;
end while;
END$$ #$$结束
delimiter ; #从新声明分号为结束符号
#3. 查看存储过程
show create procedure auto_insert1\G
#4. 调用存储过程
call auto_insert1();
准备300w条数据
测试:
- like '%xx'
select * from userinfo where name like '%al';
- 使用函数
select * from userinfo where reverse(name) = 'alex333';
- or
select * from userinfo where id = 1 or email = 'alex122@oldbody';
特别的:当or条件中有未创建索引的列才失效,如下会走索引
select * from userinfo where id = 1 or name = 'alex1222';
select * from userinfo where id = 1 or email = 'alex122@oldbody' and name = 'alex112'
- 类型不一致
若是列是字符串类型,传入条件是必须用引号引发来,否则...
select * from userinfo where name = 999;
- !=
select count(*) from userinfo where name != 'alex'
特别的:若是是主键,则仍是会走索引
select count(*) from userinfo where id != 123
- >
select * from userinfo where name > 'alex'
特别的:若是是主键或索引是整数类型,则仍是会走索引
select * from userinfo where id > 123
select * from userinfo where num > 123
- order by
select email from userinfo order by name desc;
当根据索引排序时候,选择的映射若是不是索引,则不走索引
特别的:若是对主键排序,则仍是走索引:
select * from userinfo order by nid desc;
- 组合索引最左前缀
若是组合索引为:(name,email)
name and email -- 使用索引
name -- 使用索引
email -- 不使用索引
什么是最左前缀呢?
最左前缀匹配:
create index ix_name_email on userinfo(name,email);
select * from userinfo where name = 'alex';
select * from userinfo where name = 'alex' and email='alex@oldBody';
select * from userinfo where email='alex@oldBody';
若是使用组合索引如上,name和email组合索引以后,查询
(1)name和email ---使用索引
(2)name ---使用索引
(3)email ---不适用索引
对于同时搜索n个条件时,组合索引的性能好于多个单列索引
******组合索引的性能>索引合并的性能*********
7、索引的注意事项
(1)避免使用select *
(2)count(1)或count(列) 代替count(*)
(3)建立表时尽可能使用char代替varchar
(4)表的字段顺序固定长度的字段优先
(5)组合索引代替多个单列索引(常用多个条件查询时)
(6)尽可能使用短索引 (create index ix_title on tb(title(16));特殊的数据类型 text类型)
(7)使用链接(join)来代替子查询
(8)连表时注意条件类型需一致
(9)索引散列(重复少)不适用于建索引,例如:性别不合适
8、执行计划
explain + 查询SQL - 用于显示SQL执行信息参数,根据参考信息能够进行SQL优化
mysql> explain select * from userinfo;
+----+-------------+----------+------+---------------+------+---------+------+---------+-------+
| id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra |
+----+-------------+----------+------+---------------+------+---------+------+---------+-------+
| 1 | SIMPLE | userinfo | ALL | NULL | NULL | NULL | NULL | 2973016 | NULL |
+----+-------------+----------+------+---------------+------+---------+------+---------+-------+
mysql> explain select * from (select id,name from userinfo where id <20) as A;
+----+-------------+------------+-------+---------------+---------+---------+------+------+-------------+
| id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra |
+----+-------------+------------+-------+---------------+---------+---------+------+------+-------------+
| 1 | PRIMARY | <derived2> | ALL | NULL | NULL | NULL | NULL | 19 | NULL |
| 2 | DERIVED | userinfo | range | PRIMARY | PRIMARY | 4 | NULL | 19 | Using where |
+----+-------------+------------+-------+---------------+---------+---------+------+------+-------------+
2 rows in set (0.05 sec)
参数说明:
select_type:
查询类型
SIMPLE 简单查询
PRIMARY 最外层查询
SUBQUERY 映射为子查询
DERIVED 子查询
UNION 联合
UNION RESULT 使用联合的结果
table:
正在访问的表名
type:
查询时的访问方式,性能:all < index < range < index_merge < ref_or_null < ref < eq_ref < system/const
ALL 全表扫描,对于数据表从头至尾找一遍
select * from userinfo;
特别的:若是有limit限制,则找到以后就不在继续向下扫描
select * from userinfo where email = 'alex112@oldboy'
select * from userinfo where email = 'alex112@oldboy' limit 1;
虽然上述两个语句都会进行全表扫描,第二句使用了limit,则找到一个后就再也不继续扫描。
INDEX : 全索引扫描,对索引从头至尾找一遍
select nid from userinfo;
RANGE: 对索引列进行范围查找
select * from userinfo where name < 'alex';
PS:
between and
in
> >= < <= 操做
注意:!= 和 > 符号
INDEX_MERGE: 合并索引,使用多个单列索引搜索
select * from userinfo where name = 'alex' or nid in (11,22,33);
REF: 根据索引查找一个或多个值
select * from userinfo where name = 'alex112';
EQ_REF: 链接时使用primary key 或 unique类型
select userinfo2.id,userinfo.name from userinfo2 left join tuserinfo on userinfo2.id = userinfo.id;
CONST:常量
表最多有一个匹配行,由于仅有一行,在这行的列值可被优化器剩余部分认为是常数,const表很快,由于它们只读取一次。
select id from userinfo where id = 2 ;
SYSTEM:系统
表仅有一行(=系统表)。这是const联接类型的一个特例。
select * from (select id from userinfo where id = 1) as A;
possible_keys:可能使用的索引
key:真实使用的
key_len: MySQL中使用索引字节长度
rows: mysql估计为了找到所需的行而要读取的行数 ------ 只是预估值
extra:
该列包含MySQL解决查询的详细信息
“Using index”
此值表示mysql将使用覆盖索引,以免访问表。不要把覆盖索引和index访问类型弄混了。
“Using where”
这意味着mysql服务器将在存储引擎检索行后再进行过滤,许多where条件里涉及索引中的列,当(而且若是)它读取索引时,就能被存储引擎检验,所以不是全部带where子句的查询都会显示“Using where”。有时“Using where”的出现就是一个暗示:查询可受益于不一样的索引。
“Using temporary”
这意味着mysql在对查询结果排序时会使用一个临时表。
“Using filesort”
这意味着mysql会对结果使用一个外部索引排序,而不是按索引次序从表里读取行。mysql有两种文件排序算法,这两种排序方式均可以在内存或者磁盘上完成,explain不会告诉你mysql将使用哪种文件排序,也不会告诉你排序会在内存里仍是磁盘上完成。
“Range checked for each record(index map: N)”
这个意味着没有好用的索引,新的索引将在联接的每一行上从新估算,N是显示在possible_keys列中索引的位图,而且是冗余的
9、慢日志记录
开启慢查询日志,可让MySQL记录下查询超过指定时间的语句,经过定位分析性能的瓶颈,才能更好的优化数据库系统的性能。
(1) 进入MySql 查询是否开了慢查询
show variables like 'slow_query%';
参数解释:
slow_query_log 慢查询开启状态 OFF 未开启 ON 为开启
slow_query_log_file 慢查询日志存放的位置(这个目录须要MySQL的运行账号的可写权限,通常设置为MySQL的数据存放目录)
(2)查看慢查询超时时间
show variables like 'long%';
ong_query_time 查询超过多少秒才记录 默认10秒
(3)开启慢日志(1)(是否开启慢查询日志,1表示开启,0表示关闭。)
set global slow_query_log=1;
(4)再次查看
show variables like '%slow_query_log%';
(5)开启慢日志(2):(推荐)
在my.cnf 文件中
找到[mysqld]下面添加:
slow_query_log =1
slow_query_log_file=C:\mysql-5.6.40-winx64\data\localhost-slow.log
long_query_time = 1
参数说明:
slow_query_log 慢查询开启状态 1 为开启
slow_query_log_file 慢查询日志存放的位置
long_query_time 查询超过多少秒才记录 默认10秒 修改成1秒
10、分页性能相关方案
先回顾一下,如何取当前表中的前10条记录,每十条取一次.......
第1页:
select * from userinfo limit 0,10;
第2页:
select * from userinfo limit 10,10;
第3页:
select * from userinfo limit 20,10;
第4页:
select * from userinfo limit 30,10;
......
第2000010页
select * from userinfo limit 2000000,10;
PS:会发现,越日后查询,须要的时间约长,是由于越日后查,全文扫描查询,会去数据表中扫描查询。
最优的解决方案
(1)只有上一页和下一页
作一个记录:记录当前页的最大id或最小id
下一页:
select * from userinfo where id>max_id limit 10;
上一页:
select * from userinfo where id<min_id order by id desc limit 10;
(2) 中间有页码的状况
select * from userinfo where id in(
select id from (select * from userinfo where id > pre_max_id limit (cur_max_id-pre_max_id)*10) as A order by A.id desc limit 10
);