SQL是结构化查询语言的缩写,用来访问和操作数据库系统。SQL语句既可以查询数据库中的数据,也可以添加、更新和删除数据库中的数据,还可以对数据库进行管理和维护操作。不同的数据库,都支持SQL。但,大部分数据库都在标准的SQL上做了扩展。
数据模型
数据库按照数据结构来组织、存储和管理数据,实际上,数据库一共有三种模型:
层次模型
网状模型
关系模型:关系模型把数据看作是一个二维表格,任何数据都可以通过行号+列号来唯一确定,它的数据模型看起来就是一个Excel表
随着时间的推移和市场竞争,最终,基于关系模型的关系数据库获得了绝对市场份额。
数据类型
名称 类型 说明
INT 整型 4字节整数类型,范围约+/-21亿
BIGINT 长整型 8字节整数类型,范围约+/-922亿亿
REAL 浮点型 4字节浮点数,范围约+/-1038
DOUBLE 浮点型 8字节浮点数,范围约+/-10308
DECIMAL(M,N) 高精度小数 由用户指定精度的小数,例如,DECIMAL(20,10)表示一共20位,其中小数10位,通常用于财务计算
CHAR(N) 定长字符串 存储指定长度的字符串,例如,CHAR(100)总是存储100个字符的字符串
VARCHAR(N) 变长字符串 存储可变长度的字符串,例如,VARCHAR(100)可以存储0~100个字符的字符串
BOOLEAN 布尔类型 存储True或者False
DATE 日期类型 存储日期,例如,2018-06-22
TIME 时间类型 存储时间,例如,12:20:59
DATETIME 日期和时间类型 存储日期+时间,例如,2018-06-22 12:20:59
主流关系数据库
目前,主流的关系数据库主要分为以下几类:
- 商用数据库,例如:Oracle,SQL Server,DB2等;
- 开源数据库,例如:MySQL,PostgreSQL等;
- 桌面数据库,以微软Access为代表,适合桌面应用程序使用;
- 嵌入式数据库,以Sqlite为代表,适合手机应用和桌面程序。
安装MySQL
要在Windows或Mac上安装MySQL,首先从MySQL官方网站下载最新的MySQL Community Server版本:
https://dev.mysql.com/downloads/mysql/
选择对应的操作系统版本,下载安装即可。在安装过程中,MySQL会自动创建一个root
用户,并提示输入root
口令。
运行MySQL
MySQL安装后会自动在后台运行。为了验证MySQL安装是否正确,我们需要通过mysql这个命令行程序来连接MySQL服务器。
在命令提示符下输入mysql -u root -p,然后输入口令,如果一切正确,就会连接到MySQL服务器,同时提示符变为mysql>。
输入exit退出MySQL命令行。注意,MySQL服务器仍在后台运行。
主键
不要带有业务含义。
常见的可作为id字段的类型有:
自增整数类型:数据库会在插入数据时自动为每一条记录分配一个自增整数,这样我们就完全不用担心主键重复,也不用自己预先生成主键;
全局唯一GUID类型:GUID算法通过网卡MAC地址、时间戳和随机数保证任意计算机在任意时间生成的字符串都是不同的,大部分编程语言都内置了GUID算法,可以自己预算出主键。
注意:如果使用INT自增类型,那么当一张表的记录数超过2147483647(约21亿)时,会达到上限而出错。使用BIGINT自增类型则可以最多约922亿亿条记录。
联合主键:两个或更多的字段都设置为主键,对于联合主键,允许一列有重复,只要不是所有主键列都重复即可
外键
通过类似class_id的字段,可以把数据与另一张表关联起来,这种列称为外键。
由于外键约束会降低数据库的性能,大部分互联网应用程序为了追求速度,并不设置外键约束,而是仅靠应用程序自身来保证逻辑的正确性。
ALTER TABLE students
ADD CONSTRAINT fk_class_id
FOREIGN KEY (class_id)
REFERENCES classes (id);
其中,外键约束的名称fk_class_id可以任意,FOREIGN KEY (class_id)指定了class_id作为外键,REFERENCES classes (id)指定了这个外键将关联到classes表的id列(即classes表的主键)。
ALTER TABLE students
DROP FOREIGN KEY fk_class_id;
注意:删除外键约束并没有删除外键这一列。删除列是通过DROP COLUMN ...实现的。
索引
对某一列或多个列的值进行预排序的数据结构。
通过使用索引,可以让数据库系统不必扫描整个表,而是直接定位到符合条件的记录,这样就大大加快了查询速度。
ALTER TABLE students
ADD INDEX idx_score (score);
使用ADD INDEX idx_score (score)就创建了一个名称为idx_score,使用列score的索引。索引名称是任意的,索引如果有多列,可以在括号里依次写上,例如:
ALTER TABLE students
ADD INDEX idx_name_score (name, score);
特点:
索引的效率取决于索引列的值是否散列,散列程度越高,那么索引效率越高。
可以对一张表创建多个索引。索引的优点是提高了查询效率,缺点是在插入、更新和删除记录时,需要同时修改索引,因此,索引越多,插入、更新和删除记录的速度就越慢。
对于主键,关系数据库会自动对其创建主键索引。使用主键索引的效率是最高的,因为主键会保证绝对唯一。
唯一索引
通过创建唯一索引,可以保证某一列的值具有唯一性。
具有业务含义的列根据业务要求,刚好又具有唯一性约束,可以给该列添加一个唯一索引。
数据库索引对于用户和应用程序来说都是透明的。
ALTER TABLE students
ADD UNIQUE INDEX uni_name (name);
通过UNIQUE关键字我们就添加了一个唯一索引。
只对某一列添加一个唯一约束而不创建唯一索引:
ALTER TABLE students
ADD CONSTRAINT uni_name UNIQUE (name);
这种情况下,name列没有索引,但仍然具有唯一性保证。
查询数据
基本查询SELECT * FROM <表名>
条件查询SELECT * FROM <表名> WHERE <条件表达式> => SELECT * FROM students WHERE score >= 80;
<条件1> AND <条件2> => SELECT * FROM students WHERE score >= 80 AND gender = 'M';
<条件1> OR <条件2> => SELECT * FROM students WHERE score >= 80 OR gender = 'M';
NOT <条件> => SELECT * FROM students WHERE NOT class_id = 2;
NOT class_id = 2其实等价于class_id <> 2,因此,NOT查询不是很常用。
注意:使用<>判断不相等
使用LIKE判断相似——%表示任意字符,例如'ab%'将匹配'ab','abc','abcd'
投影查询
如果我们只希望返回某些列的数据,而不是所有列的数据,我们可以用SELECT 列1, 列2, 列3 FROM ...,让结果集仅包含指定列。 => SELECT id, score, name FROM students;
给每一列起个别名——SELECT 列1 别名1, 列2 别名2, 列3 别名3 FROM ... => SELECT id, score points, name FROM students;
投影查询同样可以接WHERE条件,实现复杂的查询
排序
加上ORDER BY子句
按照成绩从低到高进行排序:SELECT id, name, gender, score FROM students ORDER BY score;
按照成绩从高到底排序:SELECT id, name, gender, score FROM students ORDER BY score DESC;
如果score列有相同的数据,要进一步排序,可以继续添加列名。例如,使用ORDER BY score DESC, gender表示先按score列倒序,如果有相同分数的,再按gender列排序:
SELECT id, name, gender, score FROM students ORDER BY score DESC, gender;
分页查询
从结果集中“截取”出第M~N条记录。这个查询可以通过LIMIT <M> OFFSET <N>子句实现。
把结果集分页,每页3条记录。要获取第1页的记录,可以使用LIMIT 3 OFFSET 0:
SELECT id, name, gender, score
FROM students
ORDER BY score DESC
LIMIT 3 OFFSET 0;
LIMIT 3 OFFSET 0表示,对结果集从0号记录开始,最多取3条。注意SQL记录集的索引从0开始。
要查询第2页,那么我们只需要“跳过”头3条记录,也就是对结果集从3号记录开始查询,把OFFSET设定为3:
SELECT id, name, gender, score
FROM students
ORDER BY score DESC
LIMIT 3 OFFSET 3;
聚合查询
1、SELECT COUNT() FROM students;
COUNT()表示查询所有列的行数,要注意聚合的计算结果虽然是一个数字,但查询的结果仍然是一个二维表,只是这个二维表只有一行一列,并且列名是COUNT()。
2、给列名设置一个别名:SELECT COUNT() num FROM students;
COUNT(*)和COUNT(id)实际上是一样的效果。
3、聚合查询同样可以使用WHERE条件.
4、SUM 计算某一列的合计值,该列必须为数值类型
5、AVG 计算某一列的平均值,该列必须为数值类型
6、MAX 计算某一列的最大值
7、MIN 计算某一列的最小值
注意:1、MAX()和MIN()函数并不限于数值类型。如果是字符类型,MAX()和MIN()会返回排序最后和排序最前的字符。
2、如果聚合查询的WHERE条件没有匹配到任何行,COUNT()会返回0,而SUM()、AVG()、MAX()和MIN()会返回NULL:
多表查询
SELECT * FROM <表1> <表2>
这种一次查询两个表的数据,查询的结果也是一个二维表,它是students表和classes表的“乘积”,即students表的每一行与classes表的每一行都两两拼在一起返回。结果集的列数是students表和classes表的列数之和,行数是students表和classes表的行数之积。
这种多表查询又称笛卡尔查询,使用笛卡尔查询时要非常小心,由于结果集是目标表的行数乘积,对两个各自有100行记录的表进行笛卡尔查询将返回1万条记录,对两个各自有1万行记录的表进行笛卡尔查询将返回1亿条记录。
FROM <表名1> <别名1>, <表名2> <别名2>避免了结果集的列名重复问题
多表查询也是可以添加WHERE条件的
连接查询
连接查询是另一种类型的多表查询。连接查询对多个表进行JOIN运算,简单地说,就是先确定一个主表作为结果集,然后,把其他表的行有选择性地“连接”在主表结果集上。
存放班级名称的name列存储在classes表中,只有根据students表的class_id,找到classes表对应的行,再取出name列,就可以获得班级名称。这时,连接查询就派上了用场。我们先使用最常用的一种内连接——INNER JOIN来实现:
SELECT s.id, s.name, s.class_id, c.name class_name, s.gender, s.score
FROM students s
INNER JOIN classes c
ON s.class_id = c.id;
注意INNER JOIN查询的写法是:
先确定主表,仍然使用FROM <表1>的语法;
再确定需要连接的表,使用INNER JOIN <表2>的语法;
然后确定连接条件,使用ON <条件...>,这里的条件是s.class_id = c.id,表示students表的class_id列与classes表的id列相同的行需要连接;
可选:加上WHERE子句、ORDER BY等子句。
外连接:
SELECT s.id, s.name, s.class_id, c.name class_name, s.gender, s.score
FROM students s
RIGHT OUTER JOIN classes c
ON s.class_id = c.id;
RIGHT OUTER JOIN返回右表都存在的行。如果某一行仅在右表存在,那么结果集就会以NULL填充剩下的字段。
LEFT OUTER JOIN则返回左表都存在的行。如果我们给students表增加一行,并添加class_id=5,由于classes表并不存在id=5的行,所以,LEFT OUTER JOIN的结果会增加一行,对应的class_name是NULL:
-- 先增加一列class_id=5:
INSERT INTO students (class_id, name, gender, score) values (5, '新生', 'M', 88);
-- 使用LEFT OUTER JOIN
SELECT s.id, s.name, s.class_id, c.name class_name, s.gender, s.score
FROM students s
LEFT OUTER JOIN classes c
ON s.class_id = c.id;
FULL OUTER JOIN,它会把两张表的所有记录全部选择出来,并且,自动把对方不存在的列填充为NULL:
SELECT s.id, s.name, s.class_id, c.name class_name, s.gender, s.score
FROM students s
FULL OUTER JOIN classes c
ON s.class_id = c.id;
网友评论