先简单介绍下数据库

主要分 2 大类:

  • 关系型数据库

    MySQL、Oracle、SQL Server

  • 非关系型数据库

    Redis 内存数据库、MongoDB 文档数据库

SQL 语句支持三种注释:

#   注释
--  注释
/*  注释  */

SQL 语句不区分大小写,但是库名、表名、字段名、值这些区不区分具体看 DBMS 及其配置。

SQL 语句要以分号结尾,如果只有一句,可写可不写。

SQL 语言分为 4 个部分: DDL(定义)、DML(操作)、DQL(查询)、DCL(控制)

SQL 语句中可以用的一些小点:

\G 格式化输入

\s 查看服务器端信息

\c 结束命令输入操作

\q 退出当前的 sql 命令行模式

\h 查看帮助

以下语句都是基于 MySQL 数据库:

命令行中登录 MySQL :

mysql -h localhost -u root -p

-h 服务器的地址, 在本地上可以省略

-u 登录的用户名, root 是权限最高的一个用户

-p (小写) 回车后输入密码

-P (大写) 端口号

MySQL 中的数据类型

  • 字符串类型: 内容应该用 引号引起来。

    • 定长串 char

      每次都分配固定的长度用于存储数据。

      1~255 个字符长度,不指定就是 char(1)

    • 变长串 varchar

      每次根据实际内容,分配用于存储数据的空间。只存储必要内容。

      最大存储 255 个字节

    • MySQL 处理定长串 比 处理变长串,性能要好。

      在进行存储和检索式,MySQL 会保留 varchar 末尾的空格,删除 char 末尾的空格。

      在执行 update 时,行可能会比原来长,当超出一个页所能容纳的大小时,就要执行额外的操作: MyISAM 会将行拆分成不同的片段存储, InnoDB 是分裂页来使行放进页内。

    • 变长文本类型 Text

      最大长度 为 64K 的变长文本。

  • 数值类型

    • 整型

      tinyint : 8 位; 1 字节

      smallint : 16 位; 2 字节

      mediumint : 24 位;3 字节

      int : 32 位; 4 字节

      bigint : 64位; 8 字节

    • 浮点型

      float : 单精度浮点型

      double : 双精度浮点型

      decimal : 高精度小数类型 。

      以上三种都可以指定列宽,比如: decimal(5, 2) : 表示一共有 5 位数字, 小数部分占 2 位。

      CPU 原生支持浮点运算,但是不支持 decimal 类型的计算,decimal 计算比浮点类型需要更高的代价。

  • 日期

    • date , 表示日期:YYYY-MM-DD

    • time , 表示时间: HH:MM:SS

    • datetime: date 和 time 的结合。

      占 8 个字节, 保存 1000 年到 9999 年的日期和时间。

      与时区无关。

      标准的 ANSI 定义的日期和时间表示法。

    • timestamp: 和 datetime 一样,只不过表示范围要小

      和 UNIX 的时间戳差不多。

      与时区有关,保存从 1970-1-1(格林威治时间)以来的秒数。 占 4 个字节。保存 1970 年到 2038 年的时间。

      timestamp 比 datetime 空间效率要高

    • year: 4 位数字表示: 1901 ~~~ 2155 年。

  • 二进制数据类型

    可以存储 图像、视频、音频等等。

    了解就好,开发的时候,不会用到这种数据类型的。。。一般都是记录他们的存储路径。

字段的约束:

  • unsigned 无符号
  • 默认无符号 int 为 int(11) , 有符号的为 int(10)。
  • zerofill : 补齐长度,前面的用 0 填充, 比如: 0001
  • not null
  • default 设置默认值
  • primary key
  • auto_increment 自增属性,默认增长为 1
  • unique 唯一索引。 增加查询速度,但是插入和更新的速度会减低。
# 在当前用户下,查看所有的库:
show databases;

# 创建库
create database 库名 default charset=utf8

# 或者先判断 这个库名 存不存在
create database if not exists 库名 default charset=utf8;

# 使用某个数据库:
use 库名;

