1.引言
1.1.为什么选择MySQL
MySQL有什么特点?
1、可以处理拥有上千万条记录的中小型数据
2、使用标准的SQL数据语言形式
3、可移植行高,安装简单小巧
4、调试、管理,优化简单(相对其他大型数据库)
5、开源(分为商业版和社区版)
目前数据库排名:
2.初涉MySQL
2.1.MySQL概述
1.MySQL是一个关系型数据库管理系统,由瑞典MySQL AB 公司开发,目前属于 Oracle 旗下产品,是最流行的关系型数据库管理系统之一。
2.MySQL是一种关系数据库管理系统,关系数据库就是将数据保存在不同的表中,而不是将所有数据放在一个大仓库内,这样就增加了数据的访问速度和灵活性。
3.MySQL 软件采用了双授权政策,分为社区版和商业版,由于其体积小、速度快、总体拥有成本低,尤其是开放源码这一特点,一般中小型网站的开发都选择 MySQL 作为网站数据库,由于其社区版的性能卓越,搭配 PHP 和 Apache 可组成良好的开发环境。
2.2.MySQL的安装配置
2.2.1.MySQL的安装
2.2.1.1.linux安装方法一
1.配置 yum 源
去 MySQL 官网下载 YUM 的 RPM 安装包,http://dev.mysql.com/downloads/repo/yum/
下载 mysql 源安装包:
$ curl -LO wget https://repo.mysql.com//mysql80-community-release-el7-3.noarch.rpm
安装 mysql 源
$ sudo yum localinstall mysql80-community-release-el7-3.noarch.rpm
检查 yum 源是否安装成功
$ sudo yum repolist enabled | grep "mysql.*-community.*"
mysql-connectors-community MySQL Connectors Community 21
mysql-tools-community MySQL Tools Community 38
mysql80-community MySQL 5.7 Community Server 130
如上所示,找到了 mysql 的安装包
2.安装
$ sudo yum install mysql-community-server
3.启动
安装服务
$ sudo systemctl enable mysqld
启动服务
$ sudo systemctl start mysqld
查看服务状态
$ sudo systemctl status mysqld
4.修改 root 默认密码
MySQL 5.7 启动后,在 /var/log/mysqld.log 文件中给 root 生成了一个默认密码。通过下面的方式找到 root 默认密码,然后登录 mysql 进行修改:
$ grep ‘temporary password‘ /var/log/mysqld.log
[Note] A temporary password is generated for [email protected]: **********
登录 MySQL 并修改密码上面找出的密码
$ mysql -u root -p
Enter password:
修改密码,IDENTIFIED BY 中输入新的密码
mysql> ALTER USER ‘root‘@‘localhost‘ IDENTIFIED BY ‘MyNewPass4!‘;
注意:MySQL 5.7 默认安装了密码安全检查插件(validate_password),默认密码检查策略要求密码必须包含:大小写字母、数字和特殊符号,并且长度不能少于 8 位。
通过 MySQL 环境变量可以查看密码策略的相关信息:
mysql> SHOW VARIABLES LIKE ‘validate_password%‘;
+--------------------------------------+--------+
| Variable_name | Value |
+--------------------------------------+--------+
| validate_password_check_user_name | OFF |
| validate_password_dictionary_file | |
| validate_password_length | 8 |
| validate_password_mixed_case_count | 1 |
| validate_password_number_count | 1 |
| validate_password_policy | MEDIUM |
| validate_password_special_char_count | 1 |
+--------------------------------------+--------+
7 rows in set (0.01 sec)
$ sudo vi /etc/my.cnf
[mysqld]
# 指定密码校验策略,这样我们就可以将密码不用按:大小写字母、数字和特殊符号,并且长度不能少于 8 位的方式设置复杂的密码了
# 添加如下键值对, 0=LOW, 1=MEDIUM, 2=STRONG
validate_password_policy=0
[mysqld]
重启 MySQL 服务,使配置生效
$ sudo systemctl restart mysqld
5.添加远程登录用户
MySQL 默认只允许 root 帐户在本地登录,如果要在其它机器上连接 MySQL,必须修改 root 允许远程连接,或者添加一个允许远程连接的帐户,为了安全起见,本例添加一个新的帐户admin,密码为:secret:
mysql> GRANT ALL PRIVILEGES ON *.* TO ‘admin‘@‘%‘ IDENTIFIED BY ‘secret‘ WITH GRANT OPTION;
6.配置默认编码为 utf8,MySQL 默认为 latin1, 一般修改为 UTF-8
$ vi /etc/my.cnf
[mysqld]
# 在myslqd下添加如下键值对
character_set_server=utf8
init_connect=‘SET NAMES utf8‘
重启 MySQL 服务,使配置生效
$ sudo systemctl restart mysqld
查看字符集
mysql> SHOW VARIABLES LIKE ‘character%‘;
+--------------------------+----------------------------+
| Variable_name | Value |
+--------------------------+----------------------------+
| character_set_client | utf8 |
| character_set_connection | utf8 |
| character_set_database | utf8 |
| character_set_filesystem | binary |
| character_set_results | utf8 |
| character_set_server | utf8 |
| character_set_system | utf8 |
| character_sets_dir | /usr/share/mysql/charsets/ |
+--------------------------+----------------------------+
8 rows in set (0.00 sec)
7.开启端口
$ sudo firewall-cmd --zone=public --add-port=3306/tcp --permanent
$ sudo firewall-cmd --reload
2.2.1.2.linux安装方法二
1.安装前我们需要先去官网下载 Yum 资源包,下载地址为:
https://dev.mysql.com/downloads/repo/yum/
我使用的是centos7,所以使用第一个:
wget https://repo.mysql.com//mysql80-community-release-el7-3.noarch.rpm
2.rpm -ivh mysql80-community-release-el7-3.noarch.rpm
3.yum update输入Y后进行下载
4.yum install mysql-server输入Y进行下载
权限设置:
chown mysql:mysql -R /var/lib/mysql
初始化 MySQL:
mysqld --initialize
启动 MySQL:
systemctl start mysqld
查看 MySQL 运行状态:
systemctl status mysqld
配置的方式如同方式一,这里不再做讲解
2.2.1.3.linux安装方法三
1系统约定
安装文件下载目录:/data/software
Mysql目录安装位置:/usr/local/mysql
数据库保存位置:/data/mysql
日志保存位置:/data/log/mysql
2下载mysql
在官网:http://dev.mysql.com/downloads/mysql/ 中,选择以下版本的mysql下载:
执行如下命名:
#mkdir -p /data/software
#cd /data/software
下载安装包:
#wget http://dev.mysql.com/get/Downloads/MySQL-5.7/mysql-5.7.17-linux-glibc2.5-x86_64.tar.gz
或者在主机上下载了,与虚拟机进行共享,virtual box虚拟机与主机进行文件共享具体的方法参考:
3解压压缩包到目标位置
#cd /data/software
--解压压缩包
#tar -xzvf /data/software/mysql-5.7.17-linux-glibc2.5-x86_64.tar.gz
--移动并修改文件名
#mv /data/software/mysql-5.7.17-linux-glibc2.5-x86_64 /usr/local/mysql
4创建数据仓库目录
--/data/mysql 数据仓库目录
# mkdir /data/mysql
#ls /data/
5新建mysql用户、组及目录
# ---新建一个msyql组
# useradd -r -s /sbin/nologin -g mysql mysql -d /usr/local/mysql ---新建msyql用户禁止登录shell
6改变目录属有者
#cd /usr/local/mysql
#pwd
#chown -R mysql .
#chgrp -R mysql .
#chown -R mysql /data/mysql
7配置参数
# bin/mysqld --initialize --user=mysql --basedir=/usr/local/mysql --datadir=/data/mysql
此处需要注意记录生成的临时密码,如上文结尾处的:YLi>7ecpe;YP
#bin/mysql_ssl_rsa_setup --datadir=/data/mysql
8修改系统配置文件
#cd /usr/local/mysql/support-files
# cp my-default.cnf /etc/my.cnf 【这一个实际操作中没有用到,因为我上面/usr/local/mysql/support-files文件下没有my-default.cnf文件】
# cp mysql.server /etc/init.d/mysql
# vim /etc/init.d/mysql
修改以下内容:
9启动mysql
# /etc/init.d/mysql start
--登陆
# mysql -hlocalhost -uroot -p
--如果出现:-bash: mysql: command not found
--就执行: # ln -s /usr/local/mysql/bin/mysql /usr/bin --没有出现就不用执行
--输入第6步生成的临时密码
--修改密码
mysql> set password=password(‘123456’);
--需要注意的是,新版的MySQL不支持password函数,所以需要使用这两种方式:
alter user ‘root‘@‘localhost‘ identified by ‘123456‘; 或
set password for ‘root‘@‘localhost‘=password(‘123456‘);
--设置root账户的host地址(修改了才可以远程连接)
mysql>grant all privileges on *.* to ‘root‘@‘%‘ identified by ‘123456’;
mysql>flush privileges;
--查看表
mysql> use mysql;
mysql> select host,user from user;
10添加系统路径
# vim /etc/profile
添加:
export PATH=/usr/local/mysql/bin:$PATH
如下:
# source /etc/profile
11配置mysql自动启动
# chmod 755 /etc/init.d/mysql
# chkconfig --add mysql
# chkconfig --level 345 mysql on
这样我们就可以直接使用mysql -uroot -p的方式登录了
2.2.1.4.Windows安装方法一:MySQL的安装包安装
以安装包的方式安装更像是一种傻瓜式的安装方式,所以我们可以不用再额外的进行环境变量的配置和MySQL的配置,只要我们在安装过程中勾选相应的选项,MySQL会自动帮我们进行基本的配置。
1.输入https://www.mysql.com/downloads/地址进入MySQL官网,点击Community(GPL) Downloads
2.选择MySQL on Windows(Installer & Tools)后点击DOWNLOAD
3.选择MySQL Installer
4.选择mysql-installer-community-8.0.11.0.msi后点击下载。
(这里mysql-installer-community-8.0.11.0.msi是可以运行在无网络的环境下,而mysql-installer-web-community-8.0.11.0.msi只能运行在有网络的)
5.然后运行下载好的安装包,一路点击NEXT即可完成安装,在开始菜单里可以找到MySQL Workbench 6.3 CE,打开可进入mysql。
6.我们也可以使用cmd命令提示符打开MySQL,环境变量的配置,默认是安装在C:\Program Files下的,将C:\Program Files\MySQL\MySQL Server 5.7\bin添加至系统变量Path中后,使用管理员打开cmd,然后开启MySQL:net start mysql57
2.2.1.5.Windows安装方法二:MySQL的压缩包安装
以压缩包的方式进行安装,需要自己额外的进行环境变量配置和MySQL的配置
1.输入https://www.mysql.com/downloads/地址进入MySQL官网,点击Community(GPL) Downloads
2.根据自己的电脑选择相应的版本进行下载
3.下载好压缩包解压之后,进行环境变量的配置,将文件夹下bin所在的路径添加至系统变量PATH中。
2.2.2.MySQL的配置
1.在MySQL的根目录下新建一个my.ini进行文件的配置,配置内容如下:
[mysql]
# 设置mysql客户端默认字符集
default-character-set=utf8
[mysqld]
#设置mysql服务端默认字符集
character-set-server=utf8
#设置3306端口
port = 3306
# 设置mysql的安装目录
basedir=C:\mysql\mysql-5.7.20-winx64
# 设置mysql数据库的数据的存放目录
datadir=C:\mysql\mysql-5.7.20-winx64\data
# 允许最大连接数
max_connections=200
# 创建新表时将使用的默认存储引擎
default-storage-engine=INNODB
如下图所示:
2.MySQL相关目录的介绍:
bin目录:用于存储一些可执行文件,如mysql.exe、mysqld.exe、mysqlshow.exe等。
data目录: 用于放置一些日志文件以及数据库。
docs 目录:用于存放一些文档
include目录:用于存储一些头文件如:mysql.h、mysql_ername.h等。
lib目录:用于放置一系列库文件。
share目录: 用于存放字符集、语言等信息
2.2.3.启动和关闭MySQL(以管理员的身份进入cmd)
mysqld --initialize:初始化data目录
mysqld install MySQL:注册MySQL服务(mysqld install MySQL --defaults-file = ‘C:\mysql\mysql-5.7.20-winx64\my.ini’’,可以在注册MySQL服务时指定默认的配置文件)
net start MySQL:启动MySQL,这里的mysql是上面注册的MySQL的服务的名称,根据自己MySQL的服务名称进行更换就行了。
net stop MySQL:关闭MySQL
同样我们可以使用:net start 服务名称 来开启服务.
2.2.4.MySQL访问服务器的客户程序
1.Mysql:一个命令行客户程序,用于交互式或以批处理模式执行SQL语句。
2.Mysqladmin:用于管理功能的客户程序。
3.Mysqlcheck:执行表维护操作。
4.mysqldump和mysqlhotcopy:负责数据库备份。
5.Mysqlimport:导入数据文件。
6.Mysqlshow:显示信息数据库和表的相关信息。
7.Myisamchk:执行表维护操作。
8.Myisampack:产生压缩、只读的表。
9.Mysqlbinlog:处理二进制日志文件的实用工具。
10.Perror:显示错误代码的含义。
2.3.MySQL基本操作
2.3.1.登录和退出MySQL
在刚安装好MySQL后,可直接使用mysql -uroot回车后可直接登录进入MySQL,最开始时我们的root用户是没有密码的,完整的方式为:
mysql -uroot -p -P3306 -h127.0.0.1:其中,-u表示用户,-p表示密码,注意密码和-p之间不能有空格,-P表示端口号,-h表示服务器ip,本地的为127.0.0.1或者localhost
exit或者quit:退出MySQL
注意:因为mysql5.7新增的特性中主要的一方面就是极大增强了安全性,安装Mysql后默认会为[email protected]用户创建一个随机密码,所以我们第一次登录之后就需要使用set password = password(‘newpassword’)来修改密码,不然第二次登录MySQL时,因为是随机的密码,我们就很难找到登录密码(目前还不知道在Windows怎么去找MySQL的随机密码,可能是看data目录下的错误日志文件)
2.3.2.MySQL特征及常用命令
2.3.2.1.主要特征
1.mysql不区分大小写,但为了更好的操作数据库,通常关键字和函数均为大写,数据库名称、表名称、字段名称均为小写;
2.语句默认以分号结尾,在命令提示符中必须要使用分号来做结束符,当然我们可以使用delimiter 来更改结束符,比如delimiter //我们的结束符就从;变为了//
3.在查询的SELECT和WHERE部分支持全部运算符和函数;
4.全面支持SQL的GROUP BY和ORDER BY子句,支持聚合函数( COUNT()、COUNT(DISTINCT)、AVG()、STD()、SUM()、 MAX()和MIN() );
5.支持ANSI SQL的LEFT OUTER JOIN和ODBC语法;
6.可以在同一查询中混用来自不同数据库的表;(select * from t1 where id in (select id from am.teacher))
7.每个表允许有16个索引。每个索引可以由1~16个列或列的一部分组成。最大索引长度是 256 个字节(在编译MySQL时,它可以改变)。一个索引可以使用一个CHAR或VARCHAR字段的前缀;
8.大数据库处理。可以对包含 50,000,000 个记录的数据库使用MySQL;
9.所有列都有缺省值,可以用INSERT插入一个表列的子集,那些没用明确给定值的列设置为他们的缺省值;
10.所有数据以 ISO-8859-1 Latin1 格式保存。所有正常的字符串比较是忽略大小写的;
11.函数名不会与表或列名冲突。例如ABS是一个有效的列名字。对函数调用的唯一限制是函数名与随后的“(”不能有空格;
12.DELETE、INSERT、REPLACE和UPDATE 返回有多少行被改变(受影响);
13.MySQL特有的SHOW命令可用来检索数据库、表和索引的信息,EXPLAIN命令可用来确定优化器如何解决一个查询。
2.3.2.2.常用命令
1.select version():查看MySQL版本
2.select database():查看当前使用的数据库
3.select user():查看当前登录的用户
4.select now():查看当前时间
5.select @@datadir:查看MySQL数据库存放目录
6.select @@basedir:查看MySQL的安装基础目录
7.select @@max_connections:查看MySQL的最大连接数
8.show variables like ‘validate_password’:查看密码策略
2.3.3.操作数据库
1.查看所有的数据库:show databases
2.使用数据库,或者叫打开数据库:use db_name
3.创建数据库:create database [if not exists] db_name [[default] character set utf8];
创建完成后,可以在MySQL的安装目录下的DATA目录中找到一个名为“db_name”的文件夹,该文件夹即是用于保存此数据库信息和数据的文件夹。实际上,如果手动在该目录下创建一个名为“db_name”的目录,MySQL服务器会将其作为一个正常的数据库目录。
4.查看该数据库的字符编码方式:show create database db_name;
5.修改数据库的编码方式:alter database db_name [default] character set gbk;
6.删除数据库:drop database db_name;
使用普通用户,你可能需要特定的权限来创建或者删除 MySQL 数据库,所以我们通常使用root用户登录,root用户拥有最高权限。
7.另外我们可以使用 mysql 的mysqladmin 命令来创建数据库。
mysqladmin -uroot -p create table_name
实例:
mysqladmin -uroot -p create ying 这样我们就创建了一个名为ying的数据库
2.3.3.1.mysqladmin
介于以上的知识,我们延伸下mysqladmin的一些基本常用知识
mysqladmin 工具的使用格式:
mysqladmin [option] command [command option] command ......
参数选项:
-c number 自动运行次数统计,必须和 -i 一起使用
-i number 间隔多长时间重复执行
0)每隔两秒查看一次服务器的状态,总共重复5次。
mysqladmin -uroot -p -i 2 -c 5 status
其中
1.Uptime表示:服务器已经运行的时间(以秒为单位);
2.Threads表示:线程数;Questions表示:已经发送给服务器的查询的个数;
3.Slow Queries表示:查询时间超过long_query_time秒的查询的个数;
4.Flush tables表示:执行的FLUSH语句数。
具体的MySQL show status参数可以参考:https://www.cnblogs.com/zengkefu/p/5634841.html
1)查看服务器的状况:status
mysqladmin -uroot -p status
2)修改root 密码:
mysqladmin -u root -p原密码 password newpassword 注意-p和原密码之间不能有空格
如下,将root原密码111111修改为了123456:
mysqladmin -uroot -p111111 password 123456
3)检查mysqlserver是否可用:
mysqladmin -uroot -p ping
如果MySQL服务正常启动的,那么将会显示如下内容:
如果MySQL服务未正常启动的,则会显示如下内容:
4)查询服务器的版本
mysqladmin -uroot -p version
5)查看服务器状态的当前值:
mysqladmin -uroot -p extended-status
6)查询服务器系统变量值:
mysqladmin -uroot -p variables
7)显示数据库服务器所有运行的进程:
mysqladmin -uroot -p processlist
mysqladmin -uroot -p-i 1 processlist //每秒刷新一次
8)创建数据库
mysqladmin -uroot -p create database_name
9)显示服务器上的所有数据库
mysqlshow -uroot -p
10)显示数据库database_name下有些什么表:
mysqlshow -uroot -p database_name
11)统计database_name 下数据库表列的汇总
mysqlshow -uroot -p database_name -v
12)统计database_name 下数据库表的列数和行数
mysqlshow -uroot -p database_name -v -v
13)删除数据库 database_name
mysqladmin -uroot -p drop database_name
14)重载权限信息
mysqladmin -uroot -p reload
15)刷新所有表缓存,并关闭和打开log
mysqladmin -uroot -p refresh
16)使用安全模式关闭数据库
mysqladmin -uroot -p shutdown
2.3.3.2.修改密码
在修改密码前,我们先来了解下MySQL的密码策略,使用如下命令可以查看现有的密码策略:
mysql> show variables like ‘validate_password%‘;
+--------------------------------------+--------+
| Variable_name | Value |
+--------------------------------------+--------+
| validate_password_check_user_name | OFF |
| validate_password_dictionary_file | |
| validate_password_length | 8 |
| validate_password_mixed_case_count | 1 |
| validate_password_number_count | 1 |
| validate_password_policy | MEDIUM |
| validate_password_special_char_count | 1 |
+--------------------------------------+--------+
7 rows in set (0.01 sec)
1.validate_password_number_count参数是密码中至少含有的数字个数,当密码策略是MEDIUM或以上时生效。
2.validate_password_special_char_count参数是密码中非英文数字等特殊字符的个数,当密码策略是MEDIUM或以上时生效。
3.validate_password_mixed_case_count参数是密码中英文字符大小写的个数,当密码策略是MEDIUM或以上时生效。
4.validate_password_length参数是密码的长度。
5.validate_password_dictionary_file参数是指定密码验证的字典文件路径,只有当策略为2时才使用得到
6.validate_password_policy这个参数可以设为 0=LOW, 1=MEDIUM, 2=STRONG,分别代表从低到高的密码强度,此参数的默认值为1,如果想将密码强度改弱,则更改此参数为0。
所以,如果我们修改的密码不符合密码策略设置的规则时,修改密码时就会报错,如果只仅仅是考虑方便,这时我们也可以通过修改密码策略来处理。
mysql> set global validate_password_policy=0;
mysql> set global validate_password_length=4;
下面开始修改密码:
最简单的方法就是借助第三方工具Navicat for MySQL来修改,方法如下:
1、登录mysql到指定库,如登录到xiong库,然后点击上方“用户”按钮。
3、选择要更改的用户名,然后点击上方的“编辑用户”按钮。
4、出现如图界面,输入新密码,并确认新密码,点击“保存”按钮即可。
第二种方式:
方法1: 用set password命令
如果我们使用root用户登录的话,就可以使用该方式修改所有用户的密码,如果我们使用普通的用户登录的话,就只能修改自己的密码。
首先登录MySQL:mysql -uroot -p
格式:mysql> set password for 用户名@localhost = password(‘新密码‘);
例子1:mysql> set password for [email protected] = password(‘123‘);
例子2:mysql>set password for [email protected] = password(‘123456’)
方法2:用mysqladmin
格式:mysqladmin -u用户名 -p旧密码 password 新密码
例子1:mysqladmin -uroot -p123456 password 123
例子2:mysqladmin -utest -p123456 password 11111
方法3:用UPDATE直接编辑user表
对于MySQL5.7以前的版本使用如下
首先登录MySQL:mysql -uroot -p
mysql> use mysql;
mysql> update user set password=password(‘123‘) where user=‘root‘ and host=‘localhost‘;
mysql> flush privileges;
对于MySQL5.7及以后的版本,MYSQL的用户表的密码字段变了,不再是password字段,而是authentication_string字段。
mysql> use mysql;
mysql> update user set authentication_string=password(‘密码‘) where user=‘root‘ and Host = ‘localhost‘
mysql> flush privileges;
方法4:使用alter
alter user ‘username’@’hostname’ identified by ‘newpwd’
alter user ‘test‘@‘localhost‘ identified by ‘123456‘;
3.数据类型与数据操作
3.1.MySQL数据类型
数据类型是指列、存储过程参数、表达式和局部变量的数据特征,它决定了数据的存储格式,代表了不同的信息类型。
3.1.1.整型
tinyint 【1个字节,有符号:-2^7—2^7-1 无符号:0—2^8-1】
smallint 【2个字节,有符号:-2^15—2^15-1 无符号:0—2^16-1】
mediumint 【3个字节】
int 【4个字节】
bigint 【 8个字节】
注意:
1.UNSIGNED 修饰符规定字段只保存正值。因为不需要保存数字的正、负符号,可以在储时节约一个“位”的空间。从而增大这个字段可以存储的值的范围,需要注意的是如果没有明确的添加UNSIGNED修饰符,默认就是有符号位的。
2.ZEROFILL 修饰符规定 0(不是空格)可以用来填补(从左边填补)输出的值。使用这个修饰符可以阻止 MySQL 数据库存储负值。
3.MySQL 以一个可选的显示宽度指示器的形式对 SQL 标准进行扩展,这样当从数据库检索一个值时,可以把这个值加长到指定的长度。例如,指定一个字段的类型为 INT(6),就可以保证所包含数字少于 6 个的值从数据库中检索出来时能够默认自动地用空格填充。需要注意的是,使用一个宽度指示器不会影响字段的大小和它可以存储的值的范围。
3.1.2.浮点型
FLOAT
float[D,M] 其中D表示精度总数字位数(精度),M表示小数的位数(标度),D和M可选的,其中D的范围:1~65,M的范围:0~30
1.不指定D,M时,只能存6个数字(包括小数点前后的位数)
2.只指定D时,只能存6个数字(包括小数点前后的位数)
3.若指定D,M时,先满足小数位M,然后再看整数位,整数位不能大于D-M,输入的值比如float(5,2),规定了存储的值不会超过 5 位数字,并且小数点后不超过 2 位,因为小数位有2位了,所以整数部分就只能输入3位的
4.D<=24时,类型为float,若53>D>24则为double类型的(在没有标度的情况下,如果有标度的情况下还是float)
DOUBLE
double[D,M] 其中D表示总数字位数,M表示小数的位数,后面的D和M可选的
1.不指定D,M时,可存储17位数字(包括小数点前后的位数)
2.在double中,如果指定了D,那么就要指定M
3.若指定D,M时,先满足小数位M,然后再看整数位,整数位不能大于D-M,输入的值比如double(5,2),规定了存储的值不会超过 5 位数字,并且小数点后不超过 2 位,因为小数位有2位了,所以整数部分就只能输入3位的
4.double的精度为可以到53
DECIMAL
decimal[D,M] decimal适合于更高精度类型的数值,其中D表示总数字位数,M表示小数的位数,后面的D和M可选的
1. D范围为1?65,M的范围为0~30,可存储38位数字
2.如果没有指定D,M时,D默认为10,M默认为0
3.MySQL使用二进制格式存储decimal值。它将9位数字包装成4个字节。对于每个部分,需要4个字节来存储9位数的每个倍数。
例如,DECIMAL(19,9)对于小数部分具有9位数字,对于整数部分具有10位数字,小数部分需要4个字节。 整数部分对于前9位数字需要4个字节,1个剩余字节需要1个字节。DECIMAL(19,9)列总共需要9个字节。
注意:
1.对于小数点后面的位数超过允许范围的值,MySQL 会自动将它四舍五入为最接近它的值,再插入它。
2.UNSIGNED 和 ZEROFILL 修饰符也可以被 FLOAT、DOUBLE 和 DECIMAL 数据类型使用。并且效果与 INT 数据类型相同。
类型 大小 范围(有符号) 范围(无符号) 用途
TINYINT 1 字节 (-128,127) (0,255) 小整数值
SMALLINT 2 字节 (-32 768,32 767) (0,65 535) 大整数值
MEDIUMINT 3 字节 (-8 388 608,8 388 607) (0,16 777 215) 大整数值
INT或INTEGER 4 字节 (-2 147 483 648,2 147 483 647) (0,4 294 967 295) 大整数值
BIGINT 8 字节 (-9,223,372,036,854,775,808,9 223 372 036 854 775 807) (0,18 446 744 073 709 551 615) 极大整数值
FLOAT 4 字节 (-3.402 823 466 E+38,-1.175 494 351 E-38),0,(1.175 494 351 E-38,3.402 823 466 351 E+38) 0,(1.175 494 351 E-38,3.402 823 466 E+38) 单精度
浮点数值
DOUBLE 8 字节 (-1.797 693 134 862 315 7 E+308,-2.225 073 858 507 201 4 E-308),0,(2.225 073 858 507 201 4 E-308,1.797 693 134 862 315 7 E+308) 0,(2.225 073 858 507 201 4 E-308,1.797 693 134 862 315 7 E+308) 双精度
浮点数值
DECIMAL 对DECIMAL(M,D) ,如果M>D,为M+2否则为D+2 依赖于M和D的值 依赖于M和D的值 小
3.1.3.时间日期型
year 【1个字节 1970-2069】
time 【3个字节 -838:59:59~838:59:59】
date 【3个字节 1000.1.1~9999.12.31】
datetime 【8个字节 1000.1.1 00:00:00 ~ 9999.12.31 23:59:59】
timestamp 【时间戳,4个字节 1970.1.1 00:00:00起秒数,如果我们对 timestamp类型的字段没有明确赋值,或是被赋与了 null 值。MySQL 会自动使用系统当前的日期和时间来填充它】
类型 大小
(字节) 范围 格式 用途
DATE 3 1000-01-01/9999-12-31 YYYY-MM-DD 日期值
TIME 3 ‘-838:59:59‘/‘838:59:59‘ HH:MM:SS 时间值或持续时间
YEAR 1 1901/2155 YYYY 年份值
DATETIME 8 1000-01-01 00:00:00/9999-12-31 23:59:59 YYYY-MM-DD HH:MM:SS 混合日期和时间值
TIMESTAMP 4 1970-01-01 00:00:00/2038
结束时间是第 2147483647 秒,北京时间 2038-1-19 11:14:07,格林尼治时间 2038年1月19日 凌晨 03:14:07 YYYYMMDD HHMMSS 混合日期和时间值,时间戳
3.1.4.字符型
char(M) 【M个字节,M的范围为:0-255,长度不够的用空格进行填充】
varchar(M) 【<=M个字节,M的范围为:0-65535】
MySQL 提供了 TEXT 和 BLOB 两种类型。根据存储数据的大小,它们都有不同的子类型。这些大型的数据用于存储文本块或图像、声音文件等二进制数据类型。
文本数据 【不区分大小写,会忽略尾部空格,tinytext,text,mediumtext,longtext】
二进制文本数据 【会区分大小写,tinyblob,blob,mediumblob,longblob】
enum :枚举
1.单选字符串数据类型,适合存储表单界面中的“单选值”。
2.设定enum的时候,需要给定“固定的几个选项”;存储的时候就只存储其中的一个值。
enum("选项1","选项2","选项3",...);
3.实际上,enum的选项都会对应一个数字,依次是1,2,3,4,5...,最多有65535个选项,1-255为一个字节,256-65535为第二个字节。
4.使用的时候,可以使用选项的字符串格式,也可以使用对应的数字。
eg:定义时:gender enum(‘M‘,‘F‘) 插入时:可以value (‘‘M”)或者value(1)
set :
1.多选字符串数据类型,适合存储表单界面的“多选值”,当存储多个相同的值,最终只保留一个。
2.设定set的时候,同样需要给定“固定的几个选项”;存储的时候,可以存储其中的若干个值。
3.设定set的格式:set("选项1","选项2","选项3",...)
4.同样的,set的每个选项值也对应一个数字,依次是1,2,4,8,16...,最多有64个选项,每8位一个字节
5.使用的时候,可以使用set选项的字符串本身(多个选项用逗号分隔),也可以使用多个选项的数字之和(比如:1+2+4=7)
定义时:hobby set(‘music‘,‘movie‘,‘swimming‘,‘footbal‘) 插入时:可以value(‘music‘,‘movie‘)等同于value(3)
类型 大小 用途
CHAR 0-255字节 定长字符串
VARCHAR 0-65535 字节 变长字符串
TINYBLOB 0-255字节 不超过 255 个字符的二进制字符串
TINYTEXT 0-255字节 短文本字符串
BLOB 0-65 535字节 二进制形式的长文本数据
TEXT 0-65 535字节 长文本数据
MEDIUMBLOB 0-16 777 215字节 二进制形式的中等长度文本数据
MEDIUMTEXT 0-16 777 215字节 中等长度文本数据
LONGBLOB 0-4 294 967 295字节 二进制形式的极大文本数据
LONGTEXT 0-4 294 967 295字节 极大文本数据
3.1.5.位类型
bit[位数]
1.此类型用于声明一个指定位数的数据,位数的取值范围是1到64,默认值是1。此类型所占用的字节数由“位数”决定,每满8位即需一个新的字节,其长度的计算方法为“(位数+7)/8”,如有小数则进一。如BIT[25]占用4个字节,而BIT[26]则占用5个字节。
2.此数据类型用于存储基于位的数值,指定位类型的值时,可以采用b‘二进制值’的写法。如, b‘111‘ 代表7,b‘10000000‘代表128。
3.如果所插入的值的位数比指定的位数少时,MySQL会在值的左边用0填充,如将值b’111’插入到BIT[8]时,等同于插入值b’00000111’。
bool型
1.bool型数据用于存储逻辑值,它只有两种状态,即“TRUE”和“FALSE”,或“1”和“0”。此类型等价于TINYINT(1)类型。其值为0时表示“FALSE”,所有非0值都表示“TRUE”。
3.2.MySQL操作数据表
3.2.1.数据库常用注释符
?# 但在pg数据库中不行
如:SELECT * FROM `test1` where number=2 #and age=24
?-- (注意:--后面有一个空格)
如:SELECT * FROM `test1` where number=2 -- and age=24
?/*...*/
如:SELECT * FROM `test1` where number=2 /*and age=24*/
另外常用的注释符还有:--+,但是在MySQL中只有上面三种
3.2.2.数据表的增-create
几个常用的命令
1.show tables;(在当前数据库下,使用这个默认展示当前数据库下的数据表)
2.等同于:select table_name from information_schema.tables where table_schema=database()
3.show tables from db_name(使用该方法可查看任何某一个数据库下的数据表)
4.select database(); (查看当前正在使用的数据库)
5.desc tb_name或者show columns from tb_name或者show fields from tb_name(查看表的结构)
创建表的模式
create table [if not exists] tb_name (col_name1 data_type constraint,col_name2 data_type constraint);
约束
1.约束分为表级约束和列级约束,对一个数据列建立的约束叫列级约束,其中not null和default约束就只有列级约束,列级约束既可以在列定义时声明,也可以在列定义后声明。对两个及以上的数据列建立的约束叫表级约束,表级约束只能在列定义后声明。
2.空值和非空:null,not null
3.主键约束:primary key 一个表中只能有一个主键,主键默认为not null,主键创建后会自动创建索引
4.唯一约束:unique key 唯一约束的值为唯一的,但是与primary key的区别就是,unique key可以为null,null的值是可以重复的。唯一约束和主键约束创建后会自动创建索引,可通过show index from tb_name来查看表中创建的索引
5.外键约束:foreign key,有外键的表叫子表,子表参照的表叫做父表,外键使用时需要注意的几点:
1.父表和子表必须使用相同的存储引擎,而且禁止使用临时表;
2.数据表的存储引擎只能为InnoDB
3.外键列和参照列必须具有相似的数据类型,其中数字的长度或是否有符号位必须相同;而字符的长度则可以不同
4.外键列和参照列必须创建索引.如果外键列不存在索引的话,MySQL将自动创建索引.
实例如下:
create table test(
id tinyint unsigned primary key
);
create table test1(
id tinyint unsigned,
name varchar(20) not null,
foreign key(id) references test(id)
)engine=myisam,charset=utf8
6.默认约束:default 一般如果未指定not null 那么未写默认值时该字段的默认只为NULL。默认值必须为常数,不能是函数或表达式,例如,一个日期列的默认值不能被设置为一个函数,如NOW()或CURRENT_DATE。这里有一个特例,可以对TIMESTAMP列指定CURRENT_TIMESTAMP为默认值。BLOB和TEXT类型的列不能指定默认值。
7.其他约束:
1)自动编号:auto_increment时必须是primary key且是整数类型的,但是反过来就不一定;如果对自增列插入了default、NULL或者0,那么也会自增。一般情况下序列的开始值为1,如果需要特定的值,我们可以使用:alter tabel tb_name auto_increment = 100
2)或者在创建表的时候指定:
create table tb_name(
id tinyint unsigned primary key auto_increment,
name varchar(20) not null
)engine = mysiam auto_increment = 100
3)非负:unsigned 规定字段只保存正值。因为不需要保存数字的正、负符号,可以在储时节约一个“位”的空间。从而增大这个字段可以存储的值的范围。
4)补零:zerofill 规定 0(不是空格)可以用来真补输出的值。使用这个修饰符可以阻止 MySQL 数据库存储负值
8.各约束的顺序:
字段名>dataType>unsigned>zerofill>
create table index(
id tinyint unsigned zerofill primary key auto_increment not null
);
或者:
create table index(
id tinyint zerofill unsigned primary key auto_increment not null
);
9.创建临时表
在创建表格时,可以使用TEMPORARY关键词使当前建立的表为临时表。临时表只能在当前连接中使用,当连接关闭时,临时表也被自动删除。这意味着两个不同的连接可以创建两个名称相同的临时表,这两个临时表不会互相冲突,也不与原有的同名的非临时表冲突。
如果临时表的名称与数据库中现有的非临时表名称相同,则非临时表会被隐藏,直到该临时表被删除为止。
创建临时表要求当前帐户拥有CREATE TEMPORARY TABLES权限。
create temporary table tb_name(
id tinyint not null,
name char(20) not null
);
10.注释
1)添加列的注释
直接在字段的定义后添加comment ‘comment_content’
2)添加表的注释
直接在表的定义后添加comment=’comment_content’
总体如下:
CREATE TABLE `xiong123` (
`id` int(11) NOT NULL COMMENT ‘自增id‘,
`name` varchar(20) DEFAULT NULL COMMENT ‘姓名‘,
`age` int(11) DEFAULT NULL COMMENT ‘年龄‘,
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT=‘这是一个表的注释‘
3.2.3.数据表的改-alter
1.添加或删除列
1)添加单列
alter table tb_name add [column] col_name data_type constraint [first] / [after another_col_name]
实例:
alter table test add name varchar(20) not null first 表示添加一个name列,并放在最前面
alter table test add age smallint not null after id 表示添加一个age列,并放在id列的后面一列
创建单列时可以指定该列的位置
2)添加多列
alter table tb_name add [column] (col_name1 data_type constraint,col_name2......,col_name3......) 创建多列时就不能指定列的位置了
实例:
alter table test add (name varchar(20) not null ,age smallint not null) 表示同时添加name,age列
3)删除列
alter table tb_name drop [column] col_name ,drop [column] col_name2;
实例:
alter table test drop name,drop age;
2.添加约束
添加主键约束:alter table tb_name add [constraint 约束名] primary key(col_name);
实例:
alter table test add primary key(id)
添加唯一约束:alter table tb_name add [constraint 约束名] unique key(col_name1,col_name2);
实例:
alter table test add unqiue key(id,name)
添加外键约束:alter table tb_name add [constraint 约束名] foreign key(col_name1) references tb_name2(col_name2);
实例:
alter table test add foreign key(id) references test1(id)
添加默认约束:alter table tb_name alter [column] col_name set default value;
实例:
alter table test alter gender set default ‘M’ 设置test表中gender列的默认值为M
【默认值约束需要注意的几点】:
1.默认值必须为常数,不能是函数或表达式,例如,一个日期列的默认值不能被设置为一个函数,如NOW()或CURRENT_DATE。
2.这里有一个特例,可以对TIMESTAMP列指定CURRENT_TIMESTAMP为默认值。
3.BLOB和TEXT类型的列不能指定默认值
3.删除约束
删除主键约束:alter table tb_name drop primary key(因为主键就只有一个,所以直接删除primary key)
实例:
alter table test drop primary key
删除唯一约束:alter table tb_name drop index/key index_name(通过show index form tb_name查看index_name)
实例:
alter table test drop index number 表示删除test表中唯一约束(名字为number)
删除默认约束:alter table tb_name alter [column] col_name drop default;
实例:
alter table test alter gender drop default
删除外键约束:alter table tb_name drop foreign key 外键约束名(show create table tb_name可以查看外键约束名称)
实例:
alter table test drop foreign key test1_ibfk_1 表示删除test表的外键,(通过show create table test来查看test表的外键)
4.修改列定义和更名
修改列定义:alter table tb_name modify [column] col_name constraintl [first / after another_col_name](在更改列定义时,如果是primary key列,更改时就不需要再把primary key加上了,不然会显示主键重复的错误)
实例:
alter table test modify age int unsigned not null 修改age列的定义
修改列名称:alter table tb_name change [column] col_name1 new_col_name1 constraint [first / after another_col_name],change [column] col_name2 new_col_name2 constraint
修改多个列时,每个change逗号隔开,change除了修改列定义外,还可以修改列的名称(必须要给出新的名称)
实例:
alter table test change age nianling int not null 将age列名改为nianling,记住一定要有后面的dataype和constraint
修改表名称:alter table tb_name1 rename to/as tb_name2;(修改单个表名称)
rename table tb_name1 to tb_name2,tb_name3 to tb_name4;(修改多个表名称)
实例:
alter table test renam to test2 或者 alter table test rename as test2
rename tabel test to test2,test1 to test3
3.2.4.数据表的删-drop
删除表:drop table tb_name
3.2.5.表数据的查询-select
所有语句的摆放顺序:
select col_name... from tb_name
[where where_condition] 查询条件
[group by {col_name|position} [asc|desc] 按某个字段进行分组,相同的只显示第一个
[having where_condition] 分组时,给出显示条件
[order by {col_name|expr|position} [asc|desc] 排序
[limit{[offset,]row_count|row_count OFFSET offset}] 限制返回数量
查询时我们可以使用as来使用别名,实际上as是可以省略的,但是这样很容易导致分歧,所以建议使用别名时都要写上as,同时别名就可以使用在order by和group by中。
3.2.5.1.查询不重复项distinct
select distinct 字段1,字段2 from 表名;
实例:
select distinct name from test1 表示查询test1表中name不相同的记录
select distinct gender,age from test1 表示查询gender和age同时不同的记录
注意:
?distinct必须放在最开头
?distinct去重多个字段时,含义是:几个字段同时重复时才会被过滤。
3.2.5.2.where语句
表示查询条件,where语句中可以使用函数和表达式
实例:
select * from test1 where name like ‘%xiong%’ 表示查询test1表中,name包含xiong的记录
注意:
where后面的条件可以用>、<、>=、<=、!=(<>)等多种比较运算符,like,not like等,多个条件之间可以用or、and、not等逻辑运算符
我们可以使用like进行模糊查询,同样我们也可以使用正则表达式来进行查询
实例:
select * from test1 where name REGEXP ‘cai’ 表示查询name包含cai的记录
select * from test1 where name REGEXP ‘ying$’ 表示查询name以ying结尾的记录
select * from test1 where name REGEXP ‘^xiong’ 表示查询name以xiong开头的记录
3.2.5.3.group by语句
按某个字段进行分组,相同的只显示第一个,asc为默认值,表示升序,desc为降序
实例:
select nianling,avg(number) from test1 group by nianling having avg(number)>5; 表示按nianling分组,并过滤掉number平均值大于5的记录
注意:
?group by语句通常与聚合函数一起使用,包括sum(求和)、count(*)(记录数)、max(最大值)、min(最小值)
?group by如果有多个条件,同时对多个字段进行分组,如果多个字段都相同时显示一条记录
3.2.5.4.having语句
用于分组时,给出显示条件,使用having时的字段必须是前面的查询展示的列,或者是使用聚合函数(count(),avg(),max()等)的字段。
?having后面操作的字段必须是前面展示的字段,如上的实例中,就只能操作nianling和avg(number)这两个字段,操作了其他的字段均会报错
3.2.5.5.order by语句
用于对查询结果进行排序
实例:
select * from test order by id desc,addtime asc 表示先按id降序排列,然后再按addtime升序排列
?desc 降序排列,asc 升序排列
?order by 后面可以跟多个不同的排序字段,每个排序字段都可以有不同的排序顺序。
?如果排序字段的值一样,则相同的字段按照第二个排序字段进行排序。
?如果只有一个排序字段,则字段相同的记录将会无序排列。
3.2.5.6.limit语句
第一种方式:limit number表示从查询的结果中的第一行开始,选择number个行;第二种方式:limit (num1,num2)第一个参数表示从第几行开始,第一行为0,然后第二个参数表示取几行,比如limit(3,2)表示从查询结果中的第四行开始,选择两行的结果。
实例:
3.2.6.表数据的插入-insert
3.2.6.1.insert into
插入单条记录:insert [into] tb_name[(col_name...)] values(val1...)
插入多条记录:insert [into ] tb_name[(col_name...)] values(val1...),(val2...),(val3....)
实例:
insert into test1 values(1,‘xiong‘,‘M‘,2,23),(2,‘ying‘,‘F‘,3,24),(3,‘cai‘,‘M‘,4,25),(4,‘liu‘,‘M‘,5,26) 完整的插入四条记录
insert into test1(name,number,nianling) values(‘xiongxiong‘,22,22) 只插入某几个字段,其他字段使用默认值或auto_increment的自增
也可以这样:
insert into test1 values(default, ‘yangyang‘,default,30,40) 表示id使用默认值即自增,gender使用默认值
注意点:
?如果全表插入,可以不用写字段,插入时每个字段都要有匹配的值
?当表中只有一个字段时,values可以写成value
?如果是auto_increment的字段,在插入时可以写在字段里,也可以不写,如果不写会自动增加的,但是如果写了就要给对应的值,不然会报错,因为主键not null。
?如果为not null的字段,那么必须有值的插入
?如果有默认值的,在插入时我们可以把值写为default,auto_increment写成默认值时就是自增,没写not null的默认值为null,写了default的默认值为创建表时的默认值。
3.2.6.2.Insert set select
insert [into] tb_name1 set col_name1 = (select col_name2 from tb_name2)(从中可以看出set语句同样可以使用子查询)
3.2.6.3.insert into select
将查询一个表的记录插到另外一个表中
insert into xiong select * from test1;
示例
首先我们使用复制表的语句复制一个test1表的复制表xiong,然后使用insert into select的方式来插入数据
查看表xiong的数据
3.2.7.表数据的更新-update
update tb_name set colmun1 = value1,column2 = value2 where expression(更新表示如果没有指定条件,那么会更新所有的记录,所以更新是要谨慎)
实例:
update test1 set nianling = 30 where name = ‘yangyang‘
3.2.8.表数据的删除-delete
delete from tb_name where conditions(删除表数据时如果没有指定条件,那么会删除表中所有的数据,所以删除时一定要谨慎)
4.子查询与连接
4.1.MySQL连接查询(字段会增加)
连接查询:将多张表(>2)进行记录的连接(按照某个指定的条件进行数据拼接)。
连接查询的意义: 在用户查看数据的时候,需要显示的数据来自多张表。
SQL中将连接查询分成三类: 内连接,外连接和交叉连接。
4.1.1.交叉连接
cross join,也叫做笛卡尔积。从一张表中循环取出每一条记录, 每条记录都去另外一张表进行匹配,匹配一定保留(没有条件匹配), 而连接本身字段就会增加(保留),最终形成的结果共M*N行
4.1.2.内连接
[inner] join, 从左表中取出每一条记录,去右表中与所有的记录进行匹配, 匹配必须是某个条件在左表中与右表中相同最终才会保留结果,否则不保留。on表示连接条件,如果没有on条件就是笛卡尔乘积,on可以使用where来代替,但是这样效率会比较低
4.1.3.外连接
以某张表为主,取出里面的所有记录, 然后每条与另外一张表进行连接;不管能不能匹配上条件,最终都会保留;能匹配,正确保留; 不能匹配,其他表的字段都置空NULL。
4.1.3.1.左外连接
left [outer] join:以左表为主,右表未匹配到的字段都为NULL,即展示左表所有记录,右表中不符合条件的自断值都为null
4.1.3.2.右外连接
right [outer] join:以右表为主,左表未匹配到的字段都为NULL
4.1.3.3.全外连接
全外连接还返回左表中不符合连接条件单符合查询条件的数据行,并且还返回右表中不符合连接条件单符合查询条件的数据行。全外连接实际是上左外连接和右外连接的数学合集(去掉重复),即“全外=左外 UNION 右外”,但是在MySQL中不支持全外连接。
SQL查询的基本原理:两种情况介绍。
1、单表查询:根据WHERE条件过滤表中的记录,形成中间表(这个中间表对用户是不可见的);然后根据SELECT的选择列选择相应的列进行返回最终结果。
2、两表连接查询:对两表求积(笛卡尔积)并用ON条件和连接连接类型进行过滤形成中间表;然后根据WHERE条件过滤中间表的记录,并根据SELECT指定的列返回查询结果。
3、多表连接查询:先对第一个和第二个表按照两表连接做查询,然后用查询结果和第三个表做连接查询,以此类推,直到所有的表都连接上为止,最终形成一个中间的结果表,然后根据WHERE条件过滤中间表的记录,并根据SELECT指定的列返回查询结果。
理解SQL查询的过程是进行SQL优化的理论依据。
ON后面的条件(ON条件)和WHERE条件的区别:
ON条件:是过滤两个链接表笛卡尔积形成中间表的约束条件。
WHERE条件:在有ON条件的SELECT语句中是过滤中间表的约束条件。在没有ON的单表查询中,是限制物理表或者中间查询结果返回记录的约束。在两表或多表连接中是限制连接形成最终中间表的返回结果的约束。
从这里可以看出,将WHERE条件移入ON后面是不恰当的。推荐的做法是:
ON只进行连接操作,WHERE只过滤中间表的记录。
连接查询是SQL查询的核心,连接查询的连接类型选择依据实际需求。如果选择不当,非但不能提高查询效率,反而会带来一些逻辑错误或者性能低下。下面总结一下两表连接查询选择方式的依据:
1、 查两表关联列相等的数据用内连接。
2、 Col_L是Col_R的子集时用右外连接。
3、 Col_R是Col_L的子集时用左外连接。
4、 Col_R和Col_L彼此有交集但彼此互不为子集时候用全外。
5、 求差操作的时候用联合查询。
4.2.MySQL联合查询union(字段不会增加)
联合查询:将多次查询的结果在记录上进行拼接(字段不会增加),即查询结果的合并。
基本语法:多条select语句构成,每一条select语句获取的字段数必须严格一致,
Select a from A
Union
Select b from B
联合查询的意义:
1. 查询同一张表,但是需求不同: 如查询学生信息, 男生身高升序, 女生身高降序(最主要是用于对同一个表不同的操作)。
2. 多表查询: 多张表的结构是完全一样的,具有相同的列,虽然没有强制要求对应的列类型,顺序一致,但是推荐将相对应的列类型和顺序保持一致。
3.联合查询默认是去重(distinct)的,如果需要显示全部则使用union all
4.在联合查询中,order by不能直接使用,需要对查询语句使用括号才行;另外,要order by生效必须搭配limit,limit使用限定的最大数即可
4.3.MySQL子查询
子查询是指嵌入在其他SQL语句中的select语句,这里说的SQL语句可以是delete,insert,update,select等语句,可包含多个关键字或条件(distinct,group by,order by,limit,函数等),子查询必须始终出现在圆括号以内。当然子查询并不是严格划分的,就是说我们在某一次查询中就可能使用到下面几种分类的子查询
4.3.1.按位置分类
子查询(select语句)在外部查询中出现的位置
1.from子查询,子查询跟在from之后
2.where子查询,子查询出现where条件中
3.exists子查询,子查询出现在exists里面
4.3.2.按结果分类
根据子查询得到的数据进行分类(理论上讲任何一个查询得到的结果都可以理解为二维表)
4.3.2.1.标量子查询
是指子查询返回的是单一值的标量,如一个数字或一个字符串,也是子查询中最简单的返回形式; 可以使用 = > < >= <= <> 这些操作符对子查询的标量结果进行比较,通常子查询的位置在比较式的右侧
如:select * from student where height > (select avg(height) from student);找出大于平均身高的学生
4.3.2.2.列子查询
指子查询返回的结果集是 N 行一列,该结果通常来自对表的某个字段查询返回, 子查询的结果为多个值时就需要可以使用 IN、ANY、SOME 和 ALL 操作符配合比较运算符进行比较。
如查找学生表中身高等于老师的身高的学生,select * from student where height in (select height from teacher);=any与in的效果是一致的,所以还可以是select * from student where height =any (select height from teacher);
另外需要注意下any,some,all关键字的使用规则,any和some的使用基本差不多(有种说法就是some其实是any的别名),唯一不同的是all,=some/=any就表示等于查询结果的每一个,=all没有意义,=any 与 in 同样的效果,<>all/!=all与not in同样的效
4.3.2.3.行子查询
指子查询返回的结果集是一行 N 列,该子查询的结果通常是对表的某行数据进行查询而返回的结果集。
比如我们想查看学生表中编号和身高都等于老师表中的编号和身高的数据
select * from student where (number,height) = (select id,height form teacher where gender = ‘男’);
4.3.2.4.表子查询
指子查询返回的结果集是 N 行 N 列的一个表数据。
比如找出男生女生中身高最高的
select * from
(select * from student group by gender,height desc) as a
group by gender;
Exists子查询,只要满足where条件的话,将展示所有的结果。
5.存储引擎
5.1.什么是存储引擎
引擎(Engine)是电子平台上开发程序或系统的核心组件。利用引擎,开发者可迅速建立、铺设程序所需的功能,或利用其辅助程序的运转。一般而言,引擎是一个程序或一套系统的支持部分。常见的程序引擎有游戏引擎,搜索引擎,杀毒引擎等。
简单来说,存储引擎就是指表的类型以及表在计算机上的存储方式,每一种存储引擎使用不同的存储机制,索引技巧,锁定水平。我们可以根据数据的特点来选择不同的存储引擎。
可使用show engines;来查看MySQL提供的所有存储引擎。
可使用show variables like ‘%storage_engine%‘;来查看当前默认的存储引擎。
5.2.存储引擎分类
5.2.1.InnoDB存储引擎
InnoDB给MySQL的表提供了事务处理、回滚、崩溃修复能力和多版本并发控制的事务安全。如果应用中需要执行大量的INSERT或UPDATE操作,则应该使用InnoDB,这样可以提高多用户并发操作的性能。而且InnoDB对事务处理的能力,也是其他存储引擎不能比拟的。5.5以后版本的MySQL的默认存储引擎就是InnoDB。
InnoDB存储引擎中支持AUTO_INCREMENT。自动增长列的值不能为空,并且值必须唯一。MySQL中规定自增列必须为主键。在插入值的时候,如果自动增长列不输入值,则插入的值为自动增长后的值;如果输入的值为0或空(NULL),则插入的值也是自动增长后的值;如果插入某个确定的值,且该值在前面没有出现过,就可以直接插入。
InnoDB还支持外键(FOREIGN KEY)。外键所在的表叫做子表,外键所依赖(REFERENCES)的表叫做父表。父表中被字表外键关联的字段必须创建索引。当删除、更新父表中的某条信息时,子表也必须有相应的改变,这是数据库的参照完整性规则。
Innodb使用的行锁(当然也支持表锁),只对指定的行进行锁定,其他进程还是可以对表中的其他行进行操作的。因此行锁能大大的减少数据库操作的冲突,但有时会导致死锁。
Innodb存储引擎可将所有数据存放于ibdata*的共享表空间,也可将每张表存放于独立的.ibd文件的独立表空间。
共享表空间以及独占表空间都是针对innodb表的数据存储而言的,ibdata1为innodb引擎的存储数据与索引的数据文件,ib_logfile0与ib_logfile1为innodb引擎使用的日志文件。
共享表空间: 某一个数据库的所有的表数据,索引文件全部放在一个文件中,默认这个共享表空间的文件路径在data目录下。 默认的文件名为:ibdata1 初始化为10M。
独立表空间: 每一个表都将会生成以独立的文件方式来进行存储,每一个表都有一个.frm表描述文件,还有一个.ibd文件,其中这个文件包括了单独一个表的数据内容以及索引内容。
实例:我们可以在创建表是明确的指定engine,或者默认都是innodb。
create table test3(
id tinyint zerofill primary key
)engine= innodb
修改时:
alter table test3 engine = myisam
默认情况下它的存储位置也是在表的位置之中(即默认的存储方式是独立表空间);在配置文件my.ini中配置innodb_file_per_table=1(MySQL5.6.6以后默认的),可以使用如下命令查看:
show variables lie ‘%innodb_file_per_table%’
InnoDB的优势在于提供了良好的事务处理、崩溃修复能力和并发控制。缺点是读写效率较差,占用的数据空间相对较大。
5.2.2.MyISAM存储引擎
MyISAM是MySQL中常见的存储引擎,曾经是MySQL的默认存储引擎【5.5以前默认是MyISAM,后面的默认就是InnoDB】。MyISAM是基于ISAM引擎发展起来的,增加了许多有用的扩展。
MyISAM的表存储成3个文件。文件的名字与表名相同。拓展名为frm、MYD、MYI。其中,frm文件存储表的结构;MYD文件存储数据,是MYData的缩写;MYI文件存储索引,是MYIndex的缩写。
MyISAM使用的是表锁。直接锁定整张表,在锁定期间,其他进程无法对该表进行写操作,如果设置的是写锁,那么其他进程读也不允许,因此myisam支持的并发量低。
MyISAM支持全文索引。
实例:
create table test2(
id tinyint unsigned primary key
)engine = myisam
修改时:
alter table test2 engine = innodb
5.2.3.MEMORY存储引擎
MEMORY是MySQL中一类特殊的存储引擎。它使用存储在内存中的内容来创建表,而且数据全部放在内存中。这些特性与前面的两个很不同。
每个基于MEMORY存储引擎的表实际对应一个磁盘文件。该文件的文件名与表名相同,类型为frm类型。该文件中只存储表的结构。而其数据文件,都是存储在内存中,这样有利于数据的快速处理,提高整个表的效率。值得注意的是,服务器需要有足够的内存来维持MEMORY存储引擎的表的使用。如果不需要了,可以释放内存,甚至删除不需要的表。
MEMORY默认使用哈希索引。速度比使用B型树索引快。当然如果你想用B型树索引,可以在创建索引时指定。
注意,MEMORY用到的很少,因为它是把数据存到内存中,如果内存出现异常就会影响数据。如果重启或者关机,所有数据都会消失。因此,基于MEMORY的表的生命周期很短,一般是一次性的。
实例:
create table index2(
id tinyint not null)
engine = memory
由于数据都是放在内存中的,所以就只有一个frm文件
5.2.4.怎样选择存储引擎
InnoDB:支持事务处理,支持外键,支持崩溃修复能力和并发控制。如果需要对事务的完整性要求比较高(比如银行),要求实现并发控制(比如售票),那选择InnoDB有很大的优势。如果需要频繁的更新、删除操作的数据库,也可以选择InnoDB,因为支持事务的提交(commit)和回滚(rollback)。
适用场景:
1)经常更新的表,适合处理多重并发的更新请求。
2)支持事务。
3)可以从灾难中恢复(通过bin-log日志等)。
4)外键约束。只有他支持外键。
5)支持自动增加列属性auto_increment。
MyISAM:插入数据快,空间和内存使用比较低(会对数据进行压缩)。如果表主要是用于插入新记录和读出记录,那么选择MyISAM能实现处理高效率。如果应用的完整性、并发性要求比 较低,也可以使用。
适用场景:
1)不支持事务的设计,但是并不代表着有事务操作的项目不能用MyISAM存储引擎,可以在service层进行根据自己的业务需求进行相应的控制。
2)不支持外键的表设计。
3)查询速度很快。
4)对表进行加锁的场景。
5)MyISAM极度强调快速读取操作。
6)MyISAM中存储了表的行数,于是SELECT COUNT(*) FROM TABLE时只需要直接读取已经保存好的值而不需要进行全表扫描。如果表的读操作远远多于写操作且不需要数据库事务的支持,那么MyIASM也是很好的选择。
MEMORY:所有的数据都在内存中,数据的处理速度快,但是安全性不高。如果需要很快的读写速度,对数据的安全性要求较低,可以选择MEMOEY。它对表的大小有要求,不能建立太大的表。所以,这类数据库只使用在相对较小的数据库表。
注意,同一个数据库也可以使用多种存储引擎可以的表。如果一个表要求比较高的事务处理,可以选择InnoDB。这个数据库中将查询要求比较高的表选择MyISAM存储。如果该数据库需要一个用于查询的临时表,可以选择MEMORY存储引擎。
5.3.事务处理
5.3.1.事务的概念
事务由单独单元的一个或多个SQL语句组成,在这个单元中,每个MySQL语句是相互依赖的。而整个单独单元作为一个不可分割的整体,如果单元中某条SQL语句一旦执行失败或产生错误,整个单元将会回滚。所有受到影响的数据将返回到事物开始以前的状态;如果单元中的所有SQL语句均执行成功,则事物被顺利执行。
事务就是一组原子性的sql,或者说一个独立的工作单元。
事务就是说,要么mysql引擎会全部执行这一组sql语句,要么全部都不执行
5.3.2.事务的四个属性
1、原子性(atomicity):事务是由一个或一组相互关联的SQL语句组成,这些语句被认为是一个不可分割的单元,事务是一个整体,要么全部执行,要么一句也不执行。
2、一致性(consistency):对于数据库的修改是一致的,即多个用户查的的数据是一样的。一致性主要由mysql的日志机制处理,他记录数据的变化,为事务恢复提供跟踪记录。
3、隔离性(isolation):每个事务都有自己的空间,和其他发生在系统中的事务隔离开来,就是说两个事务不会交叉的执行,而且事务的结果只在他完全被执行时才能看到,这就涉及到事务的隔离级别
4、持久性(durability):但提交了这个事务之后对数据的修改更新就是永久的,不会无缘无故的回滚。当一个事务完成,数据库的日志已经被更新时,持久性即可发挥其特有的功效,在mysql中,如果系统崩溃或者数据存储介质被破坏,通过日志,系统能够恢复在重启前进行的最后一次成功更新,可以反应系统崩溃时处于执行过程的事物的变化。
5.3.3.事务的隔离性
1.在多用户的时候使用孤立性级别是很重要的,这样可以保证这些事务互不影响,保证数据库性能不受到影响。
2.mysql中提供的孤立级别有以下四种:
两个命令行客户端分别为A,B;不断改变A的隔离级别,在B端修改数据
(1)READ UNCOMMITTED(读未提交)【遗留问题:是不是要设置的全局的都为read uncommitted,如果只设置了某一个会有效吗?】
事务中的修改,即使没有提交,对其他会话也是可见的。
可以读取未提交的数据——脏读。脏读会导致很多问题,一般不使用这个隔离级别。
A:启动事务,此时数据为初始状态
B:启动事务,更新数据,但不提交
A:再次读取数据,发现数据已经被修改了,这就是所谓的“脏读”
B:回滚事务
A:再次读数据,发现数据变回初始状态
经过上面的实验可以得出结论,事务B更新了一条记录,但是没有提交,此时事务A可以查询出未提交记录。造成脏读现象。未提交读是最低的隔离级别。
(2)READ COMMITTED(提交后读或不可重复读)
一般数据库都默认使用这个隔离级别(mysql不是),这个隔离级别保证了一个事务如果没有完全成功(commit执行完),事务中的操作对其他会话是不可见的。
A:启动事务,此时数据为初始状态
B:启动事务,更新数据,但不提交
A:再次读数据,发现数据未被修改
B:提交事务
A:再次读取数据,发现数据已发生变化,说明B提交的修改被事务中的A读到了,这就是所谓的“不可重复读”
经过上面的实验可以得出结论,已提交读隔离级别解决了脏读的问题,但是出现了不可重复读的问题,即事务A在两次查询的数据不一致,因为在两次查询之间事务B更新了一条数据。已提交读只允许读取已提交的记录,但不要求可重复读,一致性发生变化。
(3)REPEATABLE READ(可重读)【读取的并不是最新的数据,所以会出现幻读,比如插入了一条记录,读取时因为没有读取最新的数据,所以没有看到最新的记录,当全部都提交后再插入相同的记录时又会提示插入的数据已存在了,重复读一般不出现幻读,如果幻读就结合锁一起使用,这就是为什么重复读为默认的isolation level】
别人对幻读的理解:
幻读,并不是说两次读取获取的结果集不同,幻读侧重的方面是某一次的 select 操作得到的结果所表征的数据状态无法支撑后续的业务操作。更为具体一些:select 某记录是否存在,不存在,准备插入此记录,但执行 insert 时发现此记录已存在,无法插入,此时就发生了幻读。
一个事务中多次执行统一读SQL,返回结果一样。
A:启动事务,此时数据为初始状态
B:启动事务,更新数据,但不提交
A:再次读取数据,发现数据未被修改
B:提交事务
A:再次读取数据,发现数据依然未发生变化,这说明这次可以重复读了
B:插入一条新的数据,并提交
A:再次读取数据,发现数据依然未发生变化,虽然可以重复读了,但是却发现读的不是最新数据,这就是所谓的“幻读”
A:提交本次事务,再次读取数据,发现读取正常了
由以上的实验可以得出结论,可重复读隔离级别只允许读取已提交记录,而且在一个事务两次读取一个记录期间,其他事务部的更新该记录。但该事务不要求与其他事务可串行化。例如,当一个事务可以找到由一个已提交事务更新的记录,但是可能产生幻读问题(注意是可能,因为数据库对隔离级别的实现有所差别)。像以上的实验,就没有出现数据幻读的问题。
(4)SERIALIZABLE(序列化)
以序列的形式处理事务,只有事务提交后,用户才能看到,但是该级别的隔离会影响mysql的性能,因为需要占用大量的资源,以保证使大量事务在任意时间不被用户看到,可能导致大量的超时现象和锁竞争。
A:启动事务,此时数据为初始状态
B:发现B此时进入了等待状态,原因是因为A的事务尚未提交,只能等待(此时,B可能会发生等待超时)
A:提交事务
B:发现插入成功
serializable完全锁定字段,若一个事务来查询同一份数据就必须等待,直到前一个事务完成并解除锁定为止。是完整的隔离级别,会锁定对应的数据表格,因而会有效率的问题。
3.事务孤立级的查看和修改:
查看当前会话的隔离级别:select @@tx_isolation;
查看系统的隔离级别:select @@global.tx_isolation;
这只当前会话隔离级别:set session transaction isolation level 设置的隔离级别
设置全局隔离级别:set global transaction isolation level 设置的隔离级别;
四种隔离级别:read uncommitted, read committed ,repeatable read ,serializable
三种数据问题:脏读,不可重复读,幻读
脏读(Drity Read):某个事务已更新一份数据,另一个事务在此时读取了同一份数据,由于某些原因,前一个RollBack了操作,则后一个事务所读取的数据就会是不正确的
不可重复读(Non-repeatable read):在一个事务的两次查询之中数据不一致,这可能是两次查询过程中间插入了一个事务更新的原有的数据。【另外一个事务对数据进行更新和删除操作】
幻读(Phantom Read):在一个事务的两次查询中数据笔数不一致,例如有一个事务查询了几列(Row)数据,而另一个事务却在此时插入了新的几列数据,先前的事务在接下来的查询中,就会发现有几列数据是它先前所没有的。【另外一个事务对数据进行插入操作】
5.4.锁
从上面我们可以看出,各个不同隔离级别的特点,为防止更新丢失,并不能单靠数据库事务控制器来解决,需要应用程序对要更新的数据加必要的锁来解决。
5.4.1.什么是并发控制
并发控制:当多个连接对记录进行修改时保证数据的一致性和完整性(比如两个不同的用户a和b登录数据库,同时,a对test表中的id为20的数据进行查询,b对test表中id为20的数据进行删除,这时可以会出现错误)
5.4.2.锁的定义
锁是计算机协调多个进程或线程并发访问某一资源的机制。
如何保证数据并发访问的一致性、有效性是锁在数据库必须解决的一个问题,锁冲突也是影响数据库并发访问性能的一个重要因素。从这个角度来说,锁对数据库而言显得尤其重要,也更加复杂。
5.4.3.锁是如何运作的
事务T在对某个数据对象(如表、记录等)操作之前,先向系统发出请求,对其加锁,加锁后事务T就对数据库对象有一定的控制,在事务T释放它的锁之前,其他事务不能更新此数据对象。
5.4.4.锁定机制的分类
锁定机制就是数据库为了保证数据的一致性而使各种共享资源在被并发访问访问变得有序所设计的一种规则。MySQL数据库由于其自身架构的特点,存在多种数据存储引擎,每种存储引擎所针对的应用场景特点都不太一样,为了满足各自特定应用场景的需求,每种存储引擎的锁定机制都是为各自所面对的特定场景而优化设计,所以各存储引擎的锁定机制也有较大区别。
5.4.4.1.共享锁( S锁)
共享锁也称为读锁,读锁允许多个连接可以同一时刻并发的读取同一资源,互不干扰;总的来说就是会阻塞其他事务修改数据。
5.4.4.2.排他锁( X锁)
排他锁也称为写锁,一个写锁会阻塞其他的写锁或读锁,保证同一时刻只有一个连接可以写入数据,同时防止其他用户对这个数据的读写;总的来说会阻塞其他事务读和写。
5.4.4.3.锁策略(锁的粒度)
锁的开销是较为昂贵的,锁策略其实就是保证了线程安全的同时获取最大的性能之间的平衡策略。
1、mysql锁策略:talbe lock(表锁)
总的来说:开销小,加锁快;不会出现死锁;锁定粒度大,发生锁冲突的概率最高,并发度最低。
详细:和行级锁定相反,表级别的锁定是MySQL各存储引擎中最大颗粒度的锁定机制。该锁定机制最大的特点是实现逻辑非常简单,带来的系统负面影响最小。所以获取锁和释放锁的速度很快。由于表级锁一次会将整个表锁定,所以可以很好的避免困扰我们的死锁问题。
缺陷:锁定颗粒度大所带来最大的负面影响就是出现锁定资源争用的概率也会最高,致使并发度大打折扣。
具体情况是:若一个用户正在执行写操作,会获取排他的“写锁”,这可能会锁定整个表,阻塞其他用户的读、写操作;
若一个用户正在执行读操作,会先获取共享锁“读锁”,这个锁运行其他读锁并发的对这个表进行读取,互不干扰。只要没有写锁的进入,读锁可以是并发读取统一资源的。
2、mysql锁策略:row lock(行锁)
总的来说:开销大,加锁慢;会出现死锁;锁定粒度最小,发生锁冲突的概率最低,并发度也最高。
详细:行级锁定最大的特点就是锁定对象的颗粒度很小,也是目前各大数据库管理软件所实现的锁定颗粒度最小的。由于锁定颗粒度很小,所以发生锁定资源争用的概率也最小,能够给予应用程序尽可能大的并发处理能力而提高一些需要高并发应用系统的整体性能。
缺陷:由于锁定资源的颗粒度很小,所以每次获取锁和释放锁需要做的事情也更多,带来的消耗自然也就更大了。此外,行级锁定也最容易发生死锁。
活锁:就是当锁释放后,总是会去执行最新在等待的事务,所以会导致中间有些等待的事务一致处于循环等待下去的状态。
5.4.4.4.其他
活锁
如果事务T1封锁了数据对象R后,事务T2也请求封锁R,于是T2等待,接着T3也请求封锁R。当T1释放了加载R上的锁后,系统首先批准T3的请求,T2只能继续等待。接着T4也请求封锁R,T3释放R上的锁后,系统又批转了T4的请求。这样的一直循环下去,事务T2就只能永远等待了,这样情况叫活锁。
死锁
当两个事务分别锁定了两个单独的对象,这时每一个事务都要求在另一个事务锁定的对象上获得一个锁,因此每一个事务都必须等待另一个事务释放占有的锁。这就发生了死锁
意向锁:解决行锁与表锁的冲突
事务A锁住了表中的一行,让这一行只能读,不能写。之后,事务B申请整个表的写锁。如果事务B申请成功,那么理论上它就能修改表中的任意一行,这与A持有的行锁是冲突的。数据库需要避免这种冲突,就是说要让B的申请被阻塞,直到A释放了行锁。数据库要怎么判断这个冲突呢?
普通认为两步:step1:判断表是否已被其他事务用表锁锁表。step2:判断表中的每一行是否已被行锁锁住。但是这样的方法效率很低,因为要遍历整个表。
所以就有了意向锁的存在。
在意向锁存在的情况下,事务A必须先申请表的意向共享锁,成功后再申请一行的行锁。
在意向锁存在的情况下,两步骤为:step1:判断表是否已被其他事务用表锁锁表。step2:发现表上有意向共享锁,说明表中有些行被共享行锁锁住了,因此,事务B申请表的写锁会被阻塞。
注意:申请意向锁的动作是数据库完成的,就是说,事务A申请一行的行锁的时候,数据库会自动先开始申请表的意向锁,不需要我们程序员使用代码来申请。
意向锁是一种表级锁,锁的粒度是整张表。结合共享与排他锁使用,分为意向共享锁(IS)和意向排他锁(IX)。意向锁为了方便检测表级锁和行级锁之间的冲突,故在给一行记录加锁前,首先给该表加意向锁。也就是同时加意向锁和行级锁
MyISAM引擎的锁机制
MyISAM只有表锁,其中又分为读锁和写锁,即表共享读锁(table read lock)和表独占写锁(table write lock)
首先明确:
1.MySQL认为写请求一般比读请求重要,如果两个事务同时请求,优先执行写。
2.MyISAM在执行查询语句(SELECT)前,会自动给涉及的所有表加读锁,在执行更新操作(UPDATE、DELETE、INSERT等)前,会自动给涉及的表加写锁,这个过程并不需要用户干预,因此用户一般不需要直接用LOCK TABLE命令给MyISAM表显式加锁【因为我们平时执行的select,update等语句时间比较短,所以执行完就解锁表了,所以我们看不出来表被自动加锁了的】
给MyISAM表显示加锁,一般是为了在一定程度模拟事务操作,实现对某一时间点多个表的一致性读取。例如,有一个订单表orders,其中记录有各订单的总金额total,同时还有一个订单明细表order_detail,其中记录有各订单每一产品的金额小计 subtotal,假设我们需要检查这两个表的金额合计是否相符,可能就需要执行如下两条SQL:
Select sum(total) from orders;
Select sum(subtotal) from order_detail;
这时,如果不先给两个表加锁,就可能产生错误的结果,因为第一条语句执行过程中,order_detail表可能已经发生了改变。因此,正确的方法应该是:
Lock tables orders read local, order_detail read local;
Select sum(total) from orders;
Select sum(subtotal) from order_detail;
Unlock tables;
注意事项:
在用LOCK TABLES给表显式加表锁时,必须同时取得所有涉及到表的锁,并且MySQL不支持锁升级。也就是说,在执行LOCK TABLES后,只能访问显式加锁的这些表【如果表使用别名,也需要对别名进行加锁】,不能访问未加锁的表;同时,如果加的是读锁,那么只能执行查询操作,而不能执行更新操作。其实,在自动加锁的情况下也基本如此,MyISAM总是一次获得SQL语句所需要的全部锁【即如果要对对个表加锁,就是一次性加,不然只对最后面的语句中的表进行加锁】。这也正是MyISAM表不会出现死锁(Deadlock Free)的原因。
实例:【对表进行加锁后,只能显示访问加锁的表】
对表test进行读锁定
查询表test【可以正常的查询】
查询其他表时报错【提示表test1没有在锁定表中】
用test表的别名进行查询同样会报相同的异常
当对表的别名锁定后,使用别名即可查询【查询语句是:select * from test as t; 但是查询原表也会报异常,select * from test同样会提示没在锁定表中的错误】
实例:【一次获得SQL语句所需要的全部锁】
先锁定表test后,再锁定test1
然后查询表test
查询表test1
从上面我们可以看出,最后锁定的是表test1,即最后的那个锁定语句才生效的,如果我们需要同时锁定多张表,那么就应该一次性锁定
这样我们都可以对表test,test1进行查询了
并发插入(Concurrent Inserts)
上文提到过MyISAM表的读和写是串行的,但这是就总体而言的。在一定条件下,MyISAM表也支持查询和插入操作的并发进行。
MyISAM存储引擎有一个系统变量concurrent_insert,专门用以控制其并发插入的行为,其值分别可以为0(NEVER)、1(AUTO)或2(ALWAYS)。
?当concurrent_insert设置为0时,不允许并发插入。
?当concurrent_insert设置为1时,如果MyISAM表中没有空洞(即表的中间没有被删除的行),MyISAM允许在一个进程读表的同时,另一个进程从表尾插入记录。这也是MySQL的默认设置。
?当concurrent_insert设置为2时,无论MyISAM表中有没有空洞,都允许在表尾并发插入记录。
查看concurrent_insert变量:show global variables like ‘%concurrent_insert%’ 或者 select @@concurrent_insert
设置concurrent_insert变量:set global concurrent_insert = 1
实例:【这里我们直接将concurrent_insert = 2来方便直观的演示】
A:将concurrent_insert = 2,并查看到表xiong的数据
B:查看表xiong的数据
A:锁定表xiong,使用:lock table xiong read local;
B:向xiong表中查询一条数据【可以直接插入成功,不需要等待】
总结:
上面的例子在LOCK TABLES时加了“local”选项,【但是这个只能是加的读锁(lock table read),而写锁是加不了local的】,其作用就是在满足MyISAM表并发插入条件的情况下,允许其他用户在表尾并发插入记录。当全局变量concurrent_insert为0时,一个session锁定了表,另外一个session就不能进行任何插入或修改操作;当全局变量concurrent_insert为1时(默认情况concurrent_insert的值就为1),一个session锁定了表,另外一个session可以对锁定的表(但是要确保这个表的中间没有被删除的行)进行插入操作;当全局变量concurrent_insert为2时(默认情况concurrent_insert的值就为1),一个session锁定了表,另外一个session可以对没锁定的表进行插入操作。
MyISAM的锁调度
MyISAM存储引擎的读锁和写锁是互斥的,读写操作是串行的。那么,一个进程请求某个 MyISAM表的读锁,同时另一个进程也请求同一表的写锁,MySQL如何处理呢?答案是写进程先获得锁。不仅如此,即使读请求先到锁等待队列,写请求后到,写锁也会插到读锁请求之前!这是因为MySQL认为写请求一般比读请求要重要。这也正是MyISAM表不太适合于有大量更新操作和查询操作应用的原因,因为,大量的更新操作会造成查询操作很难获得读锁,从而可能永远阻塞。这种情况有时可能会变得非常糟糕!幸好我们可以通过一些设置来调节MyISAM 的调度行为。
?通过指定启动参数low-priority-updates,使MyISAM引擎默认给予读请求以优先的权利。
?通过执行命令SET LOW_PRIORITY_UPDATES=1,使该连接发出的更新请求优先级降低。
?通过指定INSERT、UPDATE、DELETE语句的LOW_PRIORITY属性,降低该语句的优先级。
查看变量:select @@low_priority_updates;或者show global variables like ‘%low_priority%’
设置变量:set @@low_priority_updates = 1;
虽然上面3种方法都是要么更新优先,要么查询优先的方法,但还是可以用其来解决查询相对重要的应用(如用户登录系统)中,读锁等待严重的问题。
另外,MySQL也提供了一种折中的办法来调节读写冲突,即给系统参数max_write_lock_count设置一个合适的值,当一个表的读锁达到这个值后,MySQL就暂时将写请求的优先级降低,给读进程一定获得锁的机会。
上面已经讨论了写优先调度机制带来的问题和解决办法。这里还要强调一点:一些需要长时间运行的查询操作,也会使写进程“饿死”!因此,应用中应尽量避免出现长时间运行的查询操作,不要总想用一条SELECT语句来解决问题,因为这种看似巧妙的SQL语句,往往比较复杂,执行时间较长,在可能的情况下可以通过使用中间表等措施对SQL语句做一定的“分解”,使每一步查询都能在较短时间完成,从而减少锁冲突。如果复杂查询不可避免,应尽量安排在数据库空闲时段执行,比如一些定期统计可以安排在夜间执行。
Innodb引擎的锁机制
对于UPDATE、DELETE、INSERT语句,Innodb会自动给涉及的数据集加排他锁(X);对于普通SELECT语句,Innodb不会加任何锁。
InnoDB这种行锁实现特点意味着:只有通过索引条件检索数据,innoDB才使用行级锁,否则InnoDB将使用表锁,在实际开发中应当
Select * from tb_name where... for update; :排它锁
Select * from tb_name where... lock in share mode; :共享锁
同样可以使用lock table tb_name read等方式来给INNODB的表添加表锁,但是AUTOCOMMIT设为0,否则MySQL不会给表加锁;事务结束前,不要用UNLOCAK TABLES释放表锁,因为UNLOCK TABLES会隐含地提交事务;COMMIT或ROLLBACK产不能释放用LOCAK TABLES加的表级锁,必须用UNLOCK TABLES释放表锁,正确的方式见如下语句。
SET AUTOCOMMIT=0;
LOCAK TABLES t1 WRITE, t2 READ, ...;
[do something with tables t1 and here];
COMMIT;
UNLOCK TABLES;
6.索引
6.1.什么是索引
索引是存储引擎用于快速找到记录的一种数据结构
理解索引最简单的办法就是去看一看一本书的"索引"(目录)部分. 通过找到目录中对应的特定主题的页码,可快速定位到你要找的主题.
mysql中也一样,数据查找时,先看看查询条件是否命中某条索引,符合则通过索引找到相关数据返回,不符合则需要全表扫描,找到与条件符合的行返回.
6.2.MySQL中索引的优点和缺点和使用原则
6.2.1.优点
1、所有的MySql列类型(字段类型)都可以被索引,也就是可以给任意字段设置索引
2、大大加快数据的查询速度
6.2.2.缺点
1、创建索引和维护索引要耗费时间,并且随着数据量的增加所耗费的时间也会增加
2、索引也需要占空间,我们知道数据表中的数据也会有最大上限设置的,如果我们有大量的索引,索引文件可能会比数据文件更快达到上限值
3、当对表中的数据进行增加、删除、修改时,索引也需要动态的维护,降低了数据的维护速度。
6.2.3.使用原则
6.2.3.1.索引选取类型
通过上面说的优点和缺点,我们可以知道,并不是每个字段度设置索引就好,也不是索引越多越好,而是需要自己合理的使用。
1、对经常更新的表就避免对其进行过多的索引,对经常用于查询的字段应该创建索引,
2、数据量小的表最好不要使用索引,因为由于数据较少,可能查询全部数据花费的时间比遍历索引的时间还要短,索引就可能不会产生优化效果。
3、在一同值少的列上(字段上)不要建立索引,比如在学生表的"性别"字段上只有男,女两个不同值。相反的,在一个字段上不同值较多可是建立索引。
6.2.3.2.什么场景不适合创建索引
?第一,对于那些在查询中很少使用或者参考的列不应该创建索引。这是因为,既然这些列很少使用到,因此有索引或者无索引,并不能提高查询速度。相反,由于增加了索引,反而降低了系统的维护速度和增大了空间需求。
?第二,对于那些只有很少数据值的列也不应该增加索引。因为本来结果集合就是相当于全表查询了,所以没有必要。这是因为,由于这些列的取值很少,例如人事表的性别列,在查询的结果中,结果集的数据行占了表中数据行的很大比例,即需要在表中搜索的数据行的比例很大。增加索引,并不能明显加快检索速度。
?第三,对于那些定义为text, image和bit数据类型的列不应该增加索引。这是因为,这些列的数据量要么相当大,要么取值很少。
?第四,当修改性能远远大于检索性能时,不应该创建索引(即经常做增删改操作的表)。这是因为,修改性能和检索性能是互相矛盾的。当增加索引时,会提高检索性能,但是会降低修改性能。当减少索引时,会提高修改性能,降低检索性能。因 此,当修改性能远远大于检索性能时,不应该创建索引。
?第五,不会出现在where条件中的字段不该建立索引。
?第六,当表记录太少时不应该建立索引
6.2.3.3.什么场景适合创建索引
?表的主键、外键必须有索引;
?数据量超过300的表应该有索引;
?经常与其他表进行连接的表,在连接字段上应该建立索引(其实这里说的就是外键列)
?经常出现在Where子句中的字段,加快判断速度,特别是大表的字段,应该建立索引,建立索引
?经常用到排序的列上,因为索引已经排序。
?经常用在范围内搜索的列上创建索引,因为索引已经排序了,其指定的范围是连续的
?经常用到搜索的列上,可以加快搜索的速度
6.3.索引的分类
索引主要分为单列索引(普通索引,唯一索引,主键索引)、组合索引、全文索引、空间索引
6.3.1.单列索引
1.普通索引:MySQL中基本索引类型,没有什么限制,允许在定义索引的列中插入重复值和空值,纯粹为了查询数据更快一点。
2.唯一索引:索引列中的值必须是唯一的,但是允许为空值。
3.主键索引:是一种特殊的唯一索引,不允许有空值。
6.3.2.组合索引
在表中的多个字段组合上创建的索引,只有在查询条件中使用了这些字段的左边字段时,索引才会被使用,使用组合索引时遵循最左前缀集合。
6.3.2.1.有哪些情况可以命中索引
?左前缀:如组合索引为(name,age,gender),那么(name);(name,age);(name,age,gender)三种方式就会命中索引
?对于使用 like 的查询,后面如果是常量并且只有%号不在第一个字符,索引才可能会被使用,比如select * from index2 where name like ‘xiong%‘会命中索引,但是select * from index2 where name like ‘%xiong%‘不会命中索引
?一般索引都要求为not null的,所以使用select * from index2 where name is null/ is not null不会命中到索引
?用 or 分割开的条件,需要用and连接的才能用到索引,如explain select * from test1 where name = ‘xiong’ and age =23可以命中索引,但是explain select * from test1 where name = ‘xiong’ or age =23不能命中索引
?如果列是字符型,传入的是数字,而不是‘’(加双引号)的内容,不会使用索引,如explain select * from test1 where name = 23这样就不会命中索引
?如果查询条件中的索引列加入了函数或者进行运算也不会命中索引,如xiong的组合索引为:(age,gender)那么explain select * from xiong where age+1=20就不会命中索引
6.3.3.全文索引
全文索引是为了使得“关键词搜索”功能更加的高效能,如提高站内搜索效率,只有在MyISAM引擎上才能使用,只能在CHAR,VARCHAR,TEXT类型字段上使用全文索引,mysql现在一般使用的版本其实也支持全文索引,索引类型为fulltext,只不过他的功能还不够强大,对中文的支持不够好,无法智能分词
需要工作的程序:索引程序,分词程序,数据库。
如果对大的文本进行搜索,使用全文索引而不用使用 like ‘%…%’。
6.3.4.空间索引
空间索引是对空间数据类型的字段建立的索引,MySQL中的空间数据类型有四种,GEOMETRY、POINT、LINESTRING、POLYGON。在创建空间索引时,使用SPATIAL关键字。
要求,引擎为MyISAM,创建空间索引的列,必须将其声明为NOT NULL
6.4.索引操作
6.4.1.创建索引
6.4.1.1.创建普通索引
create table tb_name(
id tinyint not null,
name varchar(20) not null,
key/index [索引名](id)) #这里使用index或者key都可以
);
创建普通索引时,使用key和index都可以,另外索引名我们可以自己添加,如果创建时没有写索引名,那么MySQL会自动帮我们用字段名当作索引名。
6.4.1.2.创建唯一索引
create table tb_name(
id tinyint not null,
name varchar(20) not null,
unique index/key [索引名](id) #这里使用index或者key都可以
);创建唯一索引时,使用key和index都可以,另外索引名我们可以自己添加,如果创建时没有写索引名,那么MySQL会自动帮我们用字段名当作索引名。
6.4.1.3.创建主键索引
Create table tb_name(
id tinyint not null,
name varchar(20) not null,
Primary key(id) #记得使用的是primary key ,如果使用primary index不行
);
primary key ,unique key,foreign key 三个创建后会自动创建索引,所以创建这三个索引其实是和创建键时一致的。
key 和index的区别:
key 是数据库的物理结构,它包含两层意义,一是约束(偏重于约束和规范数据库的结构完整性),二是索引(辅助查询用的)
index是数据库的物理结构,它只是辅助查询的,它创建时会在另外的表空间(mysql中的innodb表空间)以一个类似目录的结构存储。索引要分类的话,分为前缀索引、全文本索引等;
因此,索引只是索引,它不会去约束索引的字段的行为(那是key要做的事情)
6.4.1.4.创建单列索引
这个其实就不用在说了,前面几个就是单列索引
6.4.1.5.创建组合索引
create table tb_name(
id tinyint not null,
name varchar(20) not null,
age tinyint not null,
Info varchar(100),
index(id,name,age)
);
组合索引就是在多个字段上创建一个索引
组合索引就是遵从了最左前缀,利用索引中最左边的列集来匹配行,这样的列集称为最左前缀,不明白没关系,举几个例子就明白了,例如,这里由id、name和age三个字段构成的索引,索引行中就按id/name/age的顺序存放,索引可以索引下面字段组合(id,name,age)、(id,name)或者(id)。如果要查询的字段不构成索引最左面的前缀,那么就不会使用索引,比如,age或者(name,age)组合就不会使用索引查询。
例如在tb表中:
explain select * from tb where id = 1 and name = ‘xiong‘\G;
在tb表中,查询(age,name)字段,这样就不会使用索引查询。来看看结果
explain select * from tb where age = 3 and name = ‘ying’\G;
6.4.1.6.创建全文索引
全文索引可以用于全文搜索,但只有MyISAM存储引擎支持FULLTEXT索引,并且只为CHAR、VARCHAR和TEXT列服务。索引总是对整个列进行,不支持前缀索引
create table tb(
Id tinyint not null,
Name varchar(20) not null,
Age tinyint not null,
Info varchar(255),
Fulltext index Fulltxt(info)
)engine = myisam;
在使用全文搜索时,需要借助MATCH函数,并且其全文搜索的限制比较多,比如只能通过MyISAM引擎,比如只能在CHAR,VARCHAR,TEXT上设置全文索引。比如搜索的关键字默认至少要4个字符,比如搜索的关键字太短就会被忽略掉。
6.4.1.7.创建空间索引
空间索引也必须使用MyISAM引擎, 并且空间类型的字段必须为非空。 这个空间索引具体能干嘛我也不知道,可能跟游戏开发有关,可能跟别的东西有关,等遇到了自然就知道了,现在只要求能够创建出来
6.4.2.在已经存在的表上创建索引
6.4.2.1.为表添加索引
格式:ALTER TABLE 表名 ADD[UNIQUE|FULLTEXT|SPATIAL] [INDEX|KEY] [索引名] (索引字段名)[ASC|DESC]
Alter table tb_name add index(column1)
Alter table tb_name add unique index(column1)
Alter table tb_name add primary key(columnn1)
Alter table tb_name add index(column1,column2)
Alter table tb_name add fulltext index(column1)
Alter table tb_name add spatial index(column1)
6.4.2.2.使用CREATE INDEX创建索引
格式:CREATE [UNIQUE|FULLTEXT|SPATIAL] [INDEX|KEY] 索引名称 ON 表名(创建索引的字段名[length])[ASC|DESC]
Create index 索引名称 on tb_name(column)
Create unique index 索引名称 on tb_name(column)
create primary key 索引名称 on tb_name(column)
6.4.3.删除索引
6.4.3.1.格式一
Alter table tb_name drop index index_name;(通过show index from tb_name来查看index_name)
6.4.3.2.格式二
Drop index index_name on tb_name;(通过show index from tb_name来查看index_name)
7.函数
7.1.变量
7.1.1.用户自定义变量
7.1.1.1.局部变量
局部变量一般用在sql语句块中,比如存储过程的begin/end。其作用域仅限于该语句块,在该语句块执行完毕后,局部变量就消失了。
局部变量一般用declare来声明,可以使用default来说明默认值。
create function add( in a int, in b int) returns int
begin
declare c int default 0;
set c = a + b;
return c;
End
7.1.1.2.用户变量
用户变量的作用域要比局部变量要广。用户变量可以作用于当前整个连接,但是当当前连接断开后,其所定义的用户变量都会消失,主要是加有@符号的变量。
用户变量使用如下(这里我们无须使用declare关键字进行定义,可以直接这样使用:select @变量名来查看用户变量)
第一种是执行SET语句
SET @var_name = expr [, @var_name = expr] ...
对于SET,可以使用=或:=作为分配符。分配给每个变量的expr可以为整数、实数、字符串或者NULL值。
实例:
set @a = 32; 使用【=】的方式定义一个用户变量a并赋值为32
select @a; 查看用户变量a的值
set @b := 99; 使用【:=】的方式定义一个用户变量b并赋值为99
select @b; 查看用户变量b的值
第二种是使用SELECT语句
使用SELECT定义用户变量只能使用:=作为分配符(因为在非SET语句中 = 被视为一个‘比较操作符’)
实例:
select @a := 99+1,@a; 使用select定义了变量a,并查看a的值
7.1.2.系统变量
MySQL服务器维护两种变量:全局变量影响服务器整体操作;会话变量影响具体客户端连接的操作。
7.1.2.1.全局变量
当服务器启动时,它将所有全局变量初始化为默认值。这些默认值可以在配置文件(my.ini)中或在命令行中指定的选项进行更改。全局变量作用于server的整个生命周期,但是不能重启。即重启后所有设置的全局变量均失效。要想让全局变量重启后继续生效,需要更改相应的配置文件。服务器启动后,通过连接服务器并执行SET GLOBAL var_name语句,可以动态更改这些全局变量。要想更改全局变量,必须具有SUPER权限。
要想查看一个GLOBAL变量的值,使用下面的语法:
mysql> SELECT @@global.sort_buffer_size;
mysql> SHOW GLOBAL VARIABLES like ‘sort_buffer_size‘;
要想设置一个GLOBAL变量的值,使用下面的语法:
mysql> SET GLOBAL sort_buffer_size=value;
mysql> SET @@global.sort_buffer_size=value;
7.1.2.2.会话变量
服务器还为每个连接的客户端维护一系列会话变量。在连接时使用相应全局变量的当前值对客户端的会话变量进行初始化,也就是说,如果在建立会话以后,没有手动更改过会话变量与全局变量的值,那所有这些变量的值都是一样的(与全局变量的值一样)。对于动态会话变量,客户端可以通过SET SESSION var_name语句更改它们。设置会话变量不需要特殊权限,但客户端只能更改自己的会话变量,而不能更改其它客户端的会话变量。当前连接断开后,其设置的所有会话变量均会自动释放
要想查看一个SESSION变量的值,使用下面的语法:
mysql> SELECT @@sort_buffer_size;
mysql> SELECT @@session.sort_buffer_size;
mysql> SHOW SESSION VARIABLES like ‘sort_buffer_size‘;
要想设置一个SESSION变量的值,使用下面的语法:
mysql> SET SESSION sort_buffer_size=value;
mysql> SET @@session.sort_buffer_size=value;
mysql> SET sort_buffer_size=value;
7.1.2.3.用户变量与会话变量的区别
相同点:
1.用户变量和会话变量在当前客户端退出后都会自动释放
2.修改只对当前客户端有影响,不会影响其他的客户端
不同点:
1.用户变量是用户自身定义的变量
2.会话变量是在客户端建立连接时会创建一系列会话变量,并以全局变量的当前值初始化会话变量(会话变量相当于系统全局变量的副本)
7.2.什么是函数
mysql中的函数与存储过程类似,都是一组SQL集;
7.3.与存储过程的区别
1.存储过程实现的功能相对复杂,函数针对性较强,
2.存储过程可以返回多个值,函数只能有一个返回值
3.存储过程一般独立执行,函数可以作为 sql 语句的组成部分来出现
4.函数可以对数据库或表进行操作,而存储过程一般是对数据表中的数据进行操作
7.4.mysql自带函数
7.5.自定义函数
两个必要的条件:
1.参数:参数并非是每个函数所必须的,有的函数就没有参数,参数可以是任意类型的,理论上参数的个数不得超过1024个
2.返回值:函数必须有返回值,但是只能返回一个值,返回值同样可以是任意类型的
查看函数:
1. show function status [like ‘pattern‘]; -- 查看所有自定义函数, 自定义函数只能在本数据库使用。
2. show create function 函数名; -- 查看函数创建语句
删除函数:
drop function 函数名;
创建不带参数的自定义函数:
create function f()
returns varchar(20)
return date_format(now(),‘%y年%m月%d日 %h时%i分%s秒‘)
创建带参数的自定义函数
-- 计算1 ~ 指定数据之间的和
delimiter //
create function my_sum(x int) returns int
begin
set @i = 1;
set @sum = 0;
while @i <= x
do
set @sum = @sum + @i;
set @i = @i + 1;
end while;
return @sum;
end
//
delimiter ;
调用:
select my_sum(20);
创建具有复合体的复杂自定义函数:
-- 求1 ~ 指定数之前的和,但5的倍数不加
delimiter $$
create function my_sum2(x int) returns int
begin
declare i int default 1;
declare sum int default 0;
sumwhile:while i <= x do
if i % 5 = 0 then
set i = i + 1;
iterate sumwhile;
end if;
set sum = sum + i;
set i = i + 1;
end while;
return sum;
end
$$
delimiter ;
7.6.常用函数
7.6.1.MySQL字符函数
7.6.1.1.ltrim()
删除字符最左边的空格
select ltrim(‘ xiong‘) =>xiong
7.6.1.2.rtrim()
删除字符最右边的空格
selct rtrim(‘ xiong ‘) => xiong
7.6.1.3.trim()
可以选择删除字符最左边或最右边的其他字符
1.select trim(leading ‘?‘ from ‘??xiong??‘) =>xiong??(前导,删除前面的)
2.select trim(trailing ‘?‘ from ‘??xiong??‘) =>??xiong (后导,删除后面的)
3.select trim(both ‘?‘ from ‘??xiong??‘) =>xiong (删除前后的)需要注意的是,如果是select trim(both ‘??’ from ‘???xiong???‘)的话,结果为=>?xiong?,还有一个?是没有删除的,因为后面的?能去除的刚好是前面?的倍数。
7.6.1.4.substring()
截取字符串
select substring(‘xiong‘,1,2) =>xi 可以看出mysql中下标是从1开始而不是从0开始的
select substring(‘xiong‘,2) =>iong 如果没有第三个参数,则截取到最后
select substring(‘xiong‘,-1) =>g 可以看出用负数从最后开始,第一个为-1,没有第三个参数,截取到最后
7.6.1.5.not like 或者like
我们知道与like经常一起使用的通配符有如下两个:%:表示任意个数的字符; _ :表示任意一个字符。
但有时,要匹配的模式包含通配符,例如10%,_20等。在这些情况下,可以使用ESCAPE子句指定转义字符,以便MySQL将通配符解释为文字字符。如果没有明确指定转义字符,默认的转义字符是反斜杠"\"
如:select * from test1 where name like ‘%\%%’; 就表示查找name包含%的,使用escape
select * from test1 name like ‘%1%%‘ escape ‘1‘;
或者
select * from test1 name like ‘%x%%‘ escape ‘x‘;
7.6.2.mysql数值运算符合函数
7.6.2.1.ceil()
向上取整,即往大的方向取
select ceil(3.001) =>4
select ceil(-3.2) => -3
7.6.2.2.floor()
向下取整,即往小的方向取
select floor(3.99) =>3
select floor(-3.2) =>-4
7.6.2.3.div
整数整除,除了之后取整,与上面不同的是不管是正负数都是去掉小数部分,取整数部分
select 20 div 3 =>6
select -20 div 3 =>-6
7.6.2.4.mod
和%一样,表示取余数
select 5 mod 3 =>2
sleect 5 % 3 =>2
slelct 5.3 mod 3 =>2.3
7.6.2.5.power()
乘方
select power(3,3) =>27
7.6.2.6.round()
四舍五入,可以指定保留小数的位数
select round(4.325,2) =>4.33
7.6.2.7.truncate()
截断,同样也可以指定截断保留几个小数
select truncate(323.437,2) =>323.43
7.6.2.8.not / between ... and
在或不在范围之内,闭区间,0表示假,1表示真
select 15 between 1 and 20 =>
7.6.2.9.is null / is not null
为null或不为null,0表示假,1表示真
select null is null =>1,只有null is null才为真,其他都为假
7.6.2.10.in / not in
在集合之内,0表示假,1表示真
select 5 in (3,5,34,55) =>1
7.6.3.mysql比较运算符和函数
7.6.4.mysql日期时间函数
7.6.4.1.now()
返回当前的日期和时间
select now() => 2019-04-03 14:23:19
7.6.4.2.curdate()
返回当前的日期
select curdate() => 2019-04-03
7.6.4.3.curtime()
返回当前的时间
select curtime() =>14:24:04
7.6.4.4.date_add()
返回变化后的时间,interval表示间隔,前面的时间加上后面的间隔
select date_add(‘2014-03-12‘,interval 365 day) =>2015-03-12
select date_add(‘2014-03-12‘,interval 3 week) =>2014-04-02
7.6.4.5.datediff()
返回时间差,前一个时间减去后一个时间
select datediff(‘2013-03-12‘,‘2014-03-12‘) =>-365
7.6.4.6.date_fomat()
格式化时间
select date_format(‘2014-03-12‘,‘%m/%d/%Y‘) =>03/12/2014
常用参数格式
%a 缩写星期名
%b 缩写月名
%c 月,数值
%D 带有英文前缀的月中的天
%d 月的天,数值(00-31)
%e 月的天,数值(0-31)
%f 微秒
%H 小时 (00-23)
%h 小时 (01-12)
%I 小时 (01-12)
%i 分钟,数值(00-59)
%j 年的天 (001-366)
%k 小时 (0-23)
%l 小时 (1-12)
%M 月名
%m 月,数值(00-12)
%p AM 或 PM
%r 时间,12-小时(hh:mm:ss AM 或 PM)
%S 秒(00-59)
%s 秒(00-59)
%T 时间, 24-小时 (hh:mm:ss)
%U 周 (00-53) 星期日是一周的第一天
%u 周 (00-53) 星期一是一周的第一天
%V 周 (01-53) 星期日是一周的第一天,与 %X 使用
%v 周 (01-53) 星期一是一周的第一天,与 %x 使用
%W 星期名
%w 周的天 (0=星期日, 6=星期六)
%X 年,其中的星期日是周的第一天,4 位,与 %V 使用
%x 年,其中的星期一是周的第一天,4 位,与 %v 使用
%Y 年,4 位
%y 年,2 位
7.6.5.mysql信息函数
7.6.5.1.connection_id()
返回连接ID
select connection_id() =>46
7.6.5.2.database()
返回当前正在使用的数据库
select database(); =>xiong
7.6.5.3.last_insert_id()
返回最后插入的记录的id,注意表中必须有id,且是primary key和auto_increment,另外如果同时插入多条记录,那么返回的Id为最先插入的记录的Id,而不是最后的
select last_insert_id(); =>26
7.6.5.4.user()
返回当前的用户
select user();
7.6.5.5.version()
使用MySQL的版本
select version();
7.6.6.mysql聚合函数
7.6.6.1.AVG()
平均值
select avg(age) from test1;
7.6.6.2.COUNT()
总计数
select count(*) from test1;
7.6.6.3.MAX()
最大值
select max(age) from test1;
7.6.6.4.MIN()
最小值
select min(age) from test1;
7.6.6.5.SUM()
求和
select sum(age) from test1;
7.6.7.mysql加密函数
MD5()
信息摘要算法,web页面做准备时常用
PASSWORD()
set password = password(‘newPassword‘) 用来更改密码
如我们使用test账号登录进入mysql
然后使用set password = password(‘1111111’);来修改test账号的密码
然后使用密码为111111的进行登录
8.存储过程
8.1.什么是存储过程
首先我们先来了解下MySQL中执行SQL语句的一个过程,当输入SQL命令后,MySQL引擎会对SQL命令进行分析,看SQL命令语法是否正确,如果正确,那么继续对SQL命令进行编译,编译成可识别的命令后执行命令,将执行的结果返回给客户端。
1.如果我们需要进行一些复杂的逻辑功能时,我们每次都需要手动编写这个比较复杂的SQL语句(每次编写代码耗时费劲)
2.在执行上面我们说的这个复杂的语句时,就会在MySQL中执行SQL语句的方式一步一步的进行分析和编译(执行效率不高)
为了解决上面两个问题,所以我们引入了存储过程。
存储过程是SQL语句和控制语句的预编译集合,以一个名称存储并作为一个单元处理,(可以理解为,存储过程实际上就是SQL语句与控制语句被预编译后存储的结果),是一组SQL语句集,功能强大,可以实现一些比较复杂的逻辑功能,类似于JAVA语言中的方法。
存储过程跟触发器有点类似,都是一组SQL集,但是存储过程是主动调用的,且功能比触发器更加强大,触发器是某件事触发后自动调用;
早期的MySQL没有存储过程(Stored Procedure)语言,这是对习惯于企业级数据库的程序员的最大限制。不过在MySQL 5.0以后的版本中,MySQL也开始支持存储过程。
8.2.存储过程的特点
增强SQL语句的功能和灵活性:可以通过控制语句对流程进行控制和判断,有输入输出参数,可以声明变量,有if/else, case,while等控制语句,通过编写存储过程,可以实现复杂的逻辑功能;
函数的普遍特性:模块化,封装,代码复用;
实现较快的执行速度:客户端第一次调用存储过程时,MySQL引擎会对其进行语法分析、编译等操作,然后将编译结果存储到内存中,所以第一次和之前的效率一样,然而以后会直接调用内存中的编译结果,效率提高。
减少网络流量:例如删除一个记录,我们原本要输入DELETE FROM xx WHERE ...; 要传输的字符较多,如果写成存储过程,就只要调用存储过程的名字和相应参数就行,传输的字符数量较少,所以减少了网络流量
8.3.存储过程弊端
1.不同数据库,语法差别很大,移植困难,换了数据库,需要重新编写;
2.不好管理,把过多业务逻辑写在存储过程不好维护,不利于分层管理,容易混乱,一般存储过程适用于个别对性能要求较高的业务,其它的必要性不是很大;
8.4.创建一个简单的存储过程
语法:
CREATE PROCEDURE 过程名([[IN|OUT|INOUT] 参数名 数据类型[,[IN|OUT|INOUT] 参数名 数据类型…]]) [特性 ...] 过程体,过程体的开始与结束使用BEGIN与END进行标识。
MySQL存储过程的参数用在存储过程的定义,共有三种参数类型,in,out,inout:
1.in输入参数:表示该参数的值必须在调用存储过程时指定并传入存储过程中,在存储过程中修改该参数的值不会被返回,默认情况下存储过程的参数的类型就为in类型的;
2.out输出参数:该参数在调用存储过程时需要被指定(但是参数的值并没有真正的传入到存储过程中),该参数可在存储过程内部被改变,并会被返回;
3.inout输入输出参数:相当于in和out的结合,调用时指定,并且可被改变和返回;
8.4.1.in参数实例
delimiter //
create procedure in_par(in a int)
begin
select a;
set a = 10;
select a;
end
//
delimiter ;
set @b = 3;
call in_par(@b);
select @b;
调用的结果:
从这个例子我们可以看出:
in类型的参数确实被传入到了存储过程中,所以在存储过程中第一个select a;返回的结果就是传入的参数的值3,在存储过程中修改了a的值,所以第二个select a;返回的就是修改的值10;因为in类型的参数即使在存储过程中修改了也不会返回的,所以在调用存储过程后的select @b返回的结果还是3,并不是被存储过程修改的。
8.4.2.out参数实例
delimiter //
create procedure out_par(out a int)
begin
select a;
set a = 10;
select a;
end
//
delimiter ;
set @b = 3;
call out_par(@b);
select @b;
调用结果:
从上面的实例我们可以看出:
out类型的参数的值实际上并没有传入到存储过程中去,所以在存储过程中的第一个select a;返回的值是NULL,而在存储过程中使用set a = 10修改了变量a的值,所以在存储过程中的第二个select a;返回的结果就是10,因为out类型的参数实际上会被存储过程修改并返回,所以在调用存储过程后的select @b返回的就是被存储过程修改了后的值10
8.4.3.inout参数例子
#存储过程INOUT参数
delimiter//
create procedure inout_par(inout a int)
begin
select a;
set a = 10;
select a;
end
//
delimiter ;
set @b = 3;
call inout_par(@b);
select @b;
调用结果:
从上面的例子我们可以看出:
inout参数就相当于in和out的结合,所以在存储过程中,第一个select a;返回的结果就是传入进去的值3,第二个select a;返回的就是修改后的值10,因为也结合了out参数类型的,所以第三个select @b返回的就是被存储过程修改后的值10
8.5.存储过程中的控制语句
8.5.1.变量作用域
内部变量在其作用域范围内享有更高的优先权,当执行到end时,内部变量消失,不再可见了,在存储过程外再也找不到这个内部变量,但是可以通过out参数或者将其值指派给会话变量来保存其值。
#变量作用域
delimiter //
create procedure proc()
begin
declare x1 varchar(5) default ‘outer‘;
begin
declare x1 varchar(5) default ‘inner‘;
select x1;
end;
select x1;
end;
//
delimiter ;
#调用
call proc();
执行结果:
8.5.2.条件语句
8.5.2.1.if-then-else
#条件语句if-then-else
drop procedure if exists proc3;
delimiter //
create procedure proc3(in parameter int)
begin
declare var int;
set var=parameter+1;
if var=0 then
insert into t values (17);
end if ;
if parameter=0 then
update t set s1=s1+1;
else
update t set s1=s1+2;
end if ;
end ;
//
delimiter ;
8.5.2.2.case-when-then-else
delimiter //
create procedure proc4 (in parameter int)
begin
declare var int;
set var=parameter+1;
case var
when 0 then
insert into t values (17);
when 1 then
insert into t values (18);
else
insert into t values (19);
end case ;
end ;
//
delimiter ;
8.5.3.循环语句
8.5.3.1.while-do…end-while
delimiter //
create procedure proc5()
begin
declare var int;
set var=0;
while var<6 do
insert into t values (var);
set var=var+1;
end while ;
end;
//
delimiter ;
call proc5();
8.6.存储过程的查看
8.6.1.show procedure status where db = ‘db_name’
查看某一个数据库下创建的存储过程
8.6.2.show create procedure pro_name
主要用于查看存储过程的创建结构
8.7.存储过程的删除
8.7.1.drop procedure pro_name1,pro_name2
可支持同时删除多个存储过程
9.触发器
9.1.什么是触发器
触发器(trigger):监视某种情况,并触发某种操作,它是提供给程序员和数据分析员来保证数据完整性的一种方法,它是与表事件相关的特殊的存储过程,它的执行不是由程序调用,也不是手工启动,而是由事件来触发,例如当对一个表进行操作( insert,delete, update)时就会激活它执行。简单的说,就是一张表发生了某件事(插入、删除、更新操作)后,自动触发了预先编写好的若干条SQL语句的执行。
触发器是由事件来触发某个操作,这些事件包括insert语句、update语句和delete语句。当数据库系统执行这些事件时,会激活触发器执行相应操作。MySQL从5.0.2开始支持触发器。
从这种意义上讲触发器是一个特殊的存储过程。
触发器经常用于加强数据的完整性约束和业务规则等。 触发器创建语法四要素:
1.监视地点(table)
2.监视事件(insert/update/delete)
3.触发时间(after/before)
4.触发事件(insert/update/delete)
触发器基本语法如下所示:
create trigger trigger_name trigger_time trigger_event
on tb_name for each row trigger_stmt
其中:trigger_time是触发器的触发时间,可以为before(在检查约束前触发)或after(在检查约束后触发);trigger_event是触发器的触发事件,包括insert、update和delete;可以使用old和new来引用触发器中发生变化的记录内容;new表示当触发插入和更新事件时可用,我们可以简单的认为插入或更新的数据对于表来说就是new的;new就指向的是被操作的记录,old表示当触发删除和更新事件时可用,简单的认为删除或更新的数据对于表来说就是old的,old指向的是被操作的记录;(可结合示例理解)。trigger_stmt为SQL语句体。
9.2.特点及作用
9.2.1.特点
?触发事件的操作和触发器里的SQL语句是一个事务操作,具有原子性,要么全部执行,要么都不执行。
?MySQL中insert,update,delete三种事件支持触发器。
?创建触发器每个表同类事件有且仅有一个对应触发器,也就是说对同一个表相同触发时间的相同触发事件,只能定义一个触发器。
?只有表才支持触发器,视图不支持(临时表也不支持)。
?触发器按每个表每个事件每次的定义,每个表每个事件每次只允许一个触发器;因此每个表最多支持6个触发器(每条insert、update和delete的之前和之后),单一触发器不能与多个事件或多个表关联。
9.2.2.作用
保证数据的完整性,起到约束的作用。
9.3.触发器的查看
9.3.1.查看所有触发器
show triggers;
9.3.2.查看某个具体的触发器
show create trigger trigger_name;
9.4.触发器的删除
drop trigger trigger_name
9.5.实例
9.5.1.插入语句的insert触发器
创建一个学生表student,其中id为主键,且自增
插入具体的数据后查询数据如下
创建一个成绩表cj,number为主键,且自增
该成绩表目前没有值,先需要设计一个触发器,当增加新的学生时,需要在成绩表中插入对应的学生信息,至于具体score_math、score_chinese、score_english后面由老师打分更新即可。
那么,如何设计触发器呢?
1.首先它是一个插入Insert触发器,是建立在表student上的;
2.然后触发时间是after,插入后的事件;
3.事件内容是插入成绩表,主需要插入学生的学号和姓名,number为自增,而成绩目前不需要。
注意:new表示student中新插入的值,即新插入时的那条记录,所以new.id表示的就是新插入student中的id的值,new.name表示新插入student中的name的值。
具体如下:
然我我们就可以验证了
首先我们可以查看到表cj是无数据的
在student表中插入一条记录insert into student values(null,’F’,123456,’2019-11.17);
我们可以看到表student已经新增了一条记录
然后我们再去看cj表时,已经通过触发器新增了一条数据,获取的id和name刚好就是新增记录中的id和name
9.5.2.更新语句的更新触发器
我们同样按上面创建的student和cj表做示例。当对表student中的记录进行更新后,相应的也要对表cj中的记录进行更新。
创建一个更新触发器
我们将最后一条记录的name更新为xiong
再次查看表student时,最后一条记录已经被更新
然后我们在看表cj时,同样su_name同样也做了更新操作
9.5.3.删除语句的删除触发器
我们同样以上面的student和cj表作为示例表,当删除了表student中的一条数据后,也相应的要将表cj中对应的记录也删除掉。
创建一个删除触发器,需要注意的是,因为是删除操作,所以需要使用的是old,因为是对以前的记录进行处理
然后我们删除最后一条记录试试
然后我们再看表cj时,已经删除了对应的id=7,name=’xiong’的记录了,所以表中没有数据了
9.6.弊端
增加程序的复杂度,有些业务逻辑在代码中处理,有些业务逻辑用触发器处理,会使后期维护变得困难;
MySQL Trigger没有很好的调试、管理环境,难于在各种系统环境下测试,测试比MySQL存储过程要难,所以建议在生成环境下,尽量用存储过程来代替MySQL触发器。
10.视图
10.1.什么是视图
通俗的讲,视图就是一条select语句执行后返回的结果集,是一个虚拟表,视图本身没有数据,行和列数据来自由定义视图的查询所引用的表,并且在引用视图时动态执行相应的select语句完成获得相应的数据
10.2.视图的特性
1 视图的列可以来自不同的表,是表的抽象和逻辑意义上建立的关系。
2 视图是由基本表(实表)产生的表(虚表)。
3 视图的建立和删除不影响基本表。
4 对视图的内容更新(添加、删除、修改)直接影响基本表。
5 当视图来自多个基本表时,对数据的修改是有条件的(具体可以看下面)
10.3.视图的作用
1、 视图能简化用户操作
关键信息来源于多个复杂关联表,可以创建视图提取我们需要的信息,简化操作;
2、视图能够对机密数据提供安全保护
数据库授权命令不能限定到特定行和特定列,但是通过合理创建视图,可以把权限限定到行列级别。
10.4.使用场合
1.尽量使用视图完成读操作
2.可以隐藏一些数据,不希望用户访问表中某些含敏感信息的列,比如salary...
3.关键信息来源于多个复杂关联表,可以创建视图提取我们需要的信息,简化操作
4.如果使用视图,则需要注意,对视图的修改,也是对基表的修改,会即时生效
5.删除视图时,不会销毁实体表内的数据
10.5.视图基本操作
10.5.1.视图创建
create view view_name as select ... from ...
示例1:
create view v_student as select * from student;
10.5.2.视图修改
10.5.2.1.视图的修改
create or replace view_name as select ... from ...
示例
create or replace view v_student as select * from student where id <= 4;
10.5.2.2.视图数据的变更
更新update
插入数据insert
需要注意的是,对于视图数据的操作是有条件的,那究竟什么时候可以操作视图的数据呢?
1.视图与表是一对一关系:
如果没有其它约束(这个约束就是视图没有存在的其他基本表字段都不是not null的情况)可以修改,因为对视图的修改也会影响到原表,所以对视图进行修改时,如果原表其他没在视图中的字段没有not null约束时,就可以对视图进行修改操作。
2.视图与表是一对多关系:
如果修改视图的操作只会涉及到某一个表的数据时,这时是可以对视图进行修改的,如果修改时会涉及到两个表的数据,那么就不能对视图进行修改。
比如我们现在的表为:学生表user,课程表course,学生课程表user_course
创建的视图为,将三个表做连接查询,方便查看每个学生所学的课程,比如我们可以查看小张上的所有课程信息
像以下就可以修改视图数据成功:
update view_user_course set coursename=‘JAVA‘ where id=1; 【只更新了课程表course】
update view_user_course set username=‘test2‘ where id=3; 【只更新了学生表user】
像以下就不能修改视图数据成功:
delete from view_user_course where id=3; 【同时修改学生表user和课程表course】
insert into view_user_course(username, coursename) VALUES(‘2‘,‘3‘); 【同样会修改学生表user和课程表course的数据】
10.5.3.视图查看
show tables; 查看当前数据库下的所有表,包括了基本表、视图、临时表
show full tables where table_type like ‘view’; 查看当前数据库下的所有视图
查看视图详情与查看表一样
desc 视图名
show fields from 视图名
show columns from 视图名
10.5.4.视图删除
drop view view_name
10.6.其它
10.6.1.with check option
一般情况下,如果在创建视图的时候指定了“with check option”,那么更新数据时不能插入或更新不符合视图限制条件的记录,with check option对于没有where条件的视图不起作用。
插入后的数据,通过视图能够查询出来就符合with check option,否则就不符合;
首先视图只操作它可以查询出来的数据,对于它查询不出的数据,即使基表有,也不可以通过视图来操作。
1.对于update,有with check option,要保证update后,数据要被视图查询出来
2.对于delete,有无with check option都一样
3.对于insert,有with check option,要保证insert后,数据要被视图查询出来
10.6.2.视图算法
视图算法是对视图以及外部查询视图的select语句的一种解析方式,简单的说就是视图的select语句和外部的SQL语句的执行策略。
视图算法分为三种:
undefined :未定义(默认的)这不是一种实际算法,是一种推卸责任的算法,就是告诉系统没有定义算法,让系统自己选择。
temptable:临时表算法.系统先执行视图select语句,后执行外部select查询语句
marge : 合并算法.系统先将视图对应的select语句与外部查询视图的select语句进行合并,然后执行(效率高:常态),系统在undefined条件下优先选择marge
create algorithm = 指定算法 view 视图名字 as select 语句
11.其他
11.1.autocommit
如何理解autocommit:
MySQL默认操作模式就是autocommit自动提交模式。这就表示除非显式地开始一个事务,否则每个查询都被当做一个单独的事务自动执行。我们可以通过设置autocommit的值改变是否是自动提交autocommit模式
查看数据库中autocommit变量的默认值:select @@AUTOCOMMIT【我们可以从中看出,在mysql中,autocommit默认是开启的】
设置autocommit:set @@AUTOCOMMIT = 0;
当autocommit为1即为开启的时候,系统会自动向数据库提交结果,即自动执行了commit操作,如果用户希望通过控制MySQL自动提交参数,可以更改提交模式,这一更改过程是通过设置AUTOCOMMIT变量来实现,下面用实例来演示相关的特点:
下面开启了两个session,分别是A,B
1.两个session的autocommit都是1,即为默认
在这种情况下,A对某一个数据表进行增删改操作,不需要执行commit操作,在B中都可以查询到修改后的内容,同样在B做任何修改的内容,不需要执行commit操作,仍然可以在A中看到修改的内容:
A:查看autocommit
B:查看autocommit
A:查看表test1
B:查看表test1
A:删除id为13的数据
B:查询test1表【我们可以看到查询的结果和A中是一模一样的的】
总结:
当两个都为默认,即atuocommit都为1时,因为都会自动提交,所以每个session执行后就会自动执行commit操作,所以其他的session就能看到修改后的内容
2.A的autocommit为0,B的autocommit为1
A:将autocommit设置为0
B:不修改autocommit,保持默认值为1
A:查看test1表的数据
B:查看test1表的数据
A:插入一条数据,并查看表test1数据
B:查看表test1表数据【我们可以看出在B中,没有查看到新增的数据】
A:使用commit提交,并查看数据
B:查看表test1表数据【我们可以看到修改后的数据了,因为A使用了commit操作】
总结:
总的来说,如果A不是自动提交,B是自动提交,那么A要提交后B才能看到,如果A是自动提交,B 不是自动提交,那么B要看到也要使用commit才能看到,如果A和B都是非自动提交,那么两个都必须要使用commit操作才能让对方看到的内容相同。
总的就是,不是自动提交的,要使用commit命令后才能看到别人修改的,或这让自己修改的让别人看到。
MySQL中默认的都是默认提交的,但当我们显示的使用了start transaction或者begin时,我们就要使用commit来提交才会让其他的session看到我们修改的内容。
11.2.MySQL伪事务
在MySQL中,InnoDB和BDB类型表可以支持事务处理,但是MySQL中MyISAM类型表并不能支持事务处理,对于某些应用该类型的数据表,用户可以选择应用表锁定来替代事务。这种引用表锁定来替代事务的事件被称作伪事务。使用表锁定来锁定表的操作,可以加强非事务表在执行过程的安全性和稳定性。
在MySQL的MyISAM类型数据表中,并不支持COMMIT(提交)和ROLLBACK(回滚)命令。当用户对数据库执行插入、删除、更新等操作时,这些变化的数据都被立刻保存在磁盘中。这样,在多用户环境中,会导致诸多问题。为了避免同一时间有多个用户对数据库中指定表进行操作,可以应用表锁定来避免在用户操作数据表过程中受到干扰。当且仅当该用户释放表的操作锁定后,其他用户才可以访问这些修改后的数据表。
设置表锁定代替事务基本步骤如下:
(1)为指定数据表添加锁定。其语法如下:
LOCK TABLES table_name1 lock_type1,table_name2 lock_type2
其中,table_name为被锁定的表名,lock_type为锁定类型,该类型包括以读方式(READ)【可以允许其他session读,但不能做修改操作】锁定表,以写方式(WRITE)【不允许其他session做任何操作】锁定表。
(2)用户执行数据表的操作,可以添加、删除或者更改部分数据。
(3)用户完成对锁定数据表的操作后,需要对该表进行解锁操作,释放该表的锁定状态。其语法如下:
UNLOCK TABLES
实例:
首先我们可以看到我们当前的表test1的引擎是myisam
SessionA:锁定表test1
sessionB:读取表test1【一直在等待中】
sessionA:解锁表
sessionB:可以查看到表test1的数据了
注意:如果sessionA锁定表时用的是lock table tb_name read,那么sessionB是可以读取数据的,只是sessionB不能进行增删改操作。
11.2.1.总结
应用表锁实现伪事务
通过使用表锁定对MyISAM表进行锁定操作,以此过程来代替事务型表InnoDB,即应用表锁定来实现伪事务。实现伪事务的一般步骤如下:
(1)对数据库中的数据表进行锁定操作,可以对多个表做不同的方式锁定,其代码格式如下:
LOCK TABLE table_name1 lock_type1,table_name2 lock_type2,……
(2)执行数据库操作,向锁定的数据表中执行添加、删除、修改操等操作。
如前面提到的INSERT、UPDATE、DELETE等操作。用户可以对锁定的数据表执行上述操作,在执行过程中,该伪事务所产生的结果是不会被其他用户更改的。
(3)释放锁定的数据表,以便让正在队列中等待查看或操作的其他用户可以浏览数据表中的数据或对操作表执行各种数据的操作。
UNLOCK TABLES
如果存在其他会话要求访问已锁定的多个表格,则该会话必须被迫等待当前锁定用户释放锁定表。才允许其他会话访问该数据表,表锁定使不同会话执行的数据库操作彼此独立。应用数据表锁定方式可以使不支持事务类型的表实现伪事务
11.3.MySQL执行计划
参考地址:https://www.cnblogs.com/zping/p/5368860.html
11.4.创建临时表
创建临时表与创建一般的表是差不多,只是使用了temporary关键字,另外需要注意的几点:
1.MySQL版本低于 3.23版本就无法使用MySQL的临时表
2.临时表只在当前连接可见,当关闭连接时,Mysql会自动删除表并释放所有空间
3.如果使用了其他MySQL客户端程序连接MySQL数据库服务器来创建临时表,那么只有在关闭客户端程序时才会销毁临时表,当然也可以手动销毁
4.使用show tables是看不到临时表的
5.临时表的删除仍然使用:drop table tb_name
create temporary table tb_name(
id tinyint unsigned not null,
name varchar(20) not null,
age int unsigned not null
);
11.5.复制表
表的复制,这里主要讲解了三种复制的方法,另外还有一种方法就是,首先查看需要复制表的结构(show create table 需要复制表的表名),然后复制后重新将表的名字改为复制表的名字就创建了表结构,在然后使用insert into 复制表的表名 (select * from 需要复制表的表名)
11.5.1.CREATE TABLE tab_new [AS] SELECT * FROM tab_old【复制表的部分结构和所有内容】
实例:
create table xiong as select * from test1; 使用这种方式闯进一个test1表的复制表xiong
show create table test1; 查看表test1的结构
查看复制表xiong的结构
查看表test1的内容:
查看表xiong的内容:
将表test1的结果和复制表xiong的结构放在一起对比我们可以看出:复制表中并没有复制到primary key ,auto_increment,index等
总结:
这种方法可以复制表的内容和表的结构,但是不能复制表的结构中的主键,索引,外键,auto_increment等,所以复制之后需要自己进行重新加入主键,index等才会是完整的将表复制。
11.5.2.CREATE TABLE tab_new AS SELECT * FORM tab_old WHERE 1=2【复制表的部分结构】
这个方法和上面一样的,只是使用where 1=2不成立的条件,所以就不会复制到表的内容,这里就不再做实例的讲解
11.5.3.CREATE TABLE tab_new LIKE tab_old【复制表的所有结构】
实例:
create table ying like test1 使用如上方法创建表test1的复制表ying
查看表test1的结构
查看表ying的结构:
查看表test1的数据:
查看表ying的数据:
总结:
使用这种方法可以完全复制表的结构,包括主键、外键、auto_increment等,但是这种方法能复制表的内容,所以如果需要完全的复制表,我们还可以使用如下方法来将数据复制进去。
insert into ying (select * from test1);
select * from ying; 查看表ying的数据就是和表test1的一样了
11.6.重复数据
我们可以在 MySQL 数据表中设置指定的字段为 PRIMARY KEY(主键) 或者 UNIQUE(唯一) 索引来保证数据的唯一性。其中主键确保字段唯一且不能为null,而唯一键确保字段唯一,但是可以为null。
11.6.1.INSERT IGNORE INTO
INSERT IGNORE INTO 与 INSERT INTO 的区别就是 INSERT IGNORE INTO会忽略数据库中已经存在的该条数据,如果数据库没有数据,就插入新的数据,如果有数据的话就跳过这条数据(即不会插入数据)。这样就可以保留数据库中已经存在数据,达到在间隙中插入数据的目的。
实例:
首先查看表xiong的数据:
查看表xiong的结构【我们可以看id是主键,age是唯一键】
使用insert ignore into方式插入一条age重复的数据【我们可以看到并没有报错,只是有1个warning】
再查看表xiong的数据【我们可以看到忽略了插入的数据,因为表中已存在age为23的数据】
11.6.2.REPLACE INTO
而 REPLACE INTO 如果存在 primary 或 unique 相同的记录,则先删除掉,再插入新记录。
和上面的例子差不多,首先查看表xiong的数据
再看看表xiong的结构【我们可以看id是主键,age是唯一键】
然后使用 replace into 方法插入一条重复数据
查看表xiong的数据【我们可以看到原来的数据id=100,age=23的数据变为了id=104,age=23的了,这里需要注意,因为id是主键且自增的,所以插入时使用null表示的是自增】
11.6.3.统计重复数据
?一般情况下,查询重复数据:
?确定哪一列包含的值可能会重复。
?在列选择列表使用COUNT(*)列出的那些列。
?在GROUP BY子句中列出的列。
?HAVING子句设置重复数大于1
实例:
查看表test1的数据:
使用如上方法对age列进行重复数据的查询【我们可以看到重复的数据如下】
11.6.4.过滤重复数据
1.使用distinct关键字,但是需要注意的是,使用distinct关键字必须放在前面,另外如果后面有多个字段,则表示对多个字段进行去重,如distinct id,name表示id+name不同才会被过滤
select distinct age from test1;
2.使用group by,因为使用group by同一个分组的就只会展示一条,我们可以看到效果和上面是差不多的
11.6.5.删除重复数据
首先先使用上面的复制表的方法,这里使用12.5.3的方法
create tabel ttt like tt 创建表tt的复制表ttt
insert into ttt (select * from tt group by id,age)然后向表ttt中插入tt表中不重复的数据
查看表ttt的数据,我们就可以看到表ttt为表tt中不重复的数据了
使用in 或者not in 的子查询的注意事项
1.我们可以看到这两个表x1,x2,x1的数据如下:
2.x2的数据如下:
3.我们可以看出x1,x2同时拥有id和number两个字段,我们从值也可以看出,x1的number基本包含了x2的number,但是x2中有值为null的,现在我们想要查询x1中number不包含x2的数据
4.我们使用子查询方法,其中过滤掉x2表中number不为空的
5.如果我们使用如下的子查询,但是没有过滤掉x2表中number为空的数据,则查询的结果就为空(因为x2表中拥有了一条number为null的数据,null不会in在任何数内,即使null 也不会in null的,所以如果不过滤number为null的话,查询的结果就会为空)
6.我们同样可以来看如下这个例子,我们先在x1表中添加一条number为null的数据
7.我们可以看到x1表中仍然有number为null,x2表中仍然有number为null的数据,然后我们再使用如下方式查询,查询的结果仍然为空。
8.再使用过滤x2表中numer为null的数据查询时,结果就能正确查询出来
9.同样我们可以这个例子可以看出null 是不会in null的,查询的结果并没有number为null的。
总结:
从上面的例子我们可以看出,null是不会in在任何数据内的,即使是null in null也不会成立的,所以我们在使用in 或者not in 时,记得要把子查询中为null的数据过滤了,否则查询的结果就会为空。
原文地址:https://www.cnblogs.com/wendyw/p/11622625.html