知识梳理不易,请尊重劳动成果,文章仅发布在CSDN网站上,在其他网站看到该博文均属于未经作者授权的恶意爬取信息
如若转载,请标明出处,谢谢!
MySQL是一个关系型数据库管理系统,由瑞典MySQL AB 公司开发,目前属于Oracle 旗下产品。MySQL 是最流行的关系型数据库管理系统之一,在 WEB 应用方面,MySQL是最好的 RDBMS (Relational Database Management System,关系数据库管理系统) 应用软件之一。
MySQL是一种关系数据库管理系统,关系数据库将数据保存在不同的表中,而不是将所有数据放在一个大仓库内,这样就增加了速度并提高了灵活性。
本博客就对于MySQL数据库中的知识点进行梳理,使用MySQL 5.x作为演示软件。
安装流程可以参照链接:window版提取码:ms64;mac版网址
MySQL图形可视化软件,这里选用的是Navicat for MySQL,默认是使用14天,如果需要破解可以参考Naviact破解.
写在前面的话:SQL语句对于大小写不敏感。
在配置好环境变量后,为了连接到MySQL,需要了解如下信息
打开Navicat for MySQL软件后,点击左上方的Connection按钮,弹出如下界面,以上四个信息对应红色框线要填写的内容,如果输入确定无误后点击“OK”按钮即可连接到MySQL数据库
如果连接正常,会在软件的左侧加载MySQL数据库,注意这个小鲸鱼会变绿,就代表成功连接
这里没有使用命令行进行有关的MySQL有关的查询操作,而是直接在可视化软件中执行,比较方便、快捷,这里列举
6条常见的查询语句
show databases;
show tables;
show columns from xxx table;
show status;
show grants;
show errors / warnings;
点击软件左上方的New Query按钮,进入查询页面,然后进行语句查询,分别执行上面六条语句,结果如下,友情提示:为了方便复习,建议对每次查询(比如这次查询里面包含了七条语句查询)进行命名保存,另外查询界面支持单条查询语句的执行(鼠标选中要执行的代码后点击run按钮),也可以全部执行(默认是执行代码框中的全部代码)
保存本次查询(可以包含多条查询语句,快捷键就是Ctrl + s)
六条查询指令及其输出结果如下:(部分输出结果超出三行,为了展示方便,只截取前三行的数据)
数据存在是有一定价值的,如果当前我们处理的数据很重要,那么在个人电脑上使用root管理员权限问题不大,如果是在一个协作的环境中,那么就会出现数据安全的问题(比如别人窃取、或者误删数据),因此在正式进行数据处理之前要整明白MySQL的系统安全性,才能确保之后的工作顺利进行。
这里例举三条安全性原则,如下
进入命令行,然后的登录MySQL,使用use mysql;
指令进入里面的mysql databases
,最后可以查看里面user表中的Host,User和Password。输入的结果界面如下
安全性分析:首先理解一下基本的网络知识,不同的电脑之间的访问是通过ip地址(或者DNS),每台机器都有一个地址就是127.0.0.1,代表着自己,同时该地址还有一个名称就是localhost,而最新的window10版本中多了一个::1,也算是本机,因此Host里面的三个内容都是代表着本机。那么要使用root管理员账号进行登录的时候,也就意味着不能离开当前你的计算机,即便你不设置密码,只要保证你的电脑安全(别人操作不了你的电脑),那么数据库就很安全。除非你的电脑也不设置密码,也不注意隐私保护,去吃饭或者不留意时敞开让别人使用,那这样就是属于人为过错了,出现数据安全的问题也就是活该了~
还是接着上面的命令行界面,进行用户的创建,使用代码格式为:create user 用户名 identified by 密码;
创建用户后,然后接着执行刚刚的查询语句可以发现虽然设置的密码是123456,但是在系统中显示的却是加密的数据,前面Host里面的%代表着任何机器,也可以指定特定机器的ip地址,那么就相当于只有这个人能访问该数据库了
打开一个新的命令行测试一下,可以发现使用新创建的用户和密码能够登陆数据库
如果要指定具体的ip地址和名称的时候,可以使用代码:create user 用户名@host名 identified by 密码;
如果我们不需要一些账号时候,可以进行删除,代码: drop user 用户名@host名;
比如上面示例的账号可以通过删除操作进行处理,注意如果有指定host名称,那么在删除的时候指定的user名称与host名称对应上才能完成删除
在命令行中进行账号的管理介绍完毕了,那么在可视化软件中如何进行操作?(不过日后推荐使用可视化界面进行用户的管理)
打开软件,鼠标点击User任务头像按钮,就可以进入用户管理的可视化界面,然后直接通过点击New User按钮进行用户的添加
点击按钮就会弹出如下界面,输入相关的信息后点击保存,就可以实现用户创建,避免了使用代码指令的操作
当然也可以查看SQL指令,在输入完成后,不着急点击Save保存按钮,可以点击选项卡中的SQL Preview,就可以查看创建用户的SQL语句了(注意人像上的小星号代表着未保存,查看SQL语句是在未保存之前)
如果是要删除账号,也很简单,直接选择要删除的账号,点击Delete User按钮后确认删除即可
首先创建一个测试的用户, create user user3@'localhost' identified by '123';
然后创建两个测试的数据库,名为test和test2,分别手动在对应库下创建一个表单名为demo和demo2,设置数据的字段名都为为id,假使输入1,2,3和3,4,5三条数据,如下(这里通过软件创建数据库和表单字段数据可以先尝试一下,后面会进行介绍如何创建数据库和表单)
那么竟然创建了user3账号,使用单开的命令行窗口进行查看数据库情况,如下。可以发现在没有给定任何权限下,user3账号是可以查看到test数据库的,主要是因为test库在MySQL被设计成为来宾库,默认是创建了用户,只要系统中存在test数据库,那么其他的用户就可以访问到,也就方便很多,有时候希望把一部分数据拿出来就不设权限,方便与人共享,直接把数据丢到test数据库中就可以了
之前手动创建表单字段数据,就是为了验证uesr3用户是否可以正常访问test数据库中的数据,验证如下。这里使用到了查看表单中的全部数据的方式: select * from 表名;
需要注意user3属于系统设置一个账户没有管理员权限,属于来宾,也就只能访问test数据库,对于test2数据库是没有权限访问的,因此要有此权限,就需要返回主命令行窗口,使用root管理员身份进行授予,代码指令: grant 权限 on 数据库 to 用户;
既然可以授予来宾的权限,那么也同样可以收回给与的权限,代码指令:revoke 权限 on 数据库 from 用户;
这里面有一点需要注意的,在管理员界面撤回了用户的权限,如果用户还在访问test2数据库,是不会立即生效的,就好比你进凭借一个门禁卡进入公司,当以在公司内部的时候,还是可以看到公司内部的场景,但是一旦出来了,那么这个门禁卡就失效了,也就无法查看到test2这个数据库了,代码操作如下
以上是使用命令行进行权限设置的操作,如果使用软件操作,也就很简单了,进入用户界面,双击要授予权限的用户,跳转页面后点击选项卡中的Privileges选项,然后在点击Add Privilege并制定数据库相关的权限,最后点击OK即可,流程如下
先介绍一下在命令行(注意是root管理员主窗口才有权限)中修改密码的代码指令:set password for 用户名@host名 = Password(新密码);
如果使用软件进行密码修改,直接双击用户,然后在Password对应的输入框输入新密码后重新确定一下就可以了,然后在SQL语句预览的选项中就可以看到对应的语句(比如再将abcdef改成abc)
这部分的操作就是相当于上面的密码更改,由于丢失的是普通用户的密码,就相当于门禁卡丢了,再补办一个就行了,没啥大不了的,操作上面已经给出了,只要进入root管理员权限下,重新设置密码或者在软件界面使用root管理员权限登录后修改一下指定用户的密码,然后点击保存即可。
如果一不小心把root管理员的密码也给丢失(遗忘了),那自然也就很多操作就没有办法进行了,遇到麻烦了~
“做事留一手,往后好相见”,自然MySQL也留有后路,这时候就要开启紧急模式,首先在电脑的服务上停止我们的MySQL运行(第一次MySQL服务操作),然后进入到最初安装MySQL数据库中设置的初始文件my.ini(我这里设置的文件名为my.ini,根据最初自己设定打开对应的文件),在[mysqld]下方添加如下指令:skip-grant-tables
,如下
点击保存后,再开启我们的MySQL服务(第二次MySQL服务操作),开启成功后再到MySQL中修改密码
这里的修改密码就需要使用紧急情况下另外一条指令,首先输入use mysql;
然后再进行输入update user set password=password('abc') where user='root;'
(通过下面的操作可以发现,如果还是使用原来修改密码的方式是没有办法进行root密码的更改的,所以需要紧急情况下的密码更新指令)
既然密码进行修改了,接下来就是把MySQL服务再次停掉(第三次MySQL服务操作),在ini文件中注释掉添加的那行代码,然后保存退出,最后就是正常启动一下MySQL服务后(第四次MySQL服务操作)进入MySQL登录页面,输入密码后就可以进入了
第一部分进行了详细的MySQL配置环境的讲解,那么接下来就是应该和数据打交道了,这里为了展示方便,就直接引入案例数据库(已经上传至资源),使用的样例表为一个简单的小型商场使用的订单录入系统,这些样例表单的分类如下:
这四类数据表单几乎包含了现在公司使用数据库的雏形版本,包含了:供(供应商信息)、产(产品目录)、顾(顾客信息)、订(订购清单)四个方面。然后可以根据具体的需求进行拓展
设置具体的表来满足四个方面的要求:(共六张表)
表名 | 类型 | 说明 |
---|---|---|
vendors表 | 供应商信息 | 存储销售产品的供应商。每个供应商在这个表中有一个记录,供应商ID(vend_id)列用来匹配产品和供应商 |
products表 | 产品信息 | 包含产品目录,每行一个产品。每个产品有唯一的ID(prod_id列),通过vend_id(供应商的唯一ID)关联到它的供应商 |
customers表 | 顾客信息 | 储所有顾客的信息。每个顾客有唯一的ID(cust_id 列) |
orders表 | 订单信息 | 但不涉及订单细节,每个订单唯一地编号(order_num列)。订单用cust_id列(它关联到customer表的顾客唯一ID)与相应的顾客关联 |
orderitems表 | 订单产品信息 | 每个订单的每个物品占一行。对orders中的每一行,orderitems中有一行或多行。每个订单物品由订单号加订单物品(第一个物品、第二个物品等)唯一标识商 |
productnotes表 | 产品注释信息 | 存储与特定产品有关的注释。并非所有产品都有相关的注释,而有的产品可能有许多相关的注释。 |
对于productnotes表就可以理解为日常操作时候的备注信息的添加,对产品的标注。
orders表 和 orderitems表能否合并为一张表? 这里可以进行实际场景的带入,比如去超市买东西,每个人去购物付款时候都是会有一个订单,但是每次订单上购买的产品是不一定的,有时候是一样,那么这就可以只设定一个表就行了,那么当顾客买的多个产品时候就是一对多了,那只用一个订单表就不能很好的解决问题,因此采用两张表,一个就是简单的订单号与顾客信息关联,另一个就是商品信息,与订单号关联,也就实现了一对多的问题,如下图
在进行建库之前,首先理解一下这个库是指什么?,那么这里就介绍对MySQL里面的一些名词的理解,对比现实生活进行类比举例
MySQL | 现实 |
---|---|
MySQL系统 | 企业园区 |
databases | 园区内所有的公司大楼 |
database | 单栋大楼 |
tables | 大楼中所有的楼层 |
table | 大楼中单层 |
columns | 楼层中所有的房间 |
column | 楼层中单个房间 |
接下来就是在MySQL中创建数据库
CREATE DATABASE [IF NOT EXISTS] <数据库名> [[DEFAULT] CHARACTER SET <字符集名>] [[DEFAULT] COLLATE <校对规则名>];
关于参数的讲解:
[DEFAULT] CHARACTER SET
:指定数据库的默认字符集,中文的情况下使用utf-8字符集。[DEFAULT] COLLATE
:指定字符集的默认校对规则。使用命令行创建,代码指令: create database 数据库名;
(字符集和校对规则默认指定,之前创建的test和test2数据库已经手动删除)
如果在软件上操作如下,在弹出的页面中指定要创建的数据库的名称,后面的两项就是指定字符集和校对方式的(可以不选择),然后直接点击OK,也就创建了一个名为db2的数据库
如上图所示,在创建数据库的同时,可以指定字符集和校对规则,比如我们在国内设计的时候多少会和中文打交道,所以在建库的时候可以设定字符集为utf-8,然后校对规则可以使用默认(也就是不选择,系统自动会匹配一个),比如直接点击OK,然后右键创建的数据库,进行编辑,发现系统默认指定了针对于utf-8字符集的一个校对规则
注意:当改变字符集的时候就相当于进行数据重造了,在数据量较大的时候负担就很大,所以在进行录入之前,先确定好保存的数据字符集。如果真的出现字符集的问题了,为了保证数据的安全性,建议采用的方式是重新创建一个数据库,然后将数据导入进去,而不是在原数据库中进行修改
删除数据库的代码指令:drop database 数据库名;
在软件上的操作也就是选择要删除的数据库,鼠标右键选择Delete Database
后确认删除即可
利用CREATE TABLE
创建表,必须给出下列信息:
CREATE TABLE
之后给出;上图中除了定义表必须要的元素外,还发现了PRIMART KEY(cust_id)
主键和ENGINE=InnoDB
引擎,那么接下来就先介绍一下什么是主键?
在数据库中的数据每一行之间是没有关联了,在需要使用的时候就要能够准确的定位到数据的位置,那么这个功能就由主键来完成,一般就是id对应的字段,直接通过id的检索就能找到对应的数据
NULL
值的列。允许NULL
值的列不能作为唯一标识;AUTO_INCREMENT
自增,在主键中由于是唯一值,所以每次插入的数据保证不重复编号,这样就不需要手动指定了除了主键外,如果在插入行时没有给出值,MySQL允许指定此时使用的默认值。默认值用CREATE TABLE
语句的列定义中的DEFAULT
关键字指定
上面介绍了主键,接下来就是介绍ENGINE
存储引擎了,关于存储引擎的介绍:与其他DBMS一样,MySQL有一个具体管理和处理数据的内部引擎。在你使用CREATE TABLE
语句时,该引擎具体创建表,而在你使用SELECT语句或进行其他数据库处理时,该引擎在内部处理你的请求。多数时候,此引擎都隐藏在DBMS内,不需要过多关注它。了解一下目前常见的三个存储引擎。
之后再介绍全文检索和数据安全中的事务管理的时候会再次涉及到存储引擎的部分,读到这里只需要了解一下就可以了
有时候创建表之后,需要进行数据的添加或者更改,可使用ALTER TABLE
语句。但是,理想状态下,当表中存储数据以后,该表就不应该再被更新。在表的设计过程中需要花费大量时间来考虑,以便后期不对该表进行大的改动。
为了使用ALTER TABLE
更改表结构,必须给出下面的信息:
ALTER TABLE
之后给出要更改的表名(该表必须存在,否则将出错)ALTER TABLE
的一种常见用途是定义外键(后面的部分进行介绍)如果是复杂的表结构更改,一般需要手动删除过程:
和删除数据库操作类似,代码指令为:drop table 表名;
注意删除的是整个表,而不是只删除表中的内容,如果是在软件中操作的话就是选择要删除的表,右键选择Delete Table后确定删除即可
重命名表的操作和删除表只需要改变关键字为RENAME,软件上右键表也有一个选项为Rename,点击输入重命名的表名称即可。
创建表之前,先要了解下字段的类型,总结主要的就是4类(这部分类型的讲解,百度会有一大堆,这里就不再进行赘述了)
注意:如果使用utf-8的中文输入在指定长度的时候,一个中文字符相当于两个英文字符,也就是说length=200时候,可以存放的中文大概为100个;对于要进行交易或者计算的数值可以采用decimal类型
这里通过软件进行创建,如下(这样直接通过鼠标选择进行输入即可)
在输入完毕后,不要着急点击保存按钮,可以查看一下对应的SQL语句,也就是点击选项卡中的SQL Preview就可以查看,代码如下
如果保存后,发现这个SQL Preview为空也不用着急的,右键点击创建的表名,然后选择Dump SQL File -> Structure only后,选择一个地方存放文件即可,然后打开后就可以看到建表时候的对应SQL语句
对应的语句如下,发现有些类型没有指定长度的,系统会自动指定;还有其它的设置,比如字符集、存储引擎、校对方式和格式等都有系统默认值
以上就是通过手动操作软件进行表的创建,然后得到了SQL语句,那有一个问题——可不可以直接使用生成的SQL语句在软件上面直接创建表呢?因为网上也有很多SQL建表语句,那么直接Copy过来就可以建表且不是美哉。答案是肯定可以的,点击左上方的New Query按钮,然后就可以编辑区输入SQL代码了,可以将生成的代码复制进去,修改一下表的名称(比如改成demo1,避免覆盖掉原来的demo表),最后点击运行,看看输出的结果如何
上文中已经介绍过案例数据表的设计,紧接着也介绍了表的创建,那么接下来就是按照需求进行表单创建(还是使用上面New Query方式创建)。注意:由于之前都是测试演示,下面进行实例表单的创建,建议是新建一个数据库来存放表单,比如我这里创建的数据库表名为erp,因为后续可能添加中文的数据,所以指定字符集为utf-8
########################
# Create customers table
########################
CREATE TABLE customers
(
cust_id int NOT NULL AUTO_INCREMENT,
cust_name char(50) NOT NULL ,
cust_address char(50) NULL ,
cust_city char(50) NULL ,
cust_state char(5) NULL ,
cust_zip char(10) NULL ,
cust_country char(50) NULL ,
cust_contact char(50) NULL ,
cust_email char(255) NULL ,
PRIMARY KEY (cust_id)
) ENGINE=InnoDB;
#########################
# Create orderitems table
#########################
CREATE TABLE orderitems
(
order_num int NOT NULL ,
order_item int NOT NULL ,
prod_id char(10) NOT NULL ,
quantity int NOT NULL ,
item_price decimal(8,2) NOT NULL ,
PRIMARY KEY (order_num, order_item) #联合主键
) ENGINE=InnoDB;
#####################
# Create orders table
#####################
CREATE TABLE orders
(
order_num int NOT NULL AUTO_INCREMENT,
order_date datetime NOT NULL ,
cust_id int NOT NULL ,
PRIMARY KEY (order_num)
) ENGINE=InnoDB;
#######################
# Create products table
#######################
CREATE TABLE products
(
prod_id char(10) NOT NULL,
vend_id int NOT NULL ,
prod_name char(255) NOT NULL ,
prod_price decimal(8,2) NOT NULL ,
prod_desc text NULL ,
PRIMARY KEY(prod_id)
) ENGINE=InnoDB;
######################
# Create vendors table
######################
CREATE TABLE vendors
(
vend_id int NOT NULL AUTO_INCREMENT,
vend_name char(50) NOT NULL ,
vend_address char(50) NULL ,
vend_city char(50) NULL ,
vend_state char(5) NULL ,
vend_zip char(10) NULL ,
vend_country char(50) NULL ,
PRIMARY KEY (vend_id)
) ENGINE=InnoDB;
###########################
# Create productnotes table
###########################
CREATE TABLE productnotes
(
note_id int NOT NULL AUTO_INCREMENT,
prod_id char(10) NOT NULL,
note_date datetime NOT NULL,
note_text text NULL ,
PRIMARY KEY(note_id),
FULLTEXT(note_text)
) ENGINE=MyISAM;
#####################
# Define foreign keys
#####################
ALTER TABLE orderitems ADD CONSTRAINT fk_orderitems_orders FOREIGN KEY (order_num) REFERENCES orders (order_num);
ALTER TABLE orderitems ADD CONSTRAINT fk_orderitems_products FOREIGN KEY (prod_id) REFERENCES products (prod_id);
ALTER TABLE orders ADD CONSTRAINT fk_orders_customers FOREIGN KEY (cust_id) REFERENCES customers (cust_id);
ALTER TABLE products ADD CONSTRAINT fk_products_vendors FOREIGN KEY (vend_id) REFERENCES vendors (vend_id);
输出结果为:(关于外键的操作后面会讲解,这里直接加载。注意是要选择创建的新的erp数据库,然后在此数据库中将代码复制粘贴到Query中,最后运行)
可以通过软件查看一下这六张表的关联关系(双击erp数据库下的Tables即可),如下
至此案例数据库中的需要表单就全部创建完毕了,但是表单里面没有数据,因此需要将数据加载到对应的表单中,由于数据量较大,这里就不进行粘贴(数据已经上传至资源,有需求可以自行下载),直接给出数据加载的页面。接下来就可以执行对数据库中数据的处理操作了。
也就是查看当初创建表时有关的信息,代码指令:desc 表名;
比如这里查看产品表
SQL语句是由简单的英语单词构成的。这些单词称为关键字,每个SQL语句都是由一个或多个关键字构成的。大概,最经常使用的SQL语句就是SELECT语句了。它的用途是从一个或多个表中检索信息。
为了使用SELECT检索表数据,必须至少给出两条信息——想选择什么,以及从什么地方选择。
可以选择查看表格中所有的数据,也可以指定部分字段,查看所有数据代码指令为:select * from 表名;
选择部分字段的内容查看(如果是直接导入数据后就操作的话,不会出现字段名称代码补全,该功能功能在重启Navicat软件后就可以了),代码指令:select 字段1,字段2,... from 表名;
在vend_id字段,发现有很多重复的数值,如果想要进行唯一值的查询,代码指令:SELECT DISTINCT 字段名 FROM 表名;
(出现了大小写的单词,很显然小写的为人工手打输入,大写的是使用系统的tab自动补全功能)
限定行数查询,limit放在表名之后,代码指令:select * from 表名 LIMIT 4;
“分页”查询,按照指定的长度,查询下一个同等长度的数据,代码指令:select * from 表名 LIMIT 数值1,数值2;
(对比查询全部数据的表,prod_id为FB的数据在第5条,所以输出的结果就是第5-8条数据)
首先是进行单字段的排序,代码指令:SELECT 字段1,字段2 from 表名 ORDER BY 字段 升降序;
(默认是升序asc,可以指定降序desc)
如果不是数字,而是字符数据的排序呢?可以尝试使用prod_name字段进行查看,这里看出如果升序排列,先显示第一位字符的,然后在比较数值,最后比较字母的先后顺序
多个字段的排序也是有需求的,比如按照一个字段存在较多相同时候,就会考虑按照另一个字段的规则排序,代码指令:SELECT 字段1,字段2 from 表名 ORDER BY 字段,字段;
有没有这种需求——比如我选择要查看的数据,并不是要按照这个字段进行排序的数据呢?当然也是存在的,比如我们希望得到按照价格排序的供应商的id和name时候。
如果只是进行单字段的查询输出结果肯定是没有问题(因为单个字段没有相同的部分,自然没有对比性),但是要进行多字段的输出时候可能就会出现排序上的变化,比如选择输出全部字段只输出供应商id和name时候,结果的顺序就可能有所不同
解决方式就是按照多字段进行排序,想要以哪个字段为次要的排序标准输出,就选择该字段作为第二个字段进行排序,比如这里第二个选择prod_name,输出的结果如下,这样就解决了这个问题(当查询多个字段时候,如果部分信息内容一致,建议增加多组排序)
对应着查找最贵、最便宜的商品,这个实用技能,即是对商品的价格进行排序,结合着limit选取第一条结果,代码指令:
最小值:SELECT * from products ORDER BY prod_price LIMIT 1;
最大值:SELECT * from products ORDER BY prod_price desc LIMIT 1;
数据过滤的基础知识:
比如在产品表中找到价钱为2.5的产品,这就属于条件过滤了,代码指令:SELECT * FROM products WHERE prod_price = 2.5;
where条件过滤的操作符,在进行条件判断的时候需要有操作符与给出的值作比对,常见普通操作符的有:=、>、<、>=、<=、!=、between num1 and num2
等
SELECT * FROM products WHERE prod_price >= 2.5;
SELECT * FROM products WHERE prod_price BETWEEN 5 AND 10;
select vend_id,prod_name from products where vend_id <> 1003;
(!=和<>都是表示剔除的操作符)★★ 空值检查
比如这里在products表中手动的添加一行数据,然后对于prod_desc字段的数据不进行输入,最后要查询这个空值所在的行数据,代码指令:SELECT * FROM products WHERE prod_desc is NULL;
★★★★ 高级操作符的使用
上面常用的操作符,用于数值上的判断,接下来介绍一下高级(复杂)一些的操作。以上WHERE语句在过滤数据时使用的都是单一的条件。为了进行更强的过滤控制,MySQL允许给出多个WHERE子句,这些子句可以两种方式使用:以AND子句的方式或OR子句的方式使用。
1)逻辑操作符的使用
select prod_id , prod_price,prod_name from products where vend_id= 1003 and prod_price <=10;
select vend_id,prod_id , prod_price,prod_name from products where vend_id = 1002 or vend_id = 1003;
以上代码指令实现and和or的逻辑判断,and缩小搜索范围,or扩大搜索范围
2)计算次序操作
在进行逻辑运算符操作的时候,可能会同时出现and和or两种操作符,那么就会出现一个问题,究竟是哪个计算在先?也就涉及到了计算次序的问题了
select vend_id,prod_id , prod_price,prod_name from products where vend_id = 1002 or vend_id = 1003 and prod_price <= 10;
select vend_id,prod_id , prod_price,prod_name from products where vend_id = 1002 and prod_price <= 10 or vend_id = 1003 ;
这里最后面的and和or的条件进行调换了,看一下输出结果,第一种是7条数据,而第二种是9条数据,而要写的这行代码指令要完成的是:选择供应商编号为1002的并且其产品价钱不超过10或者是编号为1003的全部商品(对应着第二种,第一种为错误的使用方式)
这种and和or在where后面同时出现的时候,建议书写的规范是要满足的第一部分在前使用and连接,而后使用or连接满足后面的条件(建议先and再or),如果非要使用or在前,建议后面的内容用括号,表示范围,这样就不会产生理解上的问题了
select vend_id,prod_id , prod_price,prod_name from products where vend_id = 1002 and prod_price <= 10 or vend_id = 1003 ;
select vend_id,prod_id , prod_price,prod_name from products where vend_id = 1003 or (vend_id = 1002 and prod_price <= 10) ;
3)成员判断操作符
进行数据存在与否的判断,通常是给定一个数据范围然后判断一下要查找的数据是否在这个范围中,就属于成员判断的范畴,使用的操作符是:in、not in
SELECT * from products where vend_id in (1002,1003,1005);
SELECT * from products where vend_id not in (1002,1003,1005);
前面介绍的所有操作符都是针对已知值进行过滤的。不管是匹配一个还是多个值,测试大于还是小于已知值,或者检查某个范围的值,共同点是过滤中使用的值都是已知的。但是,这种过滤方法并不是任何时候都好用,比如怎样搜索产品名中包含文本anvil的所有产品?用简单的比较操作符肯定不行,必须使用通配符。利用通配符可创建比较特定数据的搜索模式。
关于通配符的知识:
1)使用like操作符筛选数据
比如筛选产品名称中以Jet开头的数据,代码指令:SELECT * from products where prod_name LIKE 'jet%';
其中待匹配的字符大小写均可,%代表着是任意长度的字符
查询中间或者末尾的数据,比如这里查询anvil数据(prod_name字段中是在末尾,prod_desc字段中是在中间),那么就可以把%提到相应的位置就可以了。故使用like通配符时,如果匹配的字符在开头,%在最后面;字符在末尾,%在最前面;字符在中间,%在两端。
SELECT * from products where prod_name LIKE '%anvil';
SELECT * from products where prod_desc LIKE '%anvil%';
由于只有这三条数据是包含anvil的,而且prod_name字段中是在末尾,prod_desc字段中是在中间,均都有anvil,所以两次检索的结果都是同样的数据
2)模糊匹配
通过上面的示例可以发现我们可以准确的根据给出的内容去匹配原数据,但是有时候人的记忆不是那么好用,就只记得部分残缺的字符,那么这时候要匹配数据就可以使用到模糊匹配,比如在prod_name字段中之前扫了一眼记得有个数据里面包含sead还是seed的,想要筛选出这条数据,代码指令:SELECT * from products where prod_name LIKE '%s%d%';
不记得部分直接使用%替代即可
3)_ 下划线通配符
采用模糊匹配是很无脑简单,但是吃计算机的性能,特别是在数据量较大的时候,这样无脑的匹配就很话费时间,因此如果记忆具体的单词的字符个数,就可以使用下划线逐字匹配的方式,还是以上面的内容为例,知道是sead或者是seed,确定是四个字符了,代码指令:SELECT * from products where prod_name LIKE '%s__d%';
将不确定的位置用下划线代替,这样会很精准,运行效率会比模糊匹配较高。
如果对于python数据处理比较了解,re库就是必不可少的核弹工具了,可以为我们减轻大量的工作量,同样在SQL中也是可以使用正则表达式,比如用来从一个文本文件中提取电话号码和邮箱、数据中替换某一个数据、url网页链接等
1)基本字符匹配
比如匹配产品名称中包含100的信息,对比之前的like通配符(下同)
select prod_name from products where prod_name like '%1000';
select prod_name from products where prod_name REGEXP '1000';
2) 匹配单个字符
比如知道名称里面是有000,前面的千分位数值不管多少,都进行匹配,这里使用到了’.'代表匹配单个字符
select prod_name from products where prod_name like 'JetPack _000';
select prod_name from products where prod_name REGEXP '.000';
3)匹配原生字符
比如上面的.匹配单个字符,如果在实际需求中就是要提取包含‘.’的数据,就需要在符号前面使用双斜杠
select prod_name from products where prod_name like '\\.%';
select prod_name from products where prod_name REGEXP '\\.';
输出结果为:
还有一些字符属于这类的原生字符,比如\f、 \n、 \r 、\t、 \v 、\、(、)等,在使用匹配的时候注意前面加斜杠
4)使用多条件进行匹配
比如进行两选一条件满足皆可的匹配,这里不是使用or,而是使用’|'符号代替
select prod_name from products where prod_name like 'JetPack _000';
select prod_name from products where prod_name REGEXP '1000|2000';
5)范围匹配
比如不限于两个条件,而且不想要匹配任意字符,就是要在指定的范围内进行数据的匹配,数据范围较小的时候可以都写出来,较大时候可以使用’-'来表示范围连接,比如常见的[0-9]、[a-z]等
select prod_name from products where prod_name like 'JetPack _000';
select prod_name from products where prod_name REGEXP '[12] ton';
select prod_name from products where prod_name REGEXP '[1-9] ton';
输出结果为:
6) 剔除匹配
比如要求开头不是某个特定字符或者数值的匹配,使用[^]匹配模式
select prod_name from products where prod_name like 'JetPack _000';
select prod_name from products where prod_name REGEXP '[^345] ton';
输出结果为:
7) 困难的精准匹配
比如有时候文本数据中就是会存在单个字符有无的却别,因此进行单个字符有无的匹配
select prod_name from products where prod_name like 'JetPack _000';
select prod_name from products where prod_name REGEXP '\\([0-9] sticks?\\)';
输出结果为:(这里可以发现对应进准匹配还是很吃力的,后面会介绍全文检索时候介绍精准匹配)
这里的’?‘代表着匹配0个或1个由前面的正则表达式定义的片段,如果不匹配带s的内容可以把上面的匹配模式中的s去掉;还有一个是’+'符号代表着匹配1个或多个的表达式。整体上这里的正则表达式的模式和python中的re库的匹配模式类似,还可以看出like通配符的使用在很多场景下失效的,因此要学会两者的互补。
什么是计算字段?以创建的案例库中的物品订单表为例
表中储存的有物品的价格和数量、但是不必要存放每个物品的总价格(用价格乘以数量即可)。有时除了总价格,还可以需要根据表数据进行总数、平均数计算或其他计算。存储在表中的数据不是应用程序所直接需要的。我们可以根据需要直接从数据库中检索出转换、计算或格式化过的数据;而不是直接使用检索出数据,最后再在客户机应用程序或报告程序中重新格式化,这就是计算字段发挥作用的所在了。计算字段并不实际存在于数据库表中
比如供应商的名称与所在的国家进行数据的拼接,最后为了方便识别,国家使用括号进行分隔,代码指令:select vend_name,vend_country, CONCAT(vend_name , ' (' ,vend_country , ')' ) from vendors;
这里就是对于存在空格的数据进行格式的标准化,去掉左右的空格后,在进行拼接组合,代码指令:select vend_name as v_n,vend_country as v_c,CONCAT(vend_name , ' (' ,LTRIM(RTRIM(vend_country)) , ')' ) as vend_title from vendors;
LTRIM和RTRIM一般配合使用,去掉左右的空格,对于要查看的字段数据,可以使用as的方式进行重命名(as可以省略,当然写了更容易理解)。字段格式化还可以使用到其他的函数,接下来就会进行详细的介绍
进行产品的单价和数量的计算,从而可以获取总价的数据,代码指令:SELECT quantity q,item_price i, quantity*item_price sum FROM orderitems;
对常见的函数进行一个分类,如下:
该函数功能主要是针对英文数据来操作,代码指令:SELECT vend_name,UPPER(vend_name) as vend_name_upper,LOWER(vend_name) as vend_name_lower from vendors;
把字符串的内容取出部分,就是指定切割位置,然后拿出切割位置之间的数据,代码指令:select vend_name ,SUBSTRING(vend_name ,1,5) as vend_name_upper from vendors order by vend_name;
但是需要注意:这里的字符串切片和python的字符串切片是有区别的,前一个数值都代表第一刀切下的位置,但是第二个数值python中是指第二刀的位置(方向是不可逆),但是SQL里面是指第一刀切后的往后数第几个数值多对应的位置(最后一个数值始终代表着往右方向)
python中切片必须要从一个数字代表的位置向右到第二个数字代表的位置,比如下图中-4代表这c所在的位置,1代表着b所在的位置,所以切片的结果就是c从右开始到b之间的内容,显然是不可能的,所以最后切片的取值就为空
对比SQL中,第一个位置是相同的含义,但是第二个数字始终代表往右方向取值的多少,是表示最终要取的数量而不是代表位置
这个函数的功能在处理英文数据时候是可以用到的,但是中文就比较差,原理就是根据发音的相似性进行数据的匹配,比如根据li来找lee,或者use寻找usa等,代码指令:select cust_name ,cust_contact from customers where soundex(cust_contact) = soundex('Y Li');
如果只需要提取日期的信息,也就是截止到日,那么可以使用Date()函数进行转化,还可以借用之前的判断语句between and筛选出一定日期范围内的数据,代码指令如下:
select cust_id ,order_num ,order_date from orders where Date(order_date) = '2005-09-01';
select cust_id ,order_num ,order_date from orders where Date(order_date) BETWEEN '2005-09-01' AND '2006-09-01';
这样得到的结果是直接到日,不会管具体的时间(时分秒)
除此之外:adddate()、addtime()、curdate()、curtime()、date()、datediff()、day()、year()、month()、hour()、minute()、now()、time()等均为常用的时间处理函数
比如指定年月的查询:select cust_id ,order_num ,order_date from orders where YEAR(order_date) = '2005 'AND MONTH(order_date) = '9';
其它没有详细介绍的时间函数,可以在编辑区直接举个小例子查看一下如何使用的,就是select接函数,函数内部要填写指定格式的数据
我们经常需要汇总数据而不用把它们实际检索出来,为此MySQL提供了专门的函数。使用这些函数,MySQL查询可用于检索数据,以便分析和报表生成。比如
1)汇总计数
使用count()函数用来统计数据的多少(可以填入*,也可以填入某一字段的名称),代码指令: select count(prod_id) as num_count from products;
2)求和
使用sum()函数统计最后的数据总和,可以配合着条件判断指定满足条件下的数据求和,代码指令:select sum(quantity) from orderitems where order_num = 20005;
3)求平均
使用avg()函数统计数据的均值,还可以指定某一字段在满足特定条件下的均值
select avg(prod_price) from products;
select avg(prod_price) from products where vend_id = 1003;
求解最大值使用max()、最小值min()函数,同样也可以指定某一字段在满足特定条件下的最值
select max(prod_price) from products;
select min(prod_price) from products where vend_id = 1003;
最后可以将多个汇总函数都在查询中进行输出,代码指令:select count(*) as num_items , min(prod_price), max(prod_price) , avg(prod_price) from products;
通过上一小节的介绍,SQL聚集函数可用来汇总数据,能够对行进行计数,计算和与平均数,获得最大和最小值而不用检索所有数据。那么现在有需求如果要返回每个供应商提供的产品数目怎么办?或者返回只提供单项产品的供应商所提供的产品,或返回提供10个以上产品的供应商怎么办?
这时候就需要对数据进行分组了,分组允许把数据分为多个逻辑组,SQL中通过使用GROUP BY关键字以便能对每个组进行聚集计算。
以上的文字信息有点抽象,可以配合着下面的实际操作进行演示
比如查看一下不同的供应商提供的商品的总数量,代码指令:select vend_id,count(*) from products GROUP BY vend_id;
通过这个执行的代码可以看出分组的基本格式select…from之间的内容是要进行分组的字段(这个字段不能是计算字段),然后指出分组对应的汇总统计方法,group by后面就为指定分组的这个字段(前后字段是一致的)
在进行表格处理的时候,有时会在字段的最后面的一个格子上进行汇总数据的输入,这里就可以通过添加with rollup
解决
光有数据分组还是远远不够的,比如要对分组的数据中进行筛选,提取可以提供产品数量超过3种的数据,这里就需要进一步过滤数据了,SQL中使用having关键字进行分组过滤(having后的内容就是之前的汇总函数),代码指令:select vend_id,count(*) from products GROUP BY vend_id HAVING count(*)>3;
既然都已经过滤完数据了,那么顺带进行数据的排序不过分吧,只需要最后加上order by就行(order by后的内容也是之前的汇总函数),可以对使用的汇总函数进行重命名来减少代码量,代码指令:select vend_id as v,count(*) as c from products GROUP BY v HAVING c>3 ORDER BY c desc;
运行到这里是不是感觉这么多关键词有点晕了?有什么顺序或者可循的规律拿来用用不,这里就总结一下。
首先是顺序:select > from > where > group by > having > order by > limit
规律就是在使用分组中代码指令可以归纳为:select 字段,汇总函数 from 表 GROUP BY 字段 HAVING 汇总函数>3 ORDER BY 汇总函数 desc limit 1;
对照上面的输出方便进行理解,核心要记住:分组是针对于目标字段、过滤和排序是针对于汇总函数、最后面放置升降序和限制输出。
SELECT语句是SQL的查询。以上的操作所有SELECT语句都是简单查询,即从单个数据库表中检索数据的单条语句。SQL还允许创建子查询(subquery),即嵌套在其他查询中的查询。常见场景:作为计算字段使用子查询和利用子查询进行过滤
有这么个需求:根据出售的商品信息来找到对应的购买用户信息。结合之前设计的表结构,顾客是直接和订单关联的然后才和订单中的商品信息关联,因此要想根据出售的商品信息来找到对应的购买用户信息,需要进行中间的一个订单编号的查询,才能继续下去,即两次查询语句可以满足要求(别忘了下图,很清晰的展现这个过程)
比如这个商品的信息就是‘TNT2’,接着就找这个商品对应的购买用户的信息。首先第一步是查询商品对应的订单编号,然后就是进行条件判断是否订单的编号在筛选后目标数据中,最后得到的数据中就包含了我们要找的购买用户的信息
SELECT * from orderitems where prod_id = 'TNT2';
select * from orders WHERE order_num in (20005,20007);
以上就实现了最初我们想要的结果,但是要执行两次,如果使用子查询的话就可以将两次执行的语句进行结合,代码指令如下:select cust_name ,cust_contact from customers where cust_id in (select cust_id from orders where order_num in (select order_num from orderitems where prod_id ='TNT2'));
究极套娃,既然可以找到购买用户的id,那么顺便就找一下详细的姓名和地址了。
可以发现子查询看上去很复杂,其实就是将要查询的每一步数据直接用SQL语句替代了
前面可以根据商品信息查询到用户的信息,那么现在尝试把用户和他们的消费记录关联起来(也就是有多少份订单),所以查询结果的最后一列应该就属于计算字段,那么也是可以使用子查询操作的,代码指令:select cust_name ,cust_state ,(select count(*) from orders where orders.cust_id= customers.cust_id ) as frequence from customers;
这里使用了联结查询,接下来就介绍
子查询最实在的功能就是:可以快速的把多个表单的内容在一个场景中全部都整出来(重点还是表单之间对应关系的理解)
SQL最强大的功能之一就是能在数据检索查询的执行中联结(join)表。联结是利用SQL的SELECT能执行的最重要的操作,很好地理解联结及其语法是学习SQL的一个极为重要的组成部分
为什么会有联结表?
假如有由同一供应商生产的多种物品,那么在何处存储供应商信息(如,供应商名、地址、联系方法等)呢?数据如果都放置在一个表中势必会因为供应商的信息重复多条而极大的浪费存储空间,而且一点发生供应商信息的修改,那么整个表中的信息都必须得修改。
可能字面上不好理解,看一下图示。把一张表拆解成两张表看似分工变多了,实则业务变得更加精确了,比如这里要修改公司的信息,小米修改为小米科技公司,那么只需要改一处就可以了。给出的只是示例,有可能还有公司的地址,邮箱、介绍等,那么这些信息的修改如果再一个表中,修改就是要针对大量数据进行操作,分成两个表后,就修改一行数据就可以了。
每个表中都会有一个唯一的标识字段,也称主键(Primary Key),这里都是id,然而发现上图右侧下方的表中还有一个company_id字段,里面的内容刚好是和它上方的供应商信息单中的id对应,这种连接两个表的字段(列)就成为外键(Foreign Key),外键包含另一个表的主键值(也就是这里产品表中的company_id值包含了供应商中的id值)
设置联结表的好处:
比如查询供应商名称,商品名称和商品价格,这三个数据明显是在供应商表和产品表中,通过联结操作就可以直接进行查询,代码指令:select vend_name,prod_name,prod_price from vendors ,products where vendors.vend_id = products.vend_id;
可以查询的前提就是where语句后的内容(也就是两个表有共同的部分)
之前已经使用过重命名的方式,这里也可以对表名进行重命名,有些单词比较长的表名就可以使用简写的方式进行,代码指令:select vend_name,prod_name,prod_price from vendors v ,products p where v.vend_id = p.vend_id;
结果输出是一致的
这种使用表名.列名 = 表名.列名的方式属于内连接(inner join),标准默认的连接方式。上面举例了两表的连接,那么接下来看看三表连接如何实现,这里选择客户表、订单表、商品信息表进行操作,比如查询一下什么人在什么时间买了’TNT2’这个商品,代码指令:SELECT cust_name,order_date,prod_id from customers c,orders o, orderitems oi where c.cust_id = o.cust_id and o.order_num = oi.order_num and prod_id = 'TNT2';
以上使用的联结表,只是内部联结(内连接)或者叫做等值联结的简单联结,还有其他的三种方式,分别是自联结、自然联结和外部联结。
顾名思义就是自己和自己联结,那这样有什么必要吗?直接进行输出不就可以了嘛,还要整个自己联结干嘛?其实主要的应用场景是这样的,就是在一张表中,都是平面的关系,如果要展示层级的关系的话,就需要使用自联结的方式,示意图如下
要展示出员工和其上级的关系,因此就只能使用自联结进行查询了,代码指令:select e1.name,(select e2.name from emp e2 where e2.job_num = e1.head_num) as boss,e1.level from emp e1;
使用自身的表,然后要求匹配到工号与主管号一直的情况,这样就知道员工的层级关系了
就是直接使用两张表,让其野蛮匹配,如果不加限制,最后就是两张表中的每行数据都会相互匹配,假如表中有十行数据,那么自然联结后就是100行数据,比如进行产品表自然联结,代码指令:select p1.prod_id, p2.prod_name from products p1 , products p2;
产品表中15行数据,最终自然联结后就是225行数据,这种方式的使用不是很多,一般是在测试、做实验的时候会用到,也就是要考虑不同的工况,使用自然联结就会把所有的情况都考虑到
就是指定某一张表作为联结的依据,做个简单的示意图如下,如果以左边为依据,那么最终结果就会保留John的信息;反之就以右侧为依据
使用实例操作,代码指令:
SELECT * from customers c,orders o where c.cust_id = o.cust_id;
(内联结)SELECT * from customers c LEFT JOIN orders o on c.cust_id =o.cust_id;
(外联结)外联结数据量为6行,但是内连接只有5条(使用外联结应该是以数据量较多的一份表作为联结依据,这样会保留数据较多的那份数据,不然选择较小的那份数据,就相当于内连接了)
这个问题之前已经提到过了,可以看一下子查询作为计算字段使用的实例,这里再进行一些分组和使用外联结的操作。代码指令:
select cust_name,c.cust_id,count(*) from customers c ,orders o where c.cust_id = o.cust_id GROUP BY c.cust_id;
(内联结分组)select cust_name,c.cust_id,count(*) from customers c LEFT JOIN orders o on c.cust_id = o.cust_id GROUP BY c.cust_id;
(外联结分组)输出结果和上面的外联结实例类似,对于内联结没有出现cust_id=10002的数据,但是外联结就可以出现。
多数SQL查询都只包含从一个或多个表中返回数据的单条SELECT语句。MySQL也允许执行多个查询(多条SELECT语句),并将结果作为单个查询结果集返回。这些组合查询通常称为并(union)或复合查询(compound query)
需求场景:
关于相似结果的理解:就是查询的是相同字段,比如分别根据供应商编号和商品价钱返回供应商信息,首先执行两条查询语句
输出结果为:(可以发现查询的都是单个表,最后返回的字段都是相同字段)
基于结果发现,那么就可以将查询的结果进行组合(union),只需要把第一个查询语句后的’;'去掉然后加上union关键词即可,代码指令:select vend_id,prod_id from products where prod_price <= 5 UNION select vend_id,prod_id from products where vend_id in ('1001','1002');
组合后的结果会被自动去重
如果不希望查询结果自动去重可以在union后面添加all,这样最后的结果就会全部显示出来
按照之前梳理的关键词的使用顺序,那么要对组合结果排序自然是要使用order by,union两个查询自然也就变成了一个整体,那么order by的位置就应该在最后一个where后面(没有group by和having,其前面的一个关键词就是where),如果忘记了,可以查看分组过滤最后关于关键词使用顺序的总结。代码指令:select vend_id,prod_id ,prod_price from products where prod_price <= 5 union select vend_id,prod_id,prod_price from products where vend_id in (1001,1002) order by vend_id , prod_price;
排序已经完成了,能不能多组合一些表呢?上面只展示了两张表的组合,试一下三张表,代码指令:
select vend_id,prod_id ,prod_price from products
where prod_price <= 5
union
select vend_id,prod_id,prod_price from products
where vend_id in (1001,1002)
union
select vend_id,prod_id,prod_price from products
where vend_id in (1003,1005)
order by vend_id , prod_price;
输出结果证明是可以进行三张表的组合,因此这个unoin关键词可以组合很多表按照自己的需求来进行整理,组合的表越多,最后的输出结果可能也就越多了
之前介绍了关于数据库中的文本数据匹配的知识,但是存在着几个重要的限制,如下:
这就要填一下最开始介绍引擎时候埋下的坑了前面介绍了innodb不支持全文索引(支持事务),myisam支持全文索引(不支持事务),所以要使用全文索引,就必须开启myisam引擎。而在创建表单的时候对于产品信息注释表(productnotes)选用了该引擎,因此下面就可以对此表进行相关的操作
常见的文本匹配关键词:
开启全文索引除了引擎的要求,还有对于字段数据的要求,必须为text类型,索引类型为Full Text类型,这三项设置完毕后就可以开启
查看一下目标字段的信息,可以看出productnotes表中的note_text字段为文本数据
接下的操作进行传统模式和全文索引的方式同时进行(因为这里的数据量较小,采用这两种方式仅为演示,如果数据量较大时候,传统方式就不实用了)
select note_text from productnotes where note_text like '%rabbit%';
select note_text from productnotes where Match(note_text) AGAINST ('rabbit');
操作的方式就是把match…against语句放置在from前面,然后删除where即可,代码指令:select note_text,MATCH(note_text) AGAINST ('rabbit') from productnotes;
使用布尔查询模式,只需要在上面的语句最后括号内添加in boolean mode
。首先介绍一下这里进行布尔操作的符号
进行布尔查询,就可以解决经典模式中无法精准查询的问题。就可以指定某些单词包含和哪些单词不包含,比如在包含’rabbit’情况下还必须要包含’bait’,代码指令:
select note_text from productnotes where note_text like '%rabbit%bait%';
select note_text from productnotes where MATCH(note_text) AGAINST ('+rabbit +bait*' IN BOOLEAN mode );
比如再进行优先级查询,代码指令:select note_text, MATCH(note_text) AGAINST ('+safe +(
select是最常使用的SQL语句了,也就是前面花这么多时间进行梳理的原因, 此外还有其它的关键词,首先就是要介绍的insert,是用来插入(或添加)行到数据库表的,可以完成的工作包括如下:
1)单行数据的插入,代码指令:insert into customers values ( null,'ABC','100 Main street','Los angeles','CA','90046','USA',null,null);
需要指定要插入的表单名称,然后values()括号中要填写对应字段的数据,插入的数据的个数应该与原表每行数据的个数相等,允许部分字段有空值。
2)多行数据的插入,就是多次执行insert into语句的操作,中间记得以 ‘;’ 分割,还有一点就是部分数据的插入,如果不想指定的字段数据就得一直写null,那么就可以直接在表名后面加个括号,后面添加对应要添加的字段,最后插入的数据只要和括号中的内容对应即可,就可以节省代码量的书写
可能我觉着这种方式还是比较麻烦的,insert into语句写了好几遍,有没有直接把数据合并在一起进行写入的方法,这里就可以使用括号中间以逗号分隔的方式进行合并,代码指令:insert into customers (cust_name,cust_zip) values ('2CDEF','199878'),('3CDEF','399878') ('4CDEF','599878'), ('5CDEF','799878');
这种方式就方便多了,今后使用python进行数据插入的时候也可用
查询的结果有时需要插入到目的表单中,也可以使用insert into操作。代码指令:
create table customers2 as SELECT * FROM customers;
(根据目标表创建备份表,操作备份的表)DELETE FROM customers2;
(删除备份表中的数据,一会就讲)insert into customers2 select * from customers;
(将查询的结果插入到备份的表中)为了保证数据的安全性,养成操作备份数据的习惯,这样不会对原数据造成影响,出问题了也可以随时修改。
上面介绍了插入操作,顺带使用了delete删除指令,现在就介绍一下关于更新和删除的操作。为了更新(修改)表中的数据,可使用UPDATE语句。可采用两种方式使用UPDATE:
同样删除操作也是如此,可以删除特定行,也可以删除所有行(上面的已经用过了)。关于更改和删除也应该和插入数据一样,在操作之前尽量使用备份数据进行操作,代码指令:
update customers2 set cust_email = '[email protected]' where cust_id =10005;
select * from customers2 where cust_id =10005;
这样就实现了定点数据的更新。注意在进行更改的时候要先写where语句,最后再写select相关语句,不然会把所有的信息都给修改了,这步是不可逆的,一旦发生就麻烦了
删除操作就和更新类似,由于不要设置数据,set就不需要了,代码指令:delete from customers where cust_id = 10005;
视图是虚拟的表。与包含数据的表不一样,视图只包含使用时动态检索数据的查询,重用SQL语句。注意相关细节:
其核心功能:简化sql语句,实现代码的复用,隐藏业务逻辑。比如将最初三表联结的例子拿过来演示,判断顾客到底买了多少东西?之前的SQL语句如下:select cust_name,c.cust_id,count(*) from customers c ,orders o where c.cust_id = o.cust_id GROUP BY c.cust_id;
那么如果直接创建视图(只需要在原来的代码前面加上create views 视图名 as
),就可以把这个查询结果作为一个虚拟的表,供后来的人查询(有种数据分析师要数据,然后数据库工程师就开放所需部分数据的味道了)。执行创建视图的代码就可以发现在左侧的Views出刷新,就会出现刚刚创建的视图,这时候在查询区就可以直接对这个虚拟表进行查询了,不用再管之前怎样的业务逻辑。
如果需要查看一下创建视图的SQL语句,可以通过show create view 视图名;
然后输出的第二个字段中的内容就是创建视图的SQL语句了
如果要删除的话,就是直接drop view 视图名;
通过以上操作发现对于业务逻辑来说,不管是专业还是新手,在工作处理的时候,任务来了,你只要整个视图把最终的结果给到数据分析师们就可以了。那么这里面的字符串的处理(格式化的操作,去空值什么的),在python中操作要比在MySQL中要方便的。还是要用好本质工具,MysSQL数据库,数据库主要就是用来进出数据的,数据清洗,就用python或者其他的方式就行了。
看到上面的操作是不是有种熟悉的感觉,就是有点类似于封装了函数,然后之后再调用了函数?其实并不是,上面只是将查询结果作为一个虚拟的表,方便后续人员操作,隐藏业务逻辑。而真实SQL里面真正类似函数的是存储过程(软件里面的名称就是Functions),可以根据输入提供输出,中间的处理过程封装好(多组sql语句形成的组合),直接拿过来用。
首先是存储过程的使用,使用之前应该声明一下这是各存储过程(所有编程语言都是一样,得让别人知道你写的是这个东西),比如把计算平均价格的代码封装一下,指令代码如下
delimiter //
create PROCEDURE productpricing()
BEGIN
select avg(prod_price) as avgprice
from products;
end //
delimiter ;
call productpricing()
存储过程使用中注意事项:
create PROCEDURE 名称()
;
删除存储过程:drop procedure 过程名;
函数分为有参和无参,上面介绍了无参,接着就是进行有参的函数创建。
1)形参函数,就是参数中的内容只是一个形式上的参数,在最后函数调用上,用指定格式的形式调用,最后输出变量的时候参数保持一致即可
create PROCEDURE productpricing2(
out pl decimal(8,2),
out ph decimal(8,2),
out pa decimal(8,2)
)
BEGIN
select min(prod_price) into pl
from products;
select max(prod_price) into ph
from products;
select avg(prod_price) into pa
from products;
end;
call productpricing2(@p1,@p2,@p3);
select @p1,@p2,@p3;
输出结果为:(调用时候的三个变量名称可以随便起,但是格式要有@,使用时候也得用相同的参数)
2)实参函数,就是有真实输入变量的参数,最后在调用函数的时候需要进行输入的
create procedure ordertotal (
in onumber int,
out ototal decimal(8,2)
)
BEGIN
select sum(item_price* quantity)
from orderitems
where order_num=onumber
into ototal;
end;
CALL ordertotal('20005',@p);
SELECT @p;
游标:在存储过程中,根据需要对数据集合进行前后浏览的一种应用。有时,需要在检索出来的行中前进或后退一行或多行,这就是使用游标的原因。游标(cursor)是一个存储在MySQL服务器上的数据库查询,它不是一条SELECT语句,而是被该语句检索出来的结果集。在存储了游标之后,应用程序可以根据需要滚动或浏览其中的数据。
游标主要用于交互式应用,其中用户需要滚动屏幕上的数据,并对数据进行浏览或做出更改。
1) 创建游标
CREATE PROCEDURE processorders()
begin
declare ordernumbers CURSOR
FOR
select order_num from orders;
--OPEN
open ordernumbers;
--CLOSE
close ordernumbers;
end;
2)浏览数据,建议还是使用python操作游标
CREATE PROCEDURE processorders4()
begin
-- declare VARIABLES
DECLARE o int;
DECLARE done boolean DEFAULT 0;
declare ordernumbers CURSOR
FOR
select order_num from orders;
declare continue handler for sqlstate '02000' set done=1;
-- OPEN
open ordernumbers;
REPEAT
FETCH ordernumbers into o;
select o;
until done end repeat;
-- CLOSE
close ordernumbers;
end;
call processorders4()
输出结果为:(就会把订单编号依次输出,如果使用过python操作数据库,还是建议在python中使用for循环吧)
有点类似于定时程序,就是满足要求后执行功能。比如:每当订购一个产品时,都从库存数量中减去订购的数量;无论何时删除一行,都在某个存档表中保留一个副本等
触发器使命:在我们执行delete update insert语句时自动执行另外一组sql语句的功能。比如进行插入提醒
#首先设置触发器,指定一下规则和要发送的内容
create trigger newproduct after insert on products
for each row select 'Product add' into @ee;
#插入数据
INSERT INTO products(prod_id, vend_id, prod_name, prod_price, prod_desc)
VALUES('ANV21', 1001, '3 ton anvil', 19.99,
'3 ton anvil, black, complete with handy hook and carrying case');
#最后查看一下状态
SELECT @ee;
输出结果为:
1)插入触发器
以上只是简单的创建触发器并激活,接下来就进行实际场景的使用,比如将插入的产品id输出
#因为刚刚已经创建一个触发器了,这里先删除后再创建
drop TRIGGER newproduct;
#指定跟新规则和发送的信息
create trigger newproduct2 after insert on products
for each row select NEW.prod_id into @ee;
#这里将产品的编号改成22了
INSERT INTO products(prod_id, vend_id, prod_name, prod_price, prod_desc)
VALUES('ANV22', 1001, '3 ton anvil', 19.99,
'3 ton anvil, black, complete with handy hook and carrying case');
#最后查看一下内容
SELECT @ee;
输出结果:(可以发现最后将修改后的产品的id输出了)
2)删除触发器
#这里就是一个模板,archive_orders是要备份的表
create trigger deleteorder before delete on orders
for each row
BEGIN
insert into archive_orders(order_num,order_date,cust_id)
values (old.order_num,old.order_date,old.cust_id);
end;
#创建备份的表并删除里面的内容
CREATE table archive_orders as SELECT * from orders;
DELETE from archive_orders;
#最后在查看一下orders表中删除数据后是否备份
SELECT * from archive_orders;
输出结果:(注意为了不破坏orders的数据,这里删除前随便添加了一行数据,然后删除的也是这一行数据)
在使用事务和事务处理时,有几个关键词汇反复出现。下面是关于事务处理需要知道的几个术语:
这些名词可以类比一下玩游戏的过程:首先每一关卡都是一个事务,有不同的经历战斗组成;如果这一关在挑战Boss中失败,就直接回退到开始的时候;如果完成了就恭喜你通过这一关,提交个人成绩;如果打游戏的过程中需要离开一下就可以进行存档,就对应设置保留点。
事务处理是一种机制,用来管理必须成批执行的MySQL操作,以保证数据库不包含不完整的操作结果。利用事务处理,可以保证一组操作不会中途停止,它们或者作为整体执行,或者完全不执行(除非明确指示)。如果没有错误发生,整组语句提交给(写到)数据库表。如果发生错误,则进行回退(撤销)以恢复数据库到某个已知且安全的状态。(这里还可以类比于ATM存款,我存成功了,银行账户上应该多一笔钱,如果失败了,就得把钱退给我)
前提条件:innodb支持事务,myisam不支持事务。在同一个编辑区内执行如下代码
select * from customers2;
start TRANSACTION;
delete from customers2;
select * from customers2;
ROLLBACK;
select * from customers2;
输出结果:(首先查询时候会自动查询到全部的数据,然后开启事务,并删除里面的所有数据,在查看时候结果2中就是空的,但是又来了一个rollback进行事物回滚,也就是回到了start transaction之前的状态,所以在此查询的时候就是相当于第一条查询语句,结果也就和最初的一样了)
如果将上面的rollback替换成commit,就相当于把更改的内容进行保存了,最后的结果也就是表中数据为空,但是这个表还是存在的
程序可以在某一处停止,回到特定的状态,比如
#刚刚提交删除的表,这里再把数据备份过来
CREATE table customers2 as SELECT * from customers;
#第一次查看数据
select * from customers2;
#开始事务,删除顾客id为10001的数据
start TRANSACTION;
delete from customers2 where cust_id = 10001;
#在上面的操作结束存档
SAVEPOINT x1;
#继续删除顾客id为1002的数据
delete from customers2 where cust_id = 10002;
#第二次查看数据
select * from customers2;
#回滚到指定存档位置
ROLLBACK to x1;
#最后查看一下数据
select * from customers2;
输出结果为:(第一次查看数据自然也就是全部的数据,经历过两次删除后结果2中就没有10001和10002的数据,然后经过读取存档,又把1002的数据显示出来了,所以最后的一次查询只是没有了10001的数据)
像所有数据一样,MySQL的数据也必须经常备份。由于MySQL数据库是基于磁盘的文件,普通的备份系统和例程就能备份MySQL的数据。但是,由于这些文件总是处于打开和使用状态,普通的文件副本备份不一定总是有效。
如果选择使用软件备份,操作如下,选择要备份的数据库,然后点击右上方的Backup按钮后,选项卡上面显示的New Backup就是用来创建一个备份的操作,在弹出的界面核实一下是确定的数据库后点击备份就可以了。
如果要看一下备份数据所在的位置,都可以右键点击,选择查看对象信息即可。
1)备份一个数据库
如果使用命令行操作,需要在bin文件夹下打开命令行窗口,输入下面指令:
mysqldump.exe -h 127.0.0.1 -P 3306 -uroot -p --database erp > c:\mysql\createdb.sql
mysqldump.exe -h 127.0.0.1 -P 3306 -uroot -p erp1 orders > c:\mysql\createdb2.sql
3)如果要备份多个数据库
mysqldump.exe -h 127.0.0.1 -P 3306 -uroot -p --database erp1 erp2 erp3 > c:\mysql\createdb3.sql
4)如果备份所有数据库
mysqldump.exe -h 127.0.0.1 -P 3306 -uroot -p --all-database > c:\mysql\createdb4.sql
5)如果备份数据库中所有表,但是不含数据
mysqldump.exe -h 127.0.0.1 -P 3306 -uroot -p --no-data --database erp1 > c:\mysql\createdb.sql
6)如果备份数据自带删除老表功能
mysqldump.exe -h 127.0.0.1 -P 3306 -uroot -p --add-drop-table --add-drop-database erp1 > c:\mysql\createdb2.sql
1)数据导出
比如把查询的数据直接保存在本地,代码指令:select * from customers into OUTFILE 'c:/mysql/data1.csv';
2)数据导入
比如把刚刚导出的数据重新导入到customers2中(首先删除表数据),代码指令:load data INFILE 'c:/mysql/data1.csv' into table customers2;
第一次查询的结果为空,但是第二次查询的结果就是原来的数据了(下面显示的为result1的结果)
数据库工作人员把他们工作中的相当一部份时间花在了性能调整上、试验改善DBMS性能。在诊断应用的滞缓现象和性能问题时,性能不良的数据库(以及数据库查询)通常是最常见的祸因。
这里在上方数据导入的时候多执行几次操作,假使执行了20次然后为了查找方便手动添加一条数据,那么最终customers2表中的数据就应该为101条
接下来就看一下使用传统(默认的方式)来查询添加的这条数据,使用Explain语句让MySQL解释它将如何执行一条SELECT语句,代码指令:EXPLAIN select * from customers2 where cust_id = 99999;
输出结果中可以发现查询了86行,而且找寻了所有字段类型
在点击status状态栏,看一下这次查询传送的数据信息,共发送了387bytes数据和接受了60bytes数据
那么看一下性能如何优化,前面提到的操作系统的参数,这个还是不要随便乱动(毕竟自己的电脑配置也很渣~),那么就试一下简单利用索引来提升速度。比如这里我插入的这条数据是只有cust_id字段是有数据的,那么把cust_id这个字段单独设置一个索引的方式就可以了,操作如下,进入表设计的界面,点击indexs选项卡,随便起一个名称,然后选取Fields为要查询那个数据的字段名称(这里就是cust_id),点击save保存后,后面的Index Type和Index Method都会自动配置
这时候在重新运行一下刚刚的查询代码,可以发现查询的结果。这里只进行了一行的查询就得到了结果,这种方式查询对数据性能是极大的改善,也提高了程序的运行效率
至此整个MySQL的知识点就梳理完毕了,如果需要对数据库操作进行熟练的掌握,后面还需要进行多次的练习,巩固
博客中对于MySQL基础知识点进行了详细梳理,命令行操作和可视化图形界面结合进行操作讲解,再难以理解的地方配置图形进行形象化描述,每一个实例不光有代码的展示还有最后输出的图形界面展示,方便阅读和理解。感觉不是很好懂的地方就是在游标这里的介绍,日后有时间会更新python操作数据库的案例进行对比研究。