# 查看数据库里,所有的数据表:
show tables;

# 删除数据库 (慎用了。。。。)  在 磁盘 中进行删除。。。
drop database 库名;

进入到某个具体的数据库里面之后:

# 创建表
# create table 表名(.....) engine=innodb default charset=utf8';'
create table user(
	id int not null primary key auto_increment,
    name varchar(25) not null,
    age tinyint default 1,  -- 默认值为 1 
    sex enum('男','女')
)engine=innodb default charset=utf8;

# 或者 先判断这个表名存不存在。。。
create table if not exists user(
	id int not null primary key auto_increment,
    name varchar(25) not null,
    age tinyint default 1,  -- 默认值为 1 
    sex enum('男','女')
)engine=innodb default charset=utf8;

# 查看表结构: 
# desc 表名;

# 删除表:
# drop table 表名;


#     ========= 修改表的结构 ==========
# 添加列
# alter table 表名 add 列名 约束条件; 
alter table user add phone varchar(11) not null;

# 指定位置添加
alter table user add phone varchar(11) not null after age;
alter table user add phone varchar(11) not null first;

# 删除列
# alter table 表名 drop column 列名;
alter table user drop column phone;

# 修改列的信息:
# alter table 表名 change | modify 被修改的列信息
# modify 不能修改列名
alter table user modify age tinyint not null default 12;

# change 可以修改列名
alter table user change phone telphone varchar(11);

# 修改表名
# alter table 原表名 rename as 新表名;


增加

# 普通的插入。 插入全部数据的话,就可以省略中间那个括号, 也可以指定字段插入, 也可以添加一行。
insert into user (id, name, age, sex) values
(1, "如也", 18, "男"),
(2, "iris", 19, "女");

# 插入检索出来的数据
insert into mytable (col1, col2) values
select name, age from user;

# 将一个表的内容,插入到新表中
create table newtable as 
select * from user;

删除

# 删除指定数据(行)
delete from user where id = 1;

# 删除所有行,清空表
truncate table user;

更新

update user set col1=val1, col2=val2 where id = 1;

查询


select 字段列表 | * from 表名

[ where 搜索条件 ]

[ group by 分组字段 [ having 分组条件 ] ]

[ order by 排序字段 排序规则 ]

[ limit 分页参数 ]


# DISTINCT :  作用于所有列, 相同值只会出现一次
select distinct age, sex from user;


# LIMIT : 限制返回的行数。
# 第一个参数: 起始行,从 0 开始;
# 第二个参数: 返回的总行数。
select * from user limit 5;	    # 返回前 5 行
select * from user limit 0, 5;  # 返回前 5 行
select * from user limit 2, 3;  # 返回 3 ~~ 5 行


# ORDER BY 排序。  可以按照多个列进行排序
# ASC  升序, 默认的
# DESC 降序
select * from user order by age desc, id asc;

过滤操作: where

如果直接在数据库里查太多数据直接传给服务器的话,有时候会传输多余的数据,浪费网络带宽。

在 数据库 里先对 数据 做好处理就很有必要了。

可用于 where 子句的操作符:

  • 操作符 说明
    =
    <
    >
    != 或者 <>
    <= 或者 !>
    >= 或者 !<
    between 在 2 个值之间
    is null 是 null 值

    and 和 or 操作符, 用于连接多个过滤条件, 优先处理 and。

    in 操作符,用于匹配一组值, 也可以接一个 select 子句,用于匹配子查询得到的结果。

    not 操作符,用于否定一个条件。

使用 LIKE , 进行通配符匹配。

  • % : 匹配 >= 0 个任意字符。

  • _ : 匹配 1 个任意字符。

  • [] : 匹配 【】 集合内的字符。

    比如: [ab] 匹配字符 a, 或者字符 b。 可以用脱字符 ^ 表示否定。

通配符位于开头,匹配进行会非常慢。。。

# 不以 张 和 李 开头的任意文本
select * from user where name like '[^张李]%';   

计算字段(给列名取别名)

