随着时代的发展,人类活动产生的信息越来越多,大家常说,现在这个时代是大数据时代。在这样一个前提下,数据的存储成为我们必须要认真对待和研究的问题了。SQL(Structured Query Language)结构化查询语言,是当今三大主流关系型数据库(MsSQL,MySQL,Oracle)的通用操作语言,今天就给大家分享一下我对数据库和 SQL 的一些认识,希望对大家有用。
一 数据库简介
1,什么是数据库
在理解什么是数据库之前,我们首先要明白另一个问题,即什么是数据?
一个人的身高、体重、年龄;一辆车的品牌、颜色,尺寸;一个运动员在完成比赛期间所用的时间,速度,心率;即使你计算机上的图片、视频等都是数据。我的结论就是:用于描述事物或事件的属性,并且能被人们识别的符号就是数据。
明白了什么是数据,不难推测出:数据库就是数据存储的仓库,各种数据的集合。当然数据库中的数据都是有序的,有组织的,这是为了能让管理数据更方便。
2,什么是数据库管理系统
你可能常常听到人们常说:MsSQL 数据库、MySQL 数据库、Oracle 数据库等等,其实他们多数指的是不同的数据库管理系统。SQL server 是微软旗下的数据库管理系统,MySQL 和 Oracle 目前都属于甲骨文公司。
数据库管理系统本质上是一套软件,专门用于帮助用户管理数据库中的数据。
3,什么是关系型数据库
根据数据库组织和存储数据的方式,数据库通常分为四类:层次式数据库、网络式数据库、关系型数据库、非关系型数据库。
前两种基本已经成为了历史,现在主流的是后两种,但占市场绝对优势的仍然是关系型数据库(Relational Database Management System:RDBMS)。顾名思义,关系型数据库是依照数据与数据之间的关系来组织存储数据的。
为了方便后面的书写,这里进行一个声明:从此之后,文中提到的所有数据库均指关系型数据库。
4,数据库和 SQL 有什么联系
SQL,指结构化查询语言,全称是 Structured Query Language。
SQL 是用于访问和处理数据库的标准的计算机语言。
SQL 是一种 ANSI(American National Standards Institute 美国国家标准化组织)标准的计算机语言。
SQL 是一种命令式语言。
当今三大数据库管理系统均支持标准 SQL。
二 如何存储
现在,我们已经知道什么是数据库、数据库管理系统和 SQL 了。那么,既然数据库是用来存储数据的,那么数据库具体是通过什么方式来进行存储的呢?它又是怎样保证数据的有效性、合法性呢?既然说现今社会主流的仍然是关系型数据库(RDBMS),那么数据库又是怎么在数据与数据之间确定关系的呢?
1,数据的存储方式
开门见山的说:数据库是通过“表”来存储数据,根据不同的数据库管理系统,以不同的文件格式最终被保存到物理磁盘上(多是硬盘)。
这里的表类似 Excel 表格,表中的数据由不同的行和列组成,每一列在数据库中我们称为一个字段,每一行数据称为一条记录或元组,它算是数据在数据库中的基本单元。
如果用一张表来记录一类事物,那么每一条记录可以看成这类事物单独的个体,而不同的列则是用来记录事物的每个具体的特性。例如一个Person 表,用来记录人员信息,每个人员都可以拥有自己的姓名,年龄,性别,身高等信息,那么这个表应该具有姓名,年龄,性别等这些列。
name | age | sex | height | birthday |
张三 | 20 | 男 | 180 | 2000-01-01 |
李四 | 18 | 女 | 170 | 2002-01-01 |
王五 | 25 | 男 | 165 | 1995-01-01 |
在这个表中,每一行就是一条记录,代表一个具体的人,每一列都是用来描述人的不同特征的,从表中可以看出,每个人都拥有不同的特性。
2,确保数据的有效性
上面的表格有一个问题:不能确保数据的有效性、合法性。为什么这么说呢?
试想一下,这个世界上人那么多,总会有其他人也叫张三,并且年龄正好也是20,正好也是个身高180的大汉,那么在表中岂不是有两条一模一样的记录?那我们怎么来区分到底谁是谁呢?还有,在不加任何限制的情况下,我可以随意录入人员的年龄,比如我错把王五的年龄记录成了250,这世上还没有能活250岁的人吧,显然这样的数据是不合常理的。
那么数据库是怎么解决这个问题的呢?答案是:约束。
约束作用于表的列,约束用于规定表中的数据存储规则。如果我们在录入数据时,不符合约束的规定,那么你将不能把该数据录入数据库中。
3,约束
数据库中主要的约束有以下几种:
NOT NULL - 指示某列不能存储 NULL 值,即空,什么都没有。
UNIQUE - 保证每行的某一列必须具有唯一的值,不能重复。
PRIMARY KEY - NOT NULL 和 UNIQUE 的结合。确保某列(或多个列的结合,即联合主键)有唯一标识,有助于更容易更快速地找到表中的一个特定的记录。
FOREIGN KEY - 保证表中某一列的数据来自另一个表中的某一列。能使用外键约束的列,其数据在另一个表中必须是主键。数据库正是通过外键来建立表与表之间的联系。
CHECK - 保证列中的值符合指定的条件,确保数据的合理性。
DEFAULT - 规定没有给列赋值时的默认值。
本章主要讲解数据库如何存储数据和如何确保数据的有效性,下一章将详细介绍如何创建表,如何为表的字段添加约束,以及如何向表中插入和删除数据等。
三 如何操作
1,基础语法
前面提到,SQL 是命令式语言,所以它的语法其实非常简单,每一条命令就是一条语句,每条语句以“;”结束(非必须)。并且 SQL 语句对大小写不敏感,不过为了方便阅读和维护代码,请尽量统一命令的大小写。
SQL 中的命令总共分为四大类:
A:DDL(Data Definition Language)数据定义语言
主要命令包括:create(创建)、alter(修改)、drop(删除)
B:DQL(Data Query Language)数据查询语言
主要命令包括:select
C:DML(Data Manipulation Language)数据操纵语言
主要命令包括:insert(插入)、update(修改),delete(删除)
很多时候,select 命令也被认为是 DML 语言的一种,所以,如果你在其他地方听到这种说法时,不必感到诧异。
D:DCL(data Control Language)数据控制语言
主要命令包括:grant(授权)、revoke(回收)、commit(提交)、rollback(回滚)等
数据控制语言主要是针对数据库安全性方面的操作,可以简单理解为权限管理,这部分命令 DBA 经常使用,一般数据开发人员用的较少。
2,使用方式
DDL:
1 create database "my_db";--创建一个库 2 use "my_db";--选择刚刚穿件的库 3 create table "my_tb" 4 ( 5 --字段名1 数据类型 约束, 6 --字段名2 数据类型 约束, 7 --...... 8 );--在“my_db”中创建表“my_tb” 9 10 alter table "my_tb" 11 add 字段名 数据类型;--修改表,并向其新增一列 12 alter table "my_tb" 13 alter 字段名 数据类型;--修改表中某一列的数据类型 14 15 drop table "my_tb";--删除表 16 drop database "my_db";--删除库 17 truncate table 表名;--清空表中的数据
DQL:数据查询语言是 SQL 的重中之重,将在下一页单独讲解,这里仅给出 select 命令的基础用法。
1 /*以上面的 Person 表为例*/ 2 select name ,age from Person;--查询Person表中所有人员的姓名及年龄 3 select * from Person;--查询Person表中的所有数据,*是通配符
DML:
1 /*以 Person 表为例*/ 2 insert into Person (name,age,sex,height) 3 values('小明',12,'男',150); 4 --向表中插入一条数据,指定插入的列 5 insert into Person 6 values ('小黑',22,'男',170); 7 --如果需要插入每一列的数据,可以不指定具体的列 8 update Person set sex='女' where name='小明'; 9 --修改小明的性别为女 10 delete from Person where name='小明'; 11 --删除姓名为小明的记录
3,创建约束
创建约束有两种方式,其一:在创建表时同时创建约束,其二:同过 alter 命令向已创建的表添加约束。
创建表时:
1 /*创建表时即添加约束*/ 2 create table tablename 3 ( 4 id int identity(1,1) primary key, 5 name varchar(50) not null, 6 idcard char(18) unique, 7 city varchar(50) foreign key(city) references City(id),--city列的值通过外键绑定City表的id列 8 age int check (age>0 and age<150), 9 email varchar(50) not null 10 )
表已创建时:
1 /*通过 alter 命令添加约束*/ 2 alter table tablename 3 add 4 constraint ck_email check (email like '%@%'); 5 --通过 constraint 可以指定约束的名字,创建表时添加约束也可以使用,但它不是必须的
联合主键:
1 --创建表时: 2 create table tablename 3 ( 4 col1 ... , 5 col2 ... , 6 ......, 7 constraint pk_name PRIMARY KEY (col1,col2) 8 ) 9 --表已创建: 10 alter table tablename 11 add 12 constraint pk_name PRIMARY KEY (col1,col2)
4,事务
事务是由一组 SQL 语句组成的执行单元,该执行单元被视为一个不可分割的整体,单元内的语句要么全部执行成功,要么全部失败,不允许某些成功,而另外一些执行失败。如果单元中某一条语句执行失败,则前面所有被成功执行语句将回滚(使其失效),即数据回到那些语句还没执行时的状态。如果所有语句被全部成功执行,那么我们就说该事务被顺利执行了。事务也是一种保证数据完整性的方式。
总结起来,事务具备以下 ACID 特性:
A(Atomicity 原子性):原子性是指事务是一个不可分割的执行单元。里面的操作要么都成功,要么都失败。
C(Consistency 一致性):一致性是指事务必须是数据从一个一致性状态过度到另一个一致性状态。关于一致性,还是那个经典的转账例子:两个人各有1000元钱,数据库中存储的钱总和是2000元,如果一个事务的操作是:他们两之见转一次账,那么执行完事务之后,数据库中存储的钱总和还应该是2000元。
I(Isolation 隔离性):隔离性是指在被一个事务操作的数据,不应该再被另一个事务所干扰。迸发执行的各个事务之间不能相互干扰。
D(Durability 持久性):持续性是指一个事务被提交以后不可恢复,它对数据的影响是永久性的,如果需要在该事务提交后再恢复到原始状态,只能通过另一个事务。
不同的数据库管理系统对事务的支持存在较大差异。但通常情况下,对于数据的 DML 操作,每一条语句的执行都算一次事务,而且这种事务是隐式的,系统在后台直接开启,并在执行完后关闭,用户是不可见的。
对于不同数据库中事务的具体实现,请持续关注我后续关于三大关系型数据库的介绍文章。
5,数据类型
不同数据库支持的数据类型有较大差异,即使相同的数据库不同的版本也存在一定差异,所以在使用时请尽量以官方文档为依据,这里仅列出部分常用的、通用的数据类型:
bigint(整型)、varchar(n)(可变长度字符串)、boolean(布尔值)、float(浮点型)、date(日期)、time(时间),timestamp(日期+时间型)、xml(XML型)。
四 如何查询
相较于其他命令,数据库开发中用的最多的就是 select 了,没有之一。
1,普通查询
通过几条简单的查询语句来说明:
1 insert into Person values('张五',30,男,175,'1990-01-01'); 2 insert into Person (name,sex,height,birthday) 3 values('张六',男,175,'1990-01-01'); 4 --先插入两条新数据 5 select * from Person as P 6 where P.birthday between '1990-01-01' and '2010-01-01' 7 and P.name like '%张%' 8 and P.age is not null 9 --查询生日在1990-2010间姓名包含张并且年龄不为空的所有人员信息
通过上面的例子,我要说明查询语句的一些基本用法。
首先是紧跟在 select 命令之后的信息,它表示需要被查询的字段,* 星号表示通配符,意为查询所有表中的字段。
其次是 from 关键字,它表示从哪个表中查询数据,紧跟在其后的是被查询的表名。
as 关键字的作用是给表起一个别名,主要是为了简化代码,被查询的字段,也可以使用 as 起一个更通俗易懂的别名。
where 关键字用于指定过滤条件,通过 where 我们可以只查询我们需要的数据,提高查询速度。
and 关键字用来连接不同的过滤条件。
between...and... 是一个组合范围关键字,如上例所示,它可以用来指定时间范围,还可以用来指定数字的取值范围等。
like 用来指定模糊查询,% 表示零个或多个任意字符,_ 表示任意单个字符,[ ] 表示指定字符中的一个,[^ ] 表示不在指定字符中的一个。如果需要使用 like 查询包含特殊字符的列,比如 %、_等,那么你需要用到 escape 定义转意字符。
1 select * from table 2 where col like '%$_%' escape '$'; 3 -- $ 符号被定义成转意字符,紧跟在 $ 后面的字符会被当成普通字符匹配
一个特别的:在过滤条件中,判断某列的值是否为空,应该使用 is 或 not is 关键字,而不是使用 = 等号或 != 不等号 。
2,分组和排序
SQL 中的分组使用 group by 实现,group by 通常和聚合函数一起使用,单独使用 group by 分组没有现实意义。
SQL 中的聚合函数从列的计算中获取值,一般返回一个单一的值。SQL 中除了聚合函数,还有另一类标量函数,它们基于输入的值返回一个单一的值。
聚合函数 | 含义 | 标量函数 | 含义 |
AVG() | 返回平均值 | UCASE() | 转换为大写 |
COUNT() | 返回行数 | LCASE() | 转换为小写 |
FIRST() | 返回第一个记录 | SUBSTRING() | 截取字符串 |
LAST() | 返回最后一个记录 | LEN() | 返回字段长度 |
MAX() | 返回最大值 | ROUND() | 四舍五入 |
MIN() | 返回最小值 | NOW() | 返回系统时间 |
SUM() | 返回总和 | FORMAT() | 格式化字符串 |
不同数据库对函数的实现有一定差异,但常用的函数使用方式都相同:如上黑体字列出的聚合函数。
单独使用聚合函数:
1 select avg(age) as "平均年龄" from Person;--计算所有人的平均年龄 2 select max(age) as "最大年龄",min(age) as "最小年龄" from Person;--计算最大年龄和最小年龄 3 select count(name) as "人数" from Person;--统计表中的人数 4 --count()通过指定列来统计个数,忽略 NULL 值
聚合函数 + group by:
1 select sex,sum(name) as "人数" from Person 2 group by sex 3 having sum(name)>2;--查询性别人数之和大于的的性别和人数 4 --group by 后面为聚合(或者叫分组)的字段,查询中用来分组的字段都必须出现在 group by 之后,having 用来对聚合后的数据再过滤
如果有多个字段需要被用来分组,那么他们的分组顺序是从左至右的,并且最右边的字段将被当做聚合函数计算的最小分组。
使用 order by 排序:
1 select * from Person order by age desc; 2 --按年龄从大到小排序查询所有信息,desc 表示倒序。默认是asc 表示升序,可以省略。group by 可以和 order by 一起使用,但 order by 永远在查询语句的最后
3,连接查询
连接查询分为三类:内连接,外连接,全连接。
我们知道,现实世界中的事物都存在各种联系,通过事物之间的种种联系,你可以收集到更多在一类事物上不存在的信息。正如著名的七人理论:你最多只需要通过7个人就能和世上任何一个人认识。这也是一种典型的关系模型。
我们的关系型数据库正是通过各种各样的外键把不同表关联起来的。只要根据他们的关系,我们就可以在不同的表中查找我们想要的任何数据了。
A:内连接
内连接的用法如下:
1 select * from tableA,tableB;--方式一 2 select * from tableA A 3 join tableB B 4 on 1=1;--方式二(join 是inner join 的缩写,on 用来指定组合产生新数据连接的条件,这里1=1始终为真,意为没有任何限制的连接两个表)
内连接会使用两个表中的数据产生一个笛卡尔积,简单的说就是:数据库把两个表中的数据认为是多对多的关系,用表 A 的每一条数据去和表 B 中的每一条数据组合成新的数据。
最终的结果是:总记录的条数是两个表记录条数的乘积,字段数是两个表字段数的总和。
很显然,通常情况下,笛卡尔积并不是我们需要的数据。方式一可以通过 where 来设置过滤条件,而方式二则是通过 on 指定连接条件。比如如下sta和sal两个表:
sta表 | id | name | sal_grade | sal表 | sal_grade | sal |
1 | 张三 | 3 | 1 | 8000 | ||
2 | 李四 | 2 | 2 | 6000 | ||
3 | 王五 | 1 | 3 | 4000 |
1 select * from sta,sal;--会返回 9 条记录,每条记录有 5 个字段 2 select * from sta,sal 3 where sta.sal_grade = sal.sal_grade;--只会返回9条记录中sta.sal_grade = sal.sal_grade的数据,共3条 4 select * from sta join sal 5 on sta.sal_grade = sal.sal_grade;--也只返回 3 条记录,但他们的原理是有差别的,where 是在产生笛卡尔积后过滤,而 join 方式是在确定连接关系时就开始过滤,最终不会产生笛卡尔积,除非 on 指定的条件为真 6 select sta.id,sta.name,sal.sal from sta join sal 7 on sta.sal_grande = sal.sal_grande;--只查询需要的字段,而不是两个表中所有的字段
B:外连接
外连接又分为左外连接和右外连接,左外连接会返回所有左表中的记录,无论是否满足连接条件,而右外连接刚好相反,会返回右表中所有的记录,无论是否满足连接条件。这里的左和右是指 join 关键字的左和右。
外连接最终的输出结果表现为:总记录数不能确定,因为可能存在一对多的关系。而字段数仍为两个表字段数之和。那些满足连接条件的记录,每个字段都会有确定的值,而那些不满足连接条件的记录,则只有左表或右表的字段有值,另一个表的字段为 NULL(具体取决于是左外连接还是右外连接)。
1 insert into sta values(4,'张五',5); 2 select sta.id,sta.name,sal.sal from sta 3 left join sal 4 on sta.sal_grande = sal.sal_grande;--返回 4 条记录,但张五的工资字段为 NULL,因为 sal 表中并没有 sal_grande 为 5 的记录可以匹配
C:全连接和交叉连接
全连接使用 full join,结果集的数量不确定,字段数是两个表字段数的总和,但是,依然会返回左右表中不满足连接条件的记录,只是另一边表的字段则均为 NULL。
交叉连接使用 cross join,产生的结果仍是笛卡尔积,等价于内连接。
insert into sal values(4,2000); select * from sta full join sal on sta.sal_grande = sal.sal_grande;--返回 5 条记录,但name为张五的记录中sal表的字段值均为 NULL,工资等级为 4 的记录中 sta 表的字段均值为 NULL
4,联合查询
连接查询是把表的字段横向组合到一起,从而产生新的数据,而联合查询是把两个表的记录纵向组合到一起,联合查询的要求是:两个表的字段数量和对应字段的数据类型必须相同。SQL 通过 union 联合两张表。
1 select col1,col2,col3 from tableA 2 union 3 select col1,col2,col3 from tableB
请注意,union 联合表后,默认只选取不同的记录,如果你希望在联合后的记录中允许相同的数据存在,请使用 union all。