在数据库服务器上,完成数据的转换和格式化,要比在客户端上完成要轻松的多。

并且转换 和 格式化后的数量如果更少的话,也可以减少网络的通信量。

# AS 取别名
select col1 * col2 as alias from user;  # col1 * col2 的内容单独为 1 列, 列名为 alias

# CONCAT()  连接 2 个字段(列), TRIM()  可以去除首尾空格。
select concat(col1, col2) as concat_col from user;			# concat_col 的内容是 col1col2
select concat(col1, '(', col2, ')') as concat_col from user; # concat_col 的内容是 col1(col2)

分组: GROUP BY:

group by 列名1 —> 列名1 里相同的内容会被分到一组中。

分好组后, 还可以用 HAVING 对组的数据再进行过滤。

WHERE 过滤行, HAVING 过滤分组,行过滤优先于分组过滤。

除了汇总字段外, select 后的每一个字段,在 group by 后都要给出,不然不知道怎么显示啊。。。

# 统计 三年级及以上年级 的 每个班的人数,并且班级人数大于等于 15 人。
select classid, count(*) as num from mytable 
where gradeid >= 3 
group by classid having num >= 15;

函数

各个 DBMS 之间,函数是不相同的,这里只介绍 MySQL 中的函数。

聚合函数(汇总函数):

  • AVG( ) 返回某列的平均值, 会忽略 NULL 行。
  • COUNT( ) 返回某列的行数
  • MAX( ) 返回某列的最大值
  • MIN( ) 返回某列的最小值
  • SUM( ) 返回某列里面所有值的和

文本处理函数

  • LEFT( ) 左边的字符
  • RIGHT( ) 右边的字符
  • LOWER( )
  • UPPER( )
  • LTRIM( ) 去掉左边的空格
  • RTRIM( ) 去掉右边的空格
  • LENGTH( )
  • SOUNDEX( ) 将一个字符串 转换成,描述其语音表示的 字母数字模式。

日期和时间的处理函数:

​ 不经常用,一个比较常见的就是 NOW( ), 返回当前的日期和时间。

数值处理的函数:

  • SIN( )
  • COS( )
  • TAN( )
  • ABS( ) 绝对值
  • SQRT( ) 平方根
  • MOD( ) 取余
  • EXP( ) 指数
  • PI( ) 圆周率
  • RAND( ) 随机数

子查询

子查询里,只能返回一个字段的数据。 多的话是用在 in 操作符里的。

# 检索客户的订单数量
select cust_name, 
(select count(*) from orders where orders.cust_id = customers.cust_id) as orders_num 
from customers order by cust_name;

连接

用于连接多个表,使用 JOIN 关键字,条件语句用 ON, 而不用 WHERE。

可以替换子查询,比子查询的性能要好一些。。。

  • 内连接 (等值连接)
  • 自连接 (同表的内连接)
  • 自然连接 (同名列,通过等值测试,连接起来)
  • 外连接 (保留没有关联的那些行)
    • 左外连接 (保留 左表 没有关联的行)
    • 右外连接 (保留 右表 没有关联的行)
    • 全外连接 (都保留)
# 内连接,等值连接, 用 INNER JOIN 关键字。  用普通查询也可以做到。。。
select A.value, B.value from tablea as A inner join tableb as b 
on A.key = b.key;

select A.value, B.value from tablea as A, tableb as B where A.key = B.key;

# 自连接,可以看成是 内连接的一种,只不过连接的表是自身。  用子查询也可以做到。。。
# 查找与 Jim 处在同一个部门的所有员工的姓名。
select e1.name from employee as e1 inner join employee as e2 
on e1.department = e2.department and e2.name = "Jim"

select name from employee 
where department = (select department from employee where name = "Jim");

# 自然连接: 自动连接所有的同名列。 内连接是提供连接的列。
select A.value, B.value from tablea as A natural join tableb as B;


# 外连接
# 检索所有顾客的订单信息,包括还没有订单的顾客。  左外连接
select customers.cust_id, customer.cust_name, orders.order_id 
from customers left outer join orders 
on customers.cust_id = orders.cust_id;

组合查询

使用 UNION 关键字。

组合 每个查询得到的结果, 每个查询出来必须包含相同的列、表达式、聚合函数, 这样才有可能组合到一起。

默认会去除相同的行,如果不希望去除,使用 UNION ALL 关键字。

只能包含一个 ORDER BY 子句,并且要在最后出现。

select col from mytable where col = 1 
union
select col from mytable where col = 2;

视图

操作视图,就跟操作普通的表是一样的。

只不过,视图是虚拟的表,只使用了实际表的一部分数据。

本身不包含数据,不能对其进行索引操作。

可以通过给用户访问视图的权限,来保证数据的安全性。

create view myview as 
select concat(col1, col2) as concat_col,  col3*col4 as compute_col 
from mytable where col5 = val;

权限管理

MySQL 的账户信息保存在 mysql 这个数据库中的 user 表里。

# 创建账户
# grant 授权的操作 on 授权的库.授权的表 to 账户@'登录地址' identified by '密码';
grant select,insert on learn.* to zhangsan@'%' identified by '123456';

# 修改账户名
rename user 原账户名 to 新账户名;

# 删除用户
drop user 账户名;

# 查看权限
show grants for 账户名;

# 修改密码, 需要用 Password 函数进行加密。
set password for 账户名 = Password('新密码');

用 GRANT 关键字授予权限, 用 REVOKE 关键字删除权限。

权限可以作用到的层次:

  • 整个服务器。 使用 grant all , 或者 revoke all。
  • 整个数据库。 使用 ON database.*
  • 特定的表。 使用 ON 库名.表名
  • 特定的列。
  • 特定的存储过程。

事务管理

一些基本的专业术语:

  • 事务 transaction : 指一组 SQL 语句。

  • 回退 rollback : 指撤销指定的 SQL 语句的过程。

  • 提交 commit : 指将未存储的 SQL 语句结果写入到数据库表中。

  • 保留点 savepoint :

    指事务处理中设置的临时占位符(placeholder), 可以 rollback 到该保留点上。

    如果没有设置保留点,rollback 就会退回到 start transaction 语句处。

不能对 create 和 drop 语句 rollback; 不能对 select 语句进行 rollback ,这样也没有用。

MySQL 中的事务默认是隐士提交,也就是每一条 SQL 语句就是一个事务。

当出现 start transaction 语句时,会关闭隐士提交。

当 commit 或 rollback 语句执行后,事务就是自动关闭,恢复到原来的隐士提交。

设置 autocommit 为 0 可以取消自动提交,这个 autocommit 是对每个连接而言的,不是对服务器。

InnoDB 和 MyISAM 存储引擎的比较

DBMS 把对数据的存储和提取这些操作都封装到了 存储引擎 的模块里。 我们比较常见的就是 InnoDB 和 MyISAM 存储引擎。

  • InnoDB

    是 MySQL 默认的存储类型。 是支持事务的,支持外键。

    支持在线热备份,其他存储引擎都不支持的。

    支持行级锁。

    有 4 个标准的隔离级别。 默认级别是 可重复读(Repeatable read),在该级别下,通过多版本并发控制(MVCC) + Next-Key Locking 防止幻影读。

    在索引中保存了数据,避免了直接对磁盘操作,对查询性能比较友好。

    内部做了很多很多的优化。

    如果需要大量的 insert 和 update 操作时, InnoDB 会更友好。

  • MyISAM

    数据以紧密的格式存储。

    支持压缩表和空间数据索引

    只支持表级锁。 在读取时对表加共享锁,在写入时对表加排它锁。 在读的时候,也可以往里插数据,这个过程称为 并发插入(Concurrent insert)

    数据崩溃后损坏的概率是很高的,修复的过程也会非常慢。。。

    小型应用,有大量的 select 语句时,可以选择 MyISAM 存储引擎。

存储过程、游标、触发器: 之后有时间再来学习整理下。

参考连接: https://cyc2018.github.io/CS-Notes/#/notes/SQL