Mysql (1)

《数据类型》

大致分为三类:数值型(整型、小数)、字符型(短文本、长文本)、日期型

数值:

整型
类型 字节大小 容值范围 显示长度n最大值
tinyint 1 byte 有符号:(-128,127) —无符号:(0,255) tinyint(n): n<=3
smallint 2 byte 有符号:(-32 768,32 767)—无符号:(0,65535) smallint(n): n<=5
mediumint 3 byte 有符号:(-8 388 608,8 388 607)—无符号:很大 medium(n): n<=7
int、integer 4 byte 有符号:(-2 147 483 648,2 147 483 647)—无符号:很大 int(n): n<=10
bigint 8 byte 有符号:很大—无符号:很大 bigint(n): n<=19
  • 说明:
  1. 创建一个字段: id int; id默认使用的范围是 有符号的情况,
    如果要使用无符号的情况,则类型后面加限定: id int unsigned;
  2. 如果赋值的数据 超过 字段数据类型的 容值范围,会爆out of range 警告,而字段最后得到的数据也只能是 容值范围 最近接 赋值数据的 数值,例如 给无符号的类型 赋值一个负数,则字段最后得到的数值是0。
  3. int(n) 中的 n 代表的是 数据在表中的 最大 显示宽度 ,以 字符 为计算单位,有的人认为是以 字节 为计算单位。
  4. 如果不设置显示长度,则类型会使用默认的显示长度。如果赋值数据的宽度 超出 n值,则直接报错。
小数
浮点数类型 字节 范围
float(M,D) 4 很大
double(M,D) 8 更大
定点数类型 字节 范围
decimal(M,D) M+2 最大取值范围 和 double相同
  • 说明:
  1. M: 整数部分的长度 + 小数部分的长度
    D: 小数部分的长度
  2. M 和 D 都是可以省略的,如果是 定点 省略的话,则M默认为10,D默认为0。
    如果是 浮点 省略的话, M 和 D 则会根据插入的数值 浮动变化。
    比如 浮点类型的字段 被赋值3.14, 则浮点的M 是3,D是2。
    这也就是之所以叫 浮点 和 定点 的原因。
  3. 从第二条也就可以看出 浮点 的精度 是浮动的,不是固定的,如果需要 固定精度的字段,比如和钱相关的,推荐使用 decimal。

字符

类型 大小 用途
CHAR 0-255 bytes 定长字符串
VARCHAR 0-65535 bytes 变长字符串
TINYTEX 0-255 bytes 短文本字符串
TEXT 0-65 535 bytes 长文本数据
LONGTEXT 0-4 294 967 295 bytes 极大文本数据
TINYBLOB 0-255 bytes 不超过 255 个字符的二进制字符串
BLOB 0-65 535 bytes 二进制形式的长文本数据
MEDIUMBLOB 0-16 777 215 bytes 二进制形式的中等长度文本数据
  • 说明 定长 和 变长 的区别在于:
  1. 空间消耗上, 定长的 大于 变长的,
    这是因为赋值字段的 字符串的长度 没有达到显示宽度时,定长的 会占用空格来填充空间,以使得 字符长度 == 显示宽度。 变长的则不会这样做。
  2. 查找效率上, 定长的 大于 变长的。

日期:

类型 字节大小 范围 格式 用途
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- YYYYMMDD HHMMSS 时间戳
  • 说明:

timestamp 和 实际时区有关,而datetime 只反映当地时区:
假设: 字段t1 是timestamp; 字段t2是datetime

-- 将mysql的时区设为 东九区 (比东八区快一个小时)
set time_zone = '+9:00'

-- 然后给t1 和t2 字段 插入当前时间
insert into tab (t1, t2) values (now(), now());

此时 t1是 东九区的时间, 而 t2 是 东八区的时间。

其他类型

枚举类型
create table tab (
   status enum('a','b', 'c')
);

insert into tab values ('a');
insert into tab values ('B');
insert into tab values ('f');--报错
insert into tab values ('a,c');--报错

表tab:

status
a
b
集合类型
create table tab (
   habit set('a', 'b', 'c')
);

insert into tab values ('a,B');
insert into tab values ('b,d');--报错
insert into tab values ('f'); --报错

表tab:

habit
a,b



《MySQL 数据库》

链接数据库

mysql  -uroot  -p123456
// 登录远程数据库
mysql -h125.21.74.1 -P3333  -uroot  -p123456

show databases;
use 数据库名字
create database 数据库名字;
CREATE DATABASE `database` CHARACTER SET utf8 COLLATE utf8_general_ci;
drop database 数据库名字;

备份数据库

本地数据库操作
1.导出数据为sql文件

无需进入MySQL,通过mysqldump命令:

  1. 导出整个数据库(创建数据表语句 + 表中的数据)
mysqldump -u root -p dbname > dbname.sql
  1. 导出数据库中指定的数据表(创建数据表语句 + 表中的数据)
mysqldump -u root -p  dbname  tablename > tablename.sql
  1. 导出数据库结构(创建数据表语句)
mysqldump -u root -p    -d    dbname > dbname.sql
2.导入sql文件中的数据到数据库中
  • 先进入MySQL选择一个数据库: use database_aaa
  • 通过source命令导入指定sql文件:source /home/hero/桌面/home.sql

扩展: 进入 mysql 命令行界面, 使用 system + linux 命令, 就可以执行linux的系统命令

远程数据库操作

远程数据库 要开启允许远程操作的权限

1.导出数据为sql文件
mysqldump -h 123.12.1.123 -u root -p dbname > dbname.sql

用户名和密码均是数据库的,不是服务器的

2.导入sql文件中的数据到数据库中
  • 先登录远程数据库: mysql -h123.12.1.123 -uroot -p123456
  • 再进入MySQL选择一个数据库: use database_aaa
  • 最后通过source命令导入指定de本地sql文件:source /home/hero/桌面/home.sql
3. Mysql8.x 的 新版mysqldump

使用 新版的mysqldump 执行 mysqldump -h 123.12.1.123 -u root -p dbname > dbname.sql
会出现一下报错:
mysqldump: Couldn’t execute ‘SELECT COLUMN_NAME, JSON_EXTRACT(HISTOGRAM, ‘$.“number-of-buckets-specified”’) FROM information_schema.COLUMN_STATISTICS WHERE SCHEMA_NAME = ‘wx_service’ AND TABLE_NAME = ‘add_user’;’: Unknown table ‘column_statistics’ in information_schema (1109)
这是因为: 新版的mysqldump默认启用了一个新标志,通过–column-statistics=0来禁用此标志。
新版的mysqldump 可以如下操作:

mysqldump -uroot -p'密码' -h 192.168.3.82 --column-statistics=0 -B rxw2 --opt > rxw2.sql



《MySQL 数据表》

MySQL数据库服务配置好后,系统会有以下默认的数据库:

  • information_schema:虚拟对象,其对象都保存在内存中
  • performance_schema:服务器性能指标库
  • mysql:记录用户权限,帮助,日志等信息

常用命令

show tables;   --查看数据库中的 全部 表
describe tableName;   --(简写: desc tableName;)查看表中的 全部 字段
SHOW TABLE STATUS     --查看数据表类型
SHOW TABLE STATUS LIKE 'table_name'\G     --查看指定数据表的详细信息 ( \G  竖着查看字段)

CREATE TABLE `table_name`(
      `id` INT(11) UNSIGNED AUTO_INCREMENT COMMENT '用户id',
       `uid` INT(11) UNSIGNED AUTO_INCREMENT COMMENT '用户id',
      `name` VARCHAR(40) NOT NULL,
      `del_time` INT(11),
      `create_time` timestamp NULL DEFAULT CURRENT_TIMESTAMP COMMENT '若值为空则采用当前时间',
        PRIMARY KEY ( `id` ) USEING BTREE, -- 主键索引
        KEY `idx_table_name_uid` (`uid`) USING BTREE COMMENT '用户id',  --普通搜因
        UNIQUE KEY `idx_table_name_name` (`name`) --唯一索引
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 AUTO_INCREMENT=10000 COMMENT='desc info';

DROP TABLE table_name ;

// 如果表存在,则不创建 的创建表法
create table if not exists table_name (......);

// 如果表存在,则替换掉 的创建表法
DROP TABLE IF EXISTS `table_name`;
CREATE TABLE `table_name` (......);

说明:

  1. DEFAULT CHARSET 设置编码
  2. 关键字不区分大小写,建议大写,便于区分
  3. 强调: 创建表时: 表名 和 字段名 都是用 反引号 包裹的,不是 单引号!!!
    当然 表名 和 字段名 也可以不用任何符号包裹,直接裸漏就可以了。
  4. ENGINE 设置 表 的 存储引擎,每个表都可以有自己的存储引擎。
  • 存储引擎 说白了就是 表类型, 表类型 的选择 是 根据 表的用途 决定的, 有的存储引擎 在 存储数据方面做的很好,有的存储引擎 在 数据查询方法做的很好等等, 所以 存储引擎 的选择 因地制宜吧。
  • 如果不选择则默认使用 InnoDB存储引擎。
  1. KEY 关键字用于 创建索引
  2. 注意:最外层括号中的 最后的建表语句(KEY `index_table_name_uid` (`uid`) USING BTREE COMMENT ‘用户id’)之后一定 不要写 逗号,否则报错。
  3. 除了数字类型 单独写一个 default 就可以 让默认值生效,其他类型的default 默认值 只有 在not null 后面 或者 null default 默认值 的情况下才生效,
  4. AUTO_INCREMENT=10000 让主键id不再 从1开始,而是从 10000开始 自增。这个字段对于 大数据表 分解 小表 时特别重要,他能保证 所有小表中的 主键 是 连续起来的。

复制一张表

假设现在有表abc,要完全复制一个新的表 aaa:

-- step1: 复制 表的 字段结构
SHOW CREATE TABLE abc;  -- 查看创建abc表时 的SQL语句
CREATE TABLE aaa (根据查询的SQL语句 创建 表aaa)

-- step2: 复制 表的 具体数据
insert into aaa(id, name,old) select id,name,old from abc;


《建表约束》

1. 主键约束: PRIMARY KEY

在表中唯一确定一条记录,该字段 不重复 不为空。

CREATE TABLE user(id INT PRIMARY KEY );

2. 自增约束: AUTO_INCREMENT

插入数据的时候可以不写自增约束的字段值

3. 唯一约束: UNIQUE

约束字段的字段值不能重复

4. 非空约束: NOT NULL

约束字段的字段值不能为空

5. 默认约束: DEFAULT

约束字段的字段值未传值便使用默认值。

6. 外键约束: foreign key (本表外键字段) references 外表(外表字段)

create table AAA(
   id int primary key,
   name varchar(10),
   BBB_id int,
   foreign key(BBB_id) references BBB(id)
)

说明:

  • 被引用的 表 为 主表, 引用别人的表 为 副表。
  • 表AAA的BBB_id字段 指向外表 BBB 的id字段,称AAA表为副表,BBB表为主表。
  • 主表 中没有的数值,在副表中 是不可以被使用的。
  • 主表中的被引用的 记录, 在主表中 是不可以 被 删除的。 除非先删除 副表 中的引用。
  • 添加数据: 先主后副; 删除数据: 先副后主
  • 嵌套语句查询外键:
    select * from AAA where BBB_id=(select id from BBB where name=“tom”);
-- 对已有表加外键
alter table 表1-表名
add constraint 外键名称(一般外键名称为”fK_”开头) 
foreign key (要设为外键的列名)
references 表2-表名(与哪个表有关联) (表2中该列列名);

-- 栗子
alter table  wtt.aaa
add constraint  fk_pid 
foreign key (pid)
references  wtt.bbb(id);

添加外键失败说明:

  • 添加的外键列与另一个表的唯一索引列(一般是主键)的数据类型不同
  • 要添加外键的 表 与另一个 表 的存储引擎是不是都为innodb引擎
  • 设置的外键 表中的已有记录 与另一个表中的唯一索引列(一般是主键)中的值不匹配
    解决办法:删除要成为外键的列,再次创建并默认为NULL

列级约束 和 表级约束

create table AAA(
   id int 列级约束,
   表级约束
)
  • 说明:
  • 上面 六大 约束 都可以作为 列级约束,但是注意, 外键约束 放在列级约束的位置 没有效果。
  • 除了 非空、默认, 都可以作为 表级约束。

主键 和 唯一 两个约束的区别

约束 保证字段值唯一性 是否允许为空 一个表中可以有多少个 是否允许组合索引
主键 yes no 1 yse,但不推荐
主键 yes yes,但最多只允许有一个空的 多个 yse,但不推荐



建表结构

关系型数据库中的表与表不是彼此独立,而是相互关联的;
表与表之间的级联关系使得整个数据库成为一个有机关联的系统;
表关系可以分为一对一、一对多、多对多三种,它们的维护方式各不相同;
表关系的管理是关系型数据库的重要组成部分,要牢牢掌握;

一对一

如果A表记录与B表记录有双向的一 一对应关系,我们就称它们之间有一对一的关系;
一对一关系的维护,由A、B中相对次要的一方来维护(这里假设是B),维护的方式是在B中插入一个指向A表主键的外键;
在一对一关系中,本来外键放在任意一方都是可行的,之所以要选择相对次要的一方,是因为万一不得以必须删除一个表时,我们会选择删除相对次要的表,此时由他所维护的关联关系也被一并删除,不会形成脏数据;

一对多

如果A表中的一条数据对应B表中的多条数据,而B表中的每条数据都对应一条唯一的A表数据,就称A表和B表是一对多的关系;
一对多关系的维护,由多方进行维护,维护方式是多方在表中添加指向一方的外键;

多对多

如果A表中的一条记录对应B表中的多条记录,B表中的一条记录也对应A表中的多条记录,就称A表和B表是多对多的关系;
多对多关系的维护,要通过建立中间表来维护.



《MySQL 记录》

-- 加入数据 (下面有 加入数据的 补充说明,要仔细看一下)
INSERT INTO table_name ( field1, field2,...fieldN ) VALUES ( value1, value2,...valueN );

-- 查找数据 (condition 中的判断符号有: =  <>  >   <   <=  >=)
SELECT field1, field2,...fieldN FROM table_name1, table_name2... WHERE condition1 [AND [OR]] condition2..

-- 更新数据 
UPDATE table_name SET field1=new-value1, field2=new-value2 [ WHERE condition ]

-- 删除数据 (如果没有指定 WHERE 子句,将会删除表中的所有记录。)
DELETE FROM table_name [ WHERE ...]

-- 清空数据 (truncate会删除表中所有记录,并且将 重新设置 ==高水线== 和== 所有的索引==,有外键的表 不可以 用该方法 清空数据。该方法 不会记录日志,执行速度很快)
truncate table_name;

说明:

  • 每个SQL语句操作都需要启用一个IO,IO是很昂贵的资源,为了性能,尽可能可优化SQL的句数,如下:
##############################
#  1、导入多个数据是,要使用一个IO
##############################
-- 使用3个IO
INSERT INTO aaa ( name, age ) VALUES ( 'tom', 11 );
INSERT INTO aaa ( name, age ) VALUES ( 'cat', 6 );
INSERT INTO aaa ( name, age ) VALUES ( 'miao', 8 );

--以上插入数据 开启了 三个 io,下面优化为 一个 io
INSERT INTO aaa ( name, age ) VALUES ( 'tom', 11 ), ( 'cat', 6 ), ( 'miao', 8 );


##############################
#  2、导入 另一个表中的 数据
##############################
--  向 aaa表中 插入 bbb 表中的数据, 不再需要 values 关键字:
insert into aaa (a,b,c) (select a,b,c from bbb;)
-- 若插入aaa表中的数据字段,是aaa表中的  ==全部字段==, 则 aaa表之后的字段 可以 省略不写
insert into aaa (select a, b from bbb where a = 18)



ALTER 修改

需要修改 数据表名 或者 字段 时,就要用到ALTER命令。

-- 修改 数据表 的名字
ALTER TABLE table_name RENAME TO new_table_name;


-- 增加一个name字段(默认插在最后
ALTER TABLE table_name ADD name VARCHAR(10);

-- 增加 多个 字段
ALTER TABLE orders                                                                                                                  
           ADD send_at int(10) default 0 COMMENT '发货时间',                                                                                          
           ADD fin_at int(10) default 0 COMMENT '确认收货(完成)时间',                                                                                 
           ADD refuse_at int(10) default 0 COMMENT '拒绝退货申请',                                                                                    
           ADD refuse_reason varchar(100) default '' COMMENT '拒绝退货申请',                                                                          
           ADD agree_at int(10) default 0 COMMENT '同意退货申请';  
  
  
-- 删除 name 字段(表中name字段的数据也会一同被删除)
ALTER TABLE table_name DROP name ;

--  删除 多个 字段
ALTER TABLE orders                                                                                                                  
           DROP send_at,                                                                         
           DROP agree_at;  


-- 将字段name的 字段名改为 caller, 类型改为 varchar(10)
ALTER TABLE table_name CHANGE name caller varchar(10);

说明:

  • 增加一个字段,默认摆放的位置是最后,可以使用 FIRST or AFTER+已有字段名 来手动控制 新加字段的摆放位置:ALTER TABLE table_name ADD name VARCHAR(10) AFTER id; ==》name字段 在 id字段 之后且紧挨着。
  • 增加一个字段,其数据默认是 NULL, 若是不想为NULL 可以在添加的 时候 通过 DEFAULT 设置一个 默认值: ALTER TABLE table_name ADD name VARCHAR(10) DEFAULT ’小明’;
  • 如果 数据表 中 只剩余一个字段 则无法使用 DROP 来删除字段。



《MySQL 操作符》

常用关键字

1. 起别名

字段 和 表名 都可以起别名,方式有两种:

  • 显性: 通过 as 关键字
SELECT name as 别名 FROM table_name;
  • 隐形: 通过 空格
SELECT name 别名 FROM table_name;

注意: 如果一个别名中有空格,则外层必须用单引号包裹: ‘see you’

  1. 起别名的效果展示:
select id,  name from aaa;

返回结果:

id name
1 tom
select id aaa,  name bbb from aaa;

返回结果:

aaa bbb
1 tom

2.给 表名 起别名:

select * from aaa 别名a inner join bbb 别名b on 别名a.id = 别名b.id;

注意: 如果为 表名 起了 别名,那么 凡是用到 表名的地方 都要使用 别名。

2. LIKE 模糊筛选

SELECT * FROM table_name WHERE name LIKE '王%';

说明:

  1. 只要开头带王的,都可以被搜出来。
  2. 百分号 %字符来表示任意字符,没有百分号 %, LIKE 子句 和 等号 = 就是一样的了。

3. UNION

SELECT name FROM t1 
UNION [ALL]
SELECT name FROM t2;

说明:

  1. UNION 操作符用于 查询的字段相同的 多表进行联合,一般而言被联合的表,没有什么关系。
    而 join 联合的多表 一般 是有 外键的指向关系的
  2. 默认情况下 UNION 操作符会删除了重复数据, 如果查询的多个字段,除非多个字段的值 都一致,才会去重。
  3. 如果不想 去除重复的数据 UNION 后面加 ALL 操作符。

demo:查询两个表的 fid 字段,要求合在一起的数据要按照 各自的时间信息 排序,且fid不能重复。

SELECT fid FROM (
	SELECT fid, addtime FROM aaa 
	WHERE id = 754238 
	AND fid NOT IN (SELECT fid FROM bbb  WHERE id = 754238) -- 这一句是为了fid去重
	UNION
	SELECT fid, addtime FROM bbb  WHERE id = 754238
) tmp ORDER BY tmp.addtime

4. ORDER BY 排序

SELECT * FROM table_name ORDER BY name DESC;

说明:

  1. ORDER BY 默认是按照 ASCII表 升序排列,这里加了 DESC 就变成降序了。
  2. ASC: 升序; DESC: 降序。
  3. order by与limit的写顺序是:先写order by,再写limit
-- 案例:按照名字的字节长度 排序
SELECT * FROM table_name ORDER BY length(name);

-- 排序: 先按照id升序,如果记录的id值有一样的 就让再按照 age 降序 排列:
select * from a order by id asc, age desc;

5. JOIN 多表联合(多个表联合为一个表)

AAA表:

id name sex
1 tom man
2 cat woman
3 miao man
BBB表:
caller gender
------ ------
aaa man
bbb unknow
1. 内连接(inner join): 联合表的内容 = 左右两表公共的部分。
SELECT * FROM AAA JOIN BBB ON AAA.gender = BBB.sex;

-- 内连接的 简写,(当出现多层内连接嵌套是,这种简写就显得很好用了:)
select * from AAA,BBB where AAA.gender = BBB.sex;

联合表:内连接

id name sex caller gender
1 tom man aaa man
3 miao man aaa man
2. 左连接(left join): 联合表的内容 = 左右两表公共的部分 + 左表特有的部分。
SELECT * FROM (AAA left join BBB ON gender = sex);

联合表:左连接

id name sex caller gender
1 tom man aaa man
3 miao man aaa man
2 cat woman NULL NULL
3. 右连接(right join): 联合表的内容 = 左右两表公共的部分 + 右表特有的部分。
SELECT * FROM (AAA right join BBB ON gender = sex);

联合表:右连接

id name sex caller gender
1 tom man aaa man
3 miao man aaa man
NULL NULL NULL bbb unknow
4.多连接
SELECT * 
FROM AAA 
JOIN BBB 
ON AAA.gender = BBB.sex
JOIN  CCC  
ON  AAA.id=CCC.pid
WHERE AAA.id = 1
LIMET 10

6. NULL 值处理

表aaa:

id name
1 NULL
2 tom
SELECT * FROM aaa WHERE name = NULL;  --筛选结果为空
SELECT * FROM aaa WHERE name IS NULL; --筛选结果 正常

SELECT * FROM aaa WHERE name <> NULL; --筛选结果为空
SELECT * FROM aaa WHERE name IS NOT NULL; --筛选结果 正常

7. REGEXP 正则

 SELECT * FROM table_name WHERE name REGEXP '^王';

说明: 只要开头带王的,都可以被搜出来。

8. limit 分页查询

-- 查出3条数据 便可以停止查询
select * from aaa where id=1 limit 3;

-- 从索引为10的记录(起始索引为0)开始找,查出2条数据便可以停止查询
select * from student LIMIT 10, 2; 

9. between、not 、and 、in、or范围查询

SELECT * FROM score WHERE age BETWEEN 60 AND 80;  --包边的
SELECT * FROM score WHERE age < 81 AND age > 59 ; --不包边的

SELECT * FROM score WHERE age NOT BETWEEN 60 AND 80;  

SELECT * FROM score WHERE age IN(85, 86, 88);
SELECT * FROM score WHERE age  NOT IN(85, 86, 88);

SELECT * FROM student WHERE name = 'tom' OR age > 10;
SELECT * FROM student WHERE name = 'tom' AND age > 10;

10. group by 分组查询

常常搭配着 统计函数 使用,其语法形式如下:

select 统计函数, 分组列(出现在group by 后面)
from 表
[where 条件]
group by 分组的列
[order by ...]
  • 分组前筛选: 查询 每个部门中,工资最高的
select max(money), depart from employee group by depart;
  • 分组后筛选: 查询 每个部门中,工资最高的, 在这些最高的之中 哪些大于 10000 的
select max(money), depart from employee group by depart 
having max(money) > 10000;
  • 分组的条件 不仅仅是根据 某一个 字段, 还可以是 某一个 表达式、函数
    按员工姓名 的长度 分组,查询每一组员工的个数,并通过 分组后筛选 筛选出 有哪些分组的员工个数大于5的。
select count(*), length(name) from employee 
group by length(name)
having count(*) > 5;

-- 给以上 函数加上 别名, 在写一遍:
select count(*) num, length(name) len from employee
group by len
having num > 5;
  • 分组的条件 不仅仅可以是 某一个 , 还可以是 某几个 :
    按照 衣服颜色 和 成绩 对学生进行分组(只要是衣服颜色 和 成绩 相同的学生 就是一组), 并 查出 每组学生的 平均身高:
select avg(tall), cloth, grade from shool 
group by cloth, grade;
  • 分组 排序:
    查询每个部门的平均工资,筛选出其中 大于一万的,然后按照 平均工资 给每个部门排序:
select avg(money), depart from employee 
group by depart
having avg(money) > 10000
order by avg(money) DESC;
/1/. group by 去重功能(多数情况下比distinct都好用)
场景需求:

现有求职人员如下三人, 工厂招工的原则就是工作人员不能出现重名,至于性别不作要求,
但是要确切的知道 入职人员的id和sex,
请根据下面的求职人员表,给出符合进场要求的结果:

求职人员表:

id name sex
1 tom man
2 tom woman
3 cat man

分析: 这里考察的就是去重, 如果用distinct 去重 name 字段,

select id, distinct name, sex from user;

会报错的,因为 两个tom的id和sex不一样,mysql 不知道该显示谁的。

如果用 分组机制去重

select * from user group by name;

也会报错,但是报错的原因和distinct不一样,报错信息如下:
which is not functionally dependent on columns in GROUP BY clause;...sql_mode=only_full_group_by
错误原因分析:

数据库为 only_full_group_by 模式, 而only_full_group_by的语义就是确定 select 之后获取的 信息值 都是明确语义,
简单的说来,在此模式下,信息值要么是来自于聚合函数(sum、avg、max等)的结果,要么是来自于group by list中的表达式的值
换句话说,信息值 不能出现多个情况都可以, 让 mysql 自己选择的情况,例如 name分组下, tom的分组的 sex字段 显示man和woman 都可以,但是mysql无法做主。
MySQL5.7之后,sql_mode中ONLY_FULL_GROUP_BY模式默认设置为打开状态。

解决方法:

MySQL提供了 any_value()函数 来抑制 only_full_group_by值被拒绝,
any_value()会选择被分到 同一组的数据里 第一条 数据的指定列值作为返回数据
注意: any_value 不要妄想和 distinct 配合使用, distinct没有组的概念,又来何来同一组的数据里 第一条

select any_value(id) id , any_value(sex) sex, name from user group by name;

这样就可以了, 就能得出 符合入厂 的人员列表了。

/2/. 时间戳 分组 (统计中常用)
SELECT FROM_UNIXTIME(create_time,'%Y-%m-%d') as date, COUNT(*) 
FROM user 
WHERE del_time <> 0
GROUP BY FROM_UNIXTIME(create_time ,'%Y-%m-%d');

11. distinct 去重

select distinct name from aaa;

注意:
SELECT DISTINCT name, sex FROM aaa;
这种写法会报错,因为会出现 名字相同 性别却不同的情况, 数据库去重 却不知道该怎么舍去。
所以 去重 最好是 针对 单个字段 搜索的情况。


常用的sql操作:

1. MySQL之空间函数:经纬度距离排序

  • 1 st_distance
    利用mysql自带函数计算结果单位是度,需要乘111195(地球半径6371000*PI/180)是将值转化为米,再除以1000 将米 转化为 公里。
SELECT *, (st_distance(point(lng,lat),point(118.3365,35.0569))*111195/1000 ) as '距离' 
FROM aaa 
having '距离' < 10 ORDER BY '距离' ASC;

距离单位 公里

  • 2 ST_Distance_Sphere (推荐,精确度 更好)
    用来计算两个经纬度之间的球体距离,算出的单位是
    若 除以 1000 便是公里。
SELECT ST_Distance_Sphere(POINT(116.4025249,39.9251859),POINT(116.4025249,39.9250644)) AS distant;

# 距离验证 ,单位 公里
SELECT (ST_Distance_Sphere(point(117.32,37.64),point(119.43, 49.32))/1000) AS distant;

2. select 后的 多条件判断

如果一个雇员的id是奇数并且他的名字不是以’M’开头,那么他的奖金是10,否则奖金为0。

select 
employee_id,
case 
    when mod(employee_id,2)=1 and LEFT(name,1)!='M' then 10
    else 0 
end bonus
from Employees
order by employee_id

3. update

多值判断 更新

sex 字段中,将 man 改为 男, 将 woman 改为 女。

update Aaa 
set sex=
case sex 
    when 'man' then '男'
    when 'woman' then '女'
    else '未知'
end
where sex <> '';

多表 指定列 更新

通过where条件,拼接两个表, 然后 将一个表中的 数据列 对应的复制到 另一个 表中 指定的列。

  • 方式一: 等同于 inner join
UPDATE user_active aaa, users bbb 
set aaa.name = bbb.name 
where aaa.uid = bbb.id 
  • 方式二:(推荐)
UPDATE users_offer a
LEFT JOIN business_offer b
ON a.id =b.users_offer_id
SET a.del_at=123, b.del_at=321,a.name=b.caller
WHERE a.id <> 0;

4. delete 单表复值

删除表中 重名的记录,将 年龄 最小的 保留下来

+----+--------+
|age | names  |
+----+--------+
| 1  | tom    |
| 2  | tom    |
| 3  | tom    |
+----+--------+
-- 自己 拼接 自己
DELETE aaa FROM user aaa, user bbb
WHERE aaa.names = bbb.names AND aaa.age > bbb.age;

5. MySQL数据去重 且 保留取最新(或 最旧)的记录

# aaa 表
id|age|name|
--+---+----+
 1|  1|tom |
 2|  1|tom2|
 3|  2|cat1|
 4|  2|cat2|

去重字段 是 age, 去除 age相同的 记录。
这里数据的新旧通过id字段体现,id 越大 记录 越新。

-- 获取 最旧 的数据
SELECT t1.*
from aaa t1
	left join aaa t2 
	on t1.age = t2.age and t1.id > t2.id -- 通过调控这个条件 控制 最新、最旧
WHERE t2.id IS NULL 
AND t1.age <> 0
ORDER by t1.id
LIMIT 0,10;

-- 结果如下:
id|age|name|
--+---+----+
 1|  1|tom |
 3|  2|cat1|

-- 获取 最新 的数据
SELECT t1.*
from aaa t1
	left join aaa t2 
	on t1.age = t2.age and t1.id < t2.id
WHERE t2.id IS NULL
AND t1.age <> 0
ORDER by t1.id
LIMIT 0,10;

6. if 多条件 可选条件 的sql 语句

要求 搜索 临沂市的 人, 如 前端 传过来 age字段,则 搜索 age 大于18 的人,如果 前端 传过来 sex字段,则 搜索 sex=1 的人。
其中 搜索的数据 在 user表中, id字段为主键(不可为0)

select * from user 
where city = '临沂市' 
and if(age=0, id<>0, age > 18) 
and if(sex=0, id<>0, sex = 1);   // 以 id<>0 这个 无作用条件 为 托词,不行 就抛出去。

7. 洋葱模型

洋葱模型 的 概念 来自于 洋葱的横截面图,描述的是一种 顺序。
对于 sql 语句的 编译、执行,正是 采用了 洋葱模型。

  • 编译
    sql语句 先 从外往里 进行 整体的编译,目的是为了 确定一下 涉及到的 表 和 相关字段。
  • 执行
    sql语句 的 执行 是 从里往外 进行的。
  • 单复字段 的经典例子

要求 一次性 查询到 某个 用户的 好友列表 的 好友名称、id 以及 该用户 的好友 拥有的 好友数。
其中 好友名称好友的id 都是 单字段, 而 好友拥有的好友数复字段

select fid, name, 
(select count(*) from user_friend as aaa where aaa.uid = bbb.fid) num
from user_friends as bbb
where bbb.uid = 1;

例子简述:
bbb 是外部的 sql语句, aaa是内部的sql语句,因为 编译的时候 已经知道 bbb代表的含义了,
所以 在 内部sql语句中 可以 直接 执行使用 bbb。
如果 编译 和 执行 都是 从里到外 的话, bbb 代表的含义 内部sql 执行的时候 是不知道的。



《MySQL 索引》

  • 当有人问到索引是什么时,大家都喜欢用“书的目录”来做类比,没有索引的数据库,就像是没有目录的书,想找第3章的第7小节,就要一页一页翻过去,最可怕的是翻到了还要把这本书翻完才算完;那反之有索引的数据库,就是有目录的书了,直接按目录找到就可以了。

  • 在数据库中 索引 说白了也是一张表, 是程序通过算法内演的一张虚表,该表保存了主键与索引字段,并指向实体表的记录。

  • 创建索引时,你需要确保: 1、表的数据量很大; 2、该索引是应用在 SQL 查询语句的条件(一般作为 WHERE 子句的条件)。

  • 好处: 索引大大提高了查询速度。

  • 坏处: 降低更新表的速度,因为更新表时,MySQL不仅要保存数据,还要保存一下索引文件。


  • 单列索引:即一个索引只包含单个列,一个表可以有多个单列索引。
  • 组合索引:即一个索引包含多个列。
    组合索引利用好了, 比创建多个 单列索引 的效果要好。
    因为 一个 组合索引 虽然 给 多个 列 做了索引规划, 但是终究是一个索引,占用的资源就是一个索引的资源。 而 对于 创建多个 单列索引 ,占用的 资源是 多个 索引的资源。

常见索引的类型:

  1. 普通索引

最基本的索引类型,允许在定义索引的列中插入 相同的值空值

  1. 唯一索引

定义索引的列 不允许出现 相同的值,但是允许有 空值

  1. 主键索引

主键会自动创建 主键索引,主键列中的每个值是 非空唯一的
联合主键 也是 主键索引 的一种。

  1. 全文索引

主要针对 多文本字段, 具体的就是 进行 全文检索,即 分词 操作 得到一个 倒排索引表。
但是 mysql的 分词 对中文很不友好, 对英文 还可以。
所以 建议 用到 全文索引 的场景 还是 使用 ESearch, 专业的事 就交给 专业的人 来做。

  1. 组合索引

即一个索引包含多个列。


新建表 中 + 索引

通过 INDEXKEY 选项来添加索引。这两种关键字是等效的,都可以用来创建索引

-- 普通索引
create table `user`(
    id int(10),
    index user_id(id)  -- user_id 是 索引名
)
-- 指定索引 数据结构 
create table `user`(
    id int(10),
    index user_id(id) USING BTREE
)

-- 唯一索引
create table `user`(
    id int(10),
    unique index user_id(id)
)

-- 全文索引
create table `user`(
    id int(10),
    fulltext index user_id(id)
)

-- 多列索引
create table `user`(
    id int(10),
    name varchar(5),
    key user_id_name(id, name)
)

-- 主键索引
create table `user`(
    id int(10),
    primary key (`id`)
)

-- 外键索引
create table `user`(
    id int(10),
    goods_id int(10),
    primary key (`id`),
    -- FOREIGN KEY (`goods_id`) REFERENCES `goods` (`id`)  // 自动命令 外键索引
    CONSTRAINT `外键名称` FOREIGN KEY (`goods_id`) REFERENCES `goods` (`id`) // 手动命名 外键索引
)

已建表 中 + 索引

-- 普通索引
create index user_id on user(id); -- user_id 是 索引名
-- 指定索引 数据结构 
create index user_id on user(id) USING BTREE;

-- 唯一索引
create unique index index_name on table_name(fields);

-- 全文索引
create fulltext index index_name on table_name(fields);

-- 多列索引
create index index_fields1_fields2 on table_name(fields1, fields2)

-- 主键索引
ALTER TABLE user ADD PRIMARY KEY (id);

基本用法:

-- 查看索引(\G表示将查询到的横向表格纵向输出,方便阅读)
SHOW INDEX FROM table_name\G

-- 此时 查询 有索引的 列 就会 走索引 查询
SELECT 字段名b FROM 表名A;
-- 索引优先,这种情况 也会 走 索引
select * from 表名A where 字段名b=1;

-- 修改索引的名字
ALTER INDEX 索引名 RENAME TO 新索引名;

-- 删除索引
DELETE INDEX 索引名;



《MySQL 变量》

在MySQL中,变量一般可分为分为三种类型: 系统变量、用户定义变量、局部变量;

系统变量

系统变量是 MySQL服务器 系统自身 提供的,分为 全局变量(GLOBAL)会话变量(SESSION)

查看
SHOW [ SESSION | GLOBAL ] VARIABLES;  -- 查看所有系统变量

SHOW [ SESSION | GLOBAL ] VARIABLES LIKE '......'; -- 可以通过LIKE模糊匹配方 式查找变量

SELECT @@[SESSION | GLOBAL] 系统变量名; -- 查看指定变量的值
设置

如果没有指定SESSION/GLOBAL,默认是SESSION,会话变量。

SET [ SESSION | GLOBAL ] 系统变量名 =;

SET @@[SESSION | GLOBAL]系统变量名 =;

说明:

  1. mysql服务重新启动之后,所设置的全局参数会失效,要想不失效,可以在 配置文件 中修改;
  2. 全局变量(GLOBAL): 全局变量针对于所有的会话;
  3. 会话变量(SESSION): 会话变量针对于单个会话,在另外一个会话窗口就不生效了;

用户自定义变量

用户定义变量,是用户根据需要自己定义的变量,用户自定义变量不用提前声明,在用的时候直接用 “@变量名” 使用就可以。其 作用域当前session 的连接。

定义&赋值

SET 关键字, 赋值时,可以使用 =,也可以使用 :=

SET @var_name = expr [, @var_name = expr] ... ;

SET @var_name := expr [, @var_name := expr] ... ;
查看&赋值

SET 关键字, 赋值时,可以使用 =,也可以使用 :=

SELECT @var_name := expr [, @var_name := expr] ... ;

SELECT 字段名 INTO @var_name FROM 表名;

说明:

  1. 用户定义变量 无需事先对其进行声明或初始化,只不过获取到的值为NULL而已;

局部变量

定义

先声明 后使用。可用作存储过程内的 局部变量 和 输入参数,局部变量的 作用于 BEGIN 和 END 之前。

-- 变量类型就是数据库字段类型,可选值包括:INT、BIGINT、CHAR、VARCHAR、DATE、TIME等。
DECLARE 变量名 变量类型 [DEFAULT ... ] ;

-- 栗子
create procedure test()
BEGIN
	declare my_count int default 0;
	select count(*) into my_count from account;
	select my_count;
END;
赋值
declare my_count int default 0;

-- 方式一
select count(*) into my_count from account;
-- 方式二
set my_count := 123

《MySQL 游标》

游标(cursor):是用来存储 查询结果集数据类型 , 在 存储过程 和 函数 中可以使用 游标 对结果集进行 循环 的处理
游标,提供了一种灵活的操作方式,让我们能够对结果集中的每一条记录进行定位,并对指向的记录中的数据进行操作的数据结构。游标让 SQL 这种面向集合的语言有了面向过
程开发的能力。
在 SQL 中,游标是一种临时的 数据库对象,可以指向存储在数据库表中的数据行指针。

语法

  • 声明游标

游标 必须 先定义,后使用。DECLARE 命名游标,并定义相应地select 语句。
SQLSTATE '02000' 是一个 未找到条件 即 select查询不到数据的 情况

DECLARE 游标名称 CURSOR FOR 查询语句(select name,age from users) ;

-- 声明游标时 建议 加上 声明条件处理程序 : 当SQL语句执行抛出的状态码为02000时,将关闭游标u_cursor,并退出
declare exit handler for SQLSTATE '02000' close u_cursor;
  • 打开游标

声明游标 仅仅是 定义 游标, 后面的 select语句不会执行。
打开游标 这个操作,才会 根据 select语句,将指定范围的数据 检索到 游标 中。

OPEN 游标名称 ;
  • 关闭游标

在结束游标使用时,必须关闭游标, 会释放游标使用的所有内部内存和资源.
如果不手动的关闭游标,mysql 将会在到达 end 关键字 的时候自动关闭它。

CLOSE 游标名称 ;
  • 获取游标记录

into 后面 接受游标记录数据 的 变量的 个数 要和 select 后的字段的个数 一致。
使用 fetch 语句分别访问它的 每一行。

FETCH 游标名称 INTO 变量1 [, 变量2 ] ;

注意:

  1. MySQL 5 添加了对游标的支持,要使用的话MySQL版本需要在mysql 5 及以后的版本。
  2. 一般循环读取游标中的数据 推荐使用 repeat 关键字。

《MySQL 存储过程》

所谓的 存储过程 就是一组预先编译好的 SQL语句的集合, 存储的是 sql的执行过程。

好处:

(1)、封装,复用, 可以把某一业务SQL封装在存储过程中,需要用到的时候直接调用即可;。
(2)、可以接收参数,也可以返回数据, 在存储过程中,可以传递参数,也可以接收返回值;。
(3)、减少网络交互,如果一次操作涉及到多条SQL,每执行一次都是一次网络传输。 如果将这些sql操作封装在存储过程中,只需网络交互一次可能就可以了;。

使用

说明: 如果我们开启了 bin-log, 我们就必须为我们的 function/procedure 指定一个参数。

-- 临时
set global log_bin_trust_function_creators=TRUE;

-- 永久, 在配置文件/etc/my.cnf的[mysqld] 下:
log_bin_trust_function_creators=1
创建
CREATE PROCEDURE 存储过程名称 ([ 参数列表 ])

BEGIN

-- 一组合法的SQL语句

END ;



-- 栗子
create procedure test(in score int,out result varchar(12))
BEGIN

	if score >= 85 then
		set result := '优秀';
	elseif score >= 60 then
		set result := '及格';
	else
		set result := '不及格';

	end if;

END;

说明:

  1. 上面创建存储过程的语句在navicat或者 dbeaver 中没问题,但是放到命令行中执行会报错,在命令行中模式下,需要通过关键字 delimiter 指定SQL语句的结束符
  • 结束标志
    在sql中 分号 ;代表这一条语句的结束;
    存储过程体中,每一条sql语句末尾都要加上分号,所以在创建的过程中,
    要改变 sql 的结束标志,一般建议改为 $$
delimiter $$  # 结束标志 改成 $$
delimiter ;  # 再改回来
  1. 输入、输出参数 说明

存储过程中使用到的参数的类型,主要分为以下三种:IN、OUT、INOUT

  • 参数模式:

in:该类参数作为输入,也就是需要调用时传入值
out:该类参数作为输出,也就是该参数可以作为返回值
inout:既可以作为输入参数,也可以作为输出参数

调用

调用函数 用 select, 而调用 存储过程用的是 call

CALL 存储过程名称;
删除
DROP PROCEDURE [ IF EXISTS ] 存储过程名称 ;
查看
SHOW CREATE PROCEDURE  存储过程名称;
栗子:
  1. 存储过程 中 使用 游标

根据传入的参数salary,来查询用户表employees中,所有工资大于等于15000的员工ID,以及first_name,并将员工的ID和first_name插入到一张新表;

create procedure p_emp(in in_salary int)

begin

	declare u_empid int(12);
	declare u_fname varchar(100);
	declare u_cursor cursor for select employee_id,first_name from employees where salary >= in_salary;

	-- 声明条件处理程序 : 当SQL语句执行抛出的状态码为02000时,将关闭游标u_cursor,并退出
	declare exit handler for SQLSTATE '02000' close u_cursor;

	drop table if exists tb_em_pro;

	create table if not exists tb_em_pro( 
			id int primary key auto_increment, 
			employee_id int(12), 
			first_name varchar(20) 
	);

	-- 打开游标
	open u_cursor;
	
	while true do
			fetch u_cursor into u_empid,u_fname;
			insert into tb_em_pro values (null, u_empid, u_fname);
	end while;

	-- 关闭游标
	close u_cursor;

end;
  1. 存储过程 中 使用 事务
CREATE procedure wtt_t5()
begin
	START TRANSACTION;

	INSERT INTO aaa.admin (name, avatar, gender, tel, password, types, stats, create_at, update_at, del_at) VALUES("caller", '', 1, '', '', 1, 1, 0, 0, 0);
	UPDATE aaa.admin set name="abc" where id = 11111;

	commit;
end;

CALL wtt_t5(); 

《MySQL 存储函数》

简单的理解 存储函数 就是 有返回值 的 存储过程,存储函数 的参数只能是IN类型的。

定义

CREATE FUNCTION 存储函数名称 ([ 参数列表 ])
RETURNS type [characteristic ...]

BEGIN
	-- SQL语句
	RETURN ...;
END ;	


-- 栗子
create function fun_add(n int)

returns int deterministic

BEGIN

		declare sum int default 0;
		while n > 0 do
				set sum := sum + n;
				set n := n - 1;
		end while;

		return sum;
END;

调用

SELECT 存储函数名称(参数);



《Mysql 视图》

基本概念

  • 视图是一个 虚拟表,是 sql的查询结果 ,是一个逻辑表,本身并不包含数据。作为一个select语句保存在数据字典中的。
  • 外表看起来 同真实的表一样,视图包含一系列带有名称的列和行数据,在使用视图时动态生成。
  • 视图的数据变化会影响到基表,基表的数据变化也会影响到视图 ;(基表:用来创建视图的表叫做基表base table)
  • 创建视图 需要create view 权限,并且对于查询涉及到的基表 有 select权限
  • 使用create or replace 或者 alter修改视图,那么还需要改视图的drop权限。

使用场景

  • 控制 一个表中 字段 的 隐匿 和 开放,比如:只把可以让访问的字段放到视图中。
  • 简化 复杂的查询,比如将多个关联表 拼合成 一个视图表。
  • 优化 查询速率,比如:表的行数超过200万行时,就会变慢,可以通过视图将表分为4个部分表。

用法

创建

create [or replace] 
[algorithm = {undefined | merge | temptable}] 
[definer = {user | current_user}]
[sql security { definer | invoker}]
view 视图名 [(列名1, 列名2, ......)] 
as
select 列名, 函数, 等 from 表名 [where ...]
[with [cascaded | local] check option]

说明:

or replace

若创建视图 已经存在,则替换已有视图

algorithm: 表示视图选择算法,

选择在处理定义视图的select语句中使用的方法

  • UNDEFINED:MySQL将自动选择所要使用的算法 (默认算法)
  • MERGE:将视图的语句与视图定义合并起来,使得视图定义的某一部分取代语句的对应部分
  • TEMPTABLE:将视图的结果存入临时表,然后使用临时表执行语句
definer:指出谁是视图的创建者或定义者
  • definer= ‘用户名’@‘登录主机’
  • 如果不指定该选项,则创建视图的用户就是定义者,指定关键字CURRENT_USER(当前用户)和不指定该选项效果相同
SQL SECURITY:

要查询一个视图,要有2个权限:

  1. 对 视图 的 访问权限。
  2. 对 视图基表 的 select权限。

如果 一个用户 只有1权限,没有2权限,那会怎么样?
SQL SECURITY选项 决定执行 的结果:

  • SQL SECURITY DEFINER:创建视图 的 用户 必须对视图所访问的 表 具有select权限,也就是说将来 其他用户访 问表的时候 以定义者 的身份,此时其他用户并没有访问权限。

  • SQL SECURITY INVOKER:访问视图 的 用户 必须对视图所访问的 表 具有select权限。

视图权限总结:
使用root用户定义一个视图(推荐使用第一种):u1、u2

  1. u1作为 定义者 定义一个视图,u1对 基表 select权限,u2对视图有访问权限:u2是以定义者的身份访问 可以查询 到基表的内容;
  2. u1作为 定义者 定义一个视图,u1对 基表 没有 select权限,u2对视图有访问权限,u2对基表有select权限:u2访问视图的时候是以调用者的身份,此时调用者是u2, 可以查询 到基表的内容。
with [cascaded | local] check option: 表示视图在更新时保证在视图的权限范围之内

对于可以执行DML操作的视图,定义时可以带上WITH CHECK OPTION约束
其作用: 对视图所做的DML操作的结果,不能违反视图的WHERE条件的限制。

举例说明:

create view v_aaa 
as 
select * from aaa where age > 10
with check option;

# 对视图进行 更新操作
update v_aaa set age = 5 where id=1;

# 以上的更新是不被允许的,因为 更新的数据 违反了 视图中的 age>10 的条件。
# 所以, 利用with check option约束限制,保证更新视图是在该视图的权限范围之内。
1. cascade是默认值,表示更新视图的时候,要满足视图和表的相关条件;
2. local表示更新视图的时候,要满足该视图定义的一个条件即可

嵌套视图:定义在另一个视图的上面的视图;
WITH CASCADED CHECK OPTION:检查所有的视图,例如:嵌套视图及其底层的视图;
WITH LOCAL CHECK OPTION:只检查将要更新的视图本身,对嵌套视图不检查其底层的视图;

1. 在单表上创建视图
-> create view v_aaa (编号,name,性别)
-> as
-> select num,name,sex from aaa where sex = 'F'
-> with check option;


select * from  v_aaa;
+--------+-----------+--------+
| 编号    | name      | 性别   |
+--------+-----------+--------+
|      8 | Newcastle | F      |
|     27 | Collins   | F      |
|     28 | Collins   | F      |
|    104 | Moorman   | F      |
|    112 | Bailey    | F      |
+--------+-----------+--------+
2. 在多表上创建视图
-> create view v_aaa_bbb (编号,name,性别)
-> as 
-> select aaa.num, aaa.name, bbb.sex from aaa,bbb where aaa.id = bbb.id;

注意:

  • 如果创建视图时不明确指定视图的列名,那么列名就和定义视图的select子句中的列名完全相同;
  • 如果显式的指定视图的列名就按照指定的列名。
  • 显示指定视图列名,要求视图名后面的列的数量必须匹配select子句中的列的数量。
  • 视图的命令规范: v_基表名1_基表名2

查看

1. 视图创建情况
show create view 视图名;
2. 查看视图
SELECT * FROM information_schema.views where table_name = 'my_view';

删除

drop view [if exists] 视图名[,视图名…];

说明:

  • 删除视图是指删除数据库中已存在的 视图(虚拟表)
  • 删除视图时,只能删除视图的定义,不会删除数据,也就是说不动基表,
  • 如果删除 不存在的视图,会抛出异常;
  • 使用IF EXISTS选项 使得删除不存在的视图时不抛出异常。

更改

表结构的修改

修改视图是指修改数据库中已存在的表的定义,
当基表的某些字段发生改变时,可以通过修改视图来保持视图和基本表之间一致

case1:

create or replace view view_name as select语句;

case2:

ALTER
    [ALGORITHM = {UNDEFINED | MERGE | TEMPTABLE}]
    [DEFINER = { user | CURRENT_USER }]
    [SQL SECURITY { DEFINER | INVOKER }]
VIEW view_name [(column_list)]
AS select_statement
    [WITH [CASCADED | LOCAL] CHECK OPTION]
DML操作:增、删、改 (只对 表 内部的 数据 的 操作,不涉及 表 的 定义、结构 的修改)

因为视图本身没有数据,因此对视图进行的 DML操作 最终都体现在基表中
所以,在创建示图时 的 select 语句中 有下列内容之一,视图不能做DML操作:

  • select子句中包含distinct
  • select子句中包含组函数
  • select语句中包含group by子句
  • select语句中包含order by子句
  • select语句中包含union 、union all等集合运算符
  • where子句中包含相关子查询
  • from子句中包含多个表
  • 如果视图中有计算列,则不能更新
  • 如果基表中有某个具有非空约束的列未出现在视图定义中,则不能做insert操作


《MySQL 触发器 和 事件》

触发器

概念

在数据表中发生了某 件事(插入、删除、更新操作),然后自动触发了预先编好的若干条SQL语句的执行。

特点

触发事件的操作和触发器里面的SQL语句是一个事物操作,具有原子性,要么全部执行,要么都不执行。

语法

  • 创建
create trigger 触发器名字 执行时机 触发器类型
on 表明 for each row
begin
	# sql执行体
	一系列sql语句操作;
end;

# 说明:
执行时机: AFTER | BEFORE =》指定触发器中的 sql操作 发生在 事件发生 的前后 ;
触发器类型:INSERT | UPDATE | DELETE ;
FOR EACH ROW: 表示任何一条记录上的操作满足触发事件都会触发该触发器;
  • 查看
# 查看全部 类型为 INSERT 的触发器
show triggers where Event='INSERT';

# 查看指定触发器
show create trigger 触发器名字;
  • 删除
drop trigger 触发器名字;
  • 触发器 的 new 表old表

MySQL 中定义了 new 和 old 两个临时表,用来 保存 触发了触发器的那一行数据 的 前后变化的数据

  • INSERT 型触发器中,new 用来拦截保存``将要(BEFORE)已经(AFTER)插入的新数据
  • UPDATE 型触发器中,old用来 拦截 并 保存 将要或已经被修改的原数据,new 用来拦截并保存将要或已经修改为的新数据
  • DELETE 型触发器中,old 用来拦截并保存将要或已经被删除的原数据

例子

TABLE `aaa` (
  `uid` int(11) NOT NULL AUTO_INCREMENT,
  `account` varchar(20) DEFAULT '' COMMENT '帐号',
  `age` tinyint(4) DEFAULT '0',
  PRIMARY KEY (`uid`)
) 
TABLE `abc` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `name` varchar(100) DEFAULT NULL,
  PRIMARY KEY (`id`)
)

# 案例一:
##
在 Dbeaver 的 sql编辑板 中 要把 穿件触发器的所有`选中` 之后 再进行 语句执行,  
否则 sql执行体 中的 分号 会被认为是 创建 触发器 sql 语句 结束 的标志,会报语法错误。
##

create trigger wtt_test_trg
after INSERT on aaa for each row
BEGIN 
	INSERT into abc (name) values (new.account);
END;


# 案例二:
##
在 命令行 中 创建 触发器 需要 将mysql默认结束符进行修改,
在 sql执行体 可以加入判断语句,让触发器更聪明。
##

delimiter $$  # 将mysql默认结束符;换为$$

create trigger tri_afrer_insert_cmd after insert on cmd for each row  
begin  # 触发器代码
	if new.account = '123456789' then  # mysql中if语句固定格式
		INSERT into abc (name) values (new.account);
	end if;
end $$

delimiter ;  # 结束触发器要把默认结束回来 不然后续操作容易混淆

事件

事件: 对一个数据库而言,在指定的时间,按照一定的时间间隔,执行预设的SQL语句。
优点: 一些对数据定时性操作不再依赖外部程序,而直接使用数据库本身提供的功能。
缺点: 触发事件的机制只能是时间,不能是别的动作。

进入mysql,通过:

show variables like 'event_sch%';

首次 查看 事件 是否开启,得到以下内容:

Variable_name Value
event_scheduler OFF

可见 默认情况下 是没有 开启的。要想使用事件 首先要 开启 事件:

set global event_scheduler=1;

事件常见操作:

-- 栗子:(周期执行Every:单位有second、minute、hour、day、week、quarter、month、year;)
-- On Schedule Every 1 second  // 每秒执行1次
create event 事件名 on schedule 时间间隔 starts 时间 ends 时间 do sql语句; 

-- 创建事件
create event insert_data on schedule every 2 second do insert into abc (id,name,gender) values (3,'tom','man');

-- 更改事件 (最好把事件停下 来后再进行修改)
alter event insert_data on schedule every 2 second do insert into abc (id,name,gender) values (44,'cat','woman');

-- 让事件停止执行
ALTER EVENT insert_data DISABLE;

-- 让停止的事件 运行起来
alter event insert_data enable;

-- 查看所有事件
SELECT * FROM information_schema.events;  

-- 删除事件
DROP EVENT insert_data;

注意:
如果没有starts 则默认为现在; 如果没有ends 则默认为 永远。



《MySQL 事务》

介绍

  1. 事务:
    数据库管理系统工作的一个逻辑单位,由一个或多个SQL语句构成。
    事务结束的标记: 成功下:commit; 失败下:rollback。
  2. 场景:
    资金流动相关的场景。
  3. 支持:
    InnoDB存储引擎支持 事务。
  4. 四大特性(ACID):
  • 原子性(Atomicity,或称不可分割性)
    一个事务的一系列SQL操作是一个最小单位,所有的SQL操作语句是一体的,要么大家都执行成功,要么大家都执行失败。绝不会出现部分SQL语句执行成功的情况。
    事务在执行过程中发生错误,会被回滚(Rollback)到事务开始前的状态,就像这个事务从来没有执行过一样。
  • 一致性(Consistency)
    在事务开始之前和事务结束以后,数据库的完整性没有被破坏。
    举例,A表转账B表,如果A少了500,而B却增加了100,这就不满足一致性了。
    一致性 通常在 代码中空值,数据库中对于一致性不好操作。
  • 隔离性(Isolation,又称独立性)
    数据库允许多个并发事务同时对其数据进行读写和修改的能力,隔离性可以防止多个事务并发执行时由于交叉执行而导致数据的不一致。
  • 持久性(Durability)
    事务处理结束后,对数据的修改就是永久的,即便系统故障也不会丢失。
  1. autocommit:
    【1】、每个SQL语句之所以 能改变数据表中 的数据,就是因为数据库的 autocommit是默认开启的。
    【2】、每次执行完SQL语句,结果数据都是在缓存中的,只有commit一下,结果数据才会到数据库中,开启了 autocommit,意味着每次执行完SQL语句,数据库就会自动 进行commit 把缓存中的数据加载到数据库中。
    【3】、autocommit开启的情况下,一句SQL语句就是一个事务。
    【4】、用 SET 来改变 MySQL 的自动提交模式:
    SET AUTOCOMMIT=0 禁止自动提交
    SET AUTOCOMMIT=1 开启自动提交

举例子:

begin;  -- 手动开启一个事务
insert into table_name (id) value(5);
commit; -- 提交事务, 事务结束, 数据表中 已添加 一条记录

begin;  -- 开事务
insert into table_name (id) value(6);
rollback; -- 回滚 到事务执行前的状态,数据表中 未添加 一条记录。

事务并发 带来的问题:

【1】、脏读: 一个事务读取到另一个事务由于 添加操作 产生在缓存中的数据(没有commit到数据库中的数据在缓存中保存着)。

【2】、幻读: 一个事务读取到另一个事务由于 更新 or 删除 操作 产生在缓存中的数据

【3】、不可重复读: 两个事务同时工作,
事务A执行:select name from table_name where id=1
事务B执行:update table_name set name=“tom” where id=1;
对于事务A而言,在一个事务中,同样的操作,获取的结果却是不一样的


解决事务并行带来的问题:

  1. LBCC:
    在读取数据前,对其加锁(锁机制下节讲到)将强事务的隔离等级,防止其他事务对数据进行操作,当事务完成后会自动释放锁。
  2. MVCC:
    在读取数据前,给数据一个快照(备份数据),这个事务的后续操作就以这个快照为准。

事务自动开启的 行锁 的四个 隔离等级,依次如下:

隔离等级 脏读 不可重复读 幻读
Read Uncommit(未提交读) yse yse yse
Read Committed(已提交读) 解决脏读 yse yse
Repeatable Read(可重复读) 解决脏读 解决不可重复读 yse
Serializable(串行化 即一个一个事务的执行) 解决脏读 解决不可重复读 解决幻读
  • 脏读”、“不可重复读”和“幻读”,其实都是数据库读一致性问题,必须由数据库提供一定的事务隔离机制来解决。
  • 数据库的 事务隔离越严格,并发产生的问题就越小,但是 对并发的支持就越不友好,这个矛盾是无法完全调和的,只能寻找其性价比 最高的那个点。
  • 查看当前数据库的事务隔离级别:show variables like ‘tx_isolation’; mysql 默认是 可重复读

常见问题:

lock wait timeout exceeded; try restarting transactio

  • 问题现象:
      接口响应时间超长,耗时几十秒才返回错误提示,后台日志中出现Lock wait timeout exceeded; try restarting transaction的错误。

  • 问题场景:
      1、多事务先后顺序操作同一 记录 造成死锁的;
      2、多台服务器操作同一数据库;
      3、瞬时出现高并发现象;

  • 解决方法:

SELECT * FROM information_schema.INNODB_TRX;
kill trx_mysql_thread_id;



《InnoDB存储引擎上 的 锁介绍》

力度上划分: 行锁(锁一行数据) 、表锁(锁一个表的数据)
用法上划分: 乐观锁、悲观锁
模式上划分: 排他锁、意向锁、共享锁、自增锁
算方上划分: 间隙锁、记录锁、插入意向锁、临键锁

表锁 和 行锁 的PK:

加锁力度: 表 > 行
加锁效率: 表 > 行
冲突概率: 表 > 行
并发性能: 表 < 行

  • 意向锁(表锁)

意向锁 是由 数据引擎 自己维护的,用户无法手动操作。
作用: 提高加表锁的效率。
作用解释:
加表锁 成功的 前提是: 这个表中的任何一条数据都没有被 事务加锁,所以在加表锁的时候,程序就会一条一条的查看数据上是否有锁,这种 全表扫描 的方式是很费事的。
意向锁的生成是你在加 行锁 的时候(或者说之前),系统就会自动给该表先加上一个意向锁(如果加的行锁是 共享锁,那么意向锁加的就是 意向共享锁),等给该表加 表锁的时候,就无需 全表扫描,只需查看该表有无意向锁就可以了,有就不能加表锁,没有就能加。

  • 间隙锁

当我们用范围条件 检索数据时,InnoDB 会给符合条件的 已有数据记录的索引项加锁;对于在 检索 条件范围内并不存在的记录,叫做**“间隙(GAP)”**
InnoDB也会对这个“间隙”加锁,这种锁机制是所谓的间隙锁

间隙锁的危害: 举例说明

数据表 tt:

id name
1 tom
3 cat
4 jerry
5 red
事物A进行如下操作:
update tt set b='aaa' where a>1 and a<5

此时事物A还为 commit,然后事物B 进行如下操作:

insert into tt (id, name) values (2, 'cookie');

事物B的 插入数据的操作 是处于 阻塞状态的,因为事物A 锁的是一个范围,只要在这个范围的,不论是间隙还是已有数据,其他 事物 都是不允许 动的。


锁到底锁住了什么? 一条记录? 一个字段?

锁其实锁住的是索引

  1. 有的表没有索引,没有索引的表 即使加了行锁,也会锁表。

其实任何表都是有索引的,表中数据的物理排放 就是 按照 聚集索引 排放的。

  • 如果表中定义了主键,那么主键就是 聚集索引,进而主键决定数据的物理存放顺序。
  • 如果表中没有主键,却又一个唯一不为空 约束的字段,那么该字段 就是 聚集索引。
  • 如果以上都没有,那就使用 表的隐藏字段 _rowid 为聚集索引。



《MySQL 函数》

函数分类

mysql中的函数分为两大类: 单行函数统计函数
单行函数: 如果函数的参数用到 表中的字段,那么涉及到该字段的 每一条记录 都会 各自调用一次该函数, 从返回结果来讲,因为该函数被 多次调用,所以返回的结果 有 多个。
统计函数: 如果函数的参数用到 表中的字段,那么涉及到该字段的 所有记录 最后一起 调用一次函数, 从返回结果来讲,因为该函数只被 调用一次,所以返回的结果 只有 一个。

函数调用形式

  • 函数中的参数不涉及到 表中的字段:

select 函数名(‘aaa’);

  • 函数中的参数涉及到 表中的字段:

select 函数名(id) from tableName;

一、单行函数

1. 数学函数

  • 运算符号

进行数学算术运算的符号

select id * age 计算结果 , id ,age from tablename;
计算结果 id age
10 1 10
20 2 10
  • PI() ==> π

返回圆周率π,默认显示6位小数
二分之一π,可以表示为: PI()/2

  • exp(2) ==> e*e

返回e的 n 次乘方 后的值

  • log(sxp(2)) ==> 2

参数的 自然对数

  • log10(1000) ==> 3

参数的 基数为10的对数

  • abs(-9) ==> 9

返回x的绝对值

  • sign(-23) ==> -1

返回 数 的符号,-1表示负数,0表示0,1表示正数

  • sqrt(9) ==> 3

返回非负数的x的二次方根

  • power(3,4) ==> 81

3 的 4 次方

  • mod(8,3)

等同于 sql的 8%3 ==》 2

  • ceil(3.13) ==> 4

上取整

  • floor(3.13) ==> 3

下取整

  • round(2.71828, 2) ==> 2.72

四舍五入,第二个参数指定保留小数点后几位,如果不写,则四舍五入为整数。

  • radians(180) ==> π

将 角度 转为 弧度
角度 360度 对应的 弧度为 2π
三角函数的 使用的 参数弧度

  • degrees(2*Pi()) ==> 360

将 弧度 转为 角度

  • sin(pi()/2) ==> 1
  • asin()
  • cos()
  • acos()
  • tan()
  • atan()
  • cot()

重点说明:

  1. 如果参与 计算的字段 不是数值类型,则按其值为0处理。
  2. 如果参与 计算的字段 值 为 NULL值,则计算结果 直接为 == NULL== 值。
  3. 如果要使用 函数的返回值 作为筛选条件,应该使用having而不是where,这一条也适用于其他类的函数:
#  下面这样用 where 是会报错的
select id * age 计算结果 , id ,age from tablename where 计算结果 > 10 order by 计算结果 ;

#  下面这样用 having 就可以了
select id * age 计算结果 , id ,age from tablename having 计算结果 > 10 order by 计算结果 ;

# 从上可以得出,如果==排序==的话,  则直接可以使用 计算的结果

2. 字符函数

  • length(‘aaa你好’) ==> 9

字节长度;
一个字母一个字节,一个汉字三个字节, 这是由 字符集utf-8 决定的,
如果mysql使用的时 jdk字符集 那么一个汉字 两个 字节。

  • char_length(‘aaa你好’) ==> 5

字符长度

  • concat(‘a’,‘b’,666, false, true,‘—’, now()); ==> ab66601—2021-08-06 10:06:30

字符拼接
待拼接的内容任意一个为NULL则返回值为NULL

  • concat_ws(‘+’, ‘aaa’, ‘bbb’,123) ==> aaa+bbb+123

可以添加连接符 的 字符拼接

  • insert(‘a你cdefg’, 1, 4, ‘12’) ==> 12efg

将字符 abcdefg, 从第1个字符开始,往后数4个字符,用’12’将其替换掉。

  • replace(‘a+a+a+a’, ‘+’, ‘-’) ==> a-a-a-a

  • upper() 和 lower() 转换大小写

  • repeat(‘123’, 3) ==> 123123123

  • concat(‘a’, space(3), ‘b’) ==> a___b (a和b之间有三个空格)

字符切割:

  • left(‘abcdefg’, 2) ==> ‘ab’

从左开始截取字符串: left(被截取字段,截取长度)

  • right(‘abcdefg’, 2) ==> ‘fg’

从右开始截取字符串: left(被截取字段,截取长度)

  • trim(‘+’ from ‘+++123456+’) ==> 123456

若未指定要删除左右两边的内容,则默认删除空格。

  • substring(‘abcdefg’, 1, 2) ==> ab

从第几个字符开始切, 一共要切取几个字符。
注意不是从0开始的。

  • substring_index(‘abc-def-ghi’, ‘-’, 1) ==》 abc
  • substring_index(‘abc-def-ghi’, ‘-’, -1) ==》 ghi
  • substring_index(‘abc-def-ghi’, ‘-’, -1) ==》abc-def

根据字符切割分段, 获取切割后的几段 由 第三个参数 控制。

3. 时间函数

  • UTC_DATE()和UTC_TIME()

前者返回当前UTC(世界标准时间)日期值

  • now() 返回当前系统的 日期 + 时间

  • curdate 返回当前系统的 日期

  • curtime 返回当前系统的 时间

  • year() 获取 指定时间 的 年份

select year(now());
select year('2021-1-1');
  • month() 获取 指定时间 的 月份

  • WEEK(d)、WEEKOFYEAD(d)

前者计算日期d是一年中的第几周,
后者计算某一天位于一年中的第几周

  • str_to_date() 将 日期样式的 字符串 转换为 指定格式的 时间。
select str_to_date('12-30 2020', '%c-%d %Y');

mysql的智能自动格式转换:

select * from aaa where bithday = '1992-3-4';
select * from aaa where age = '10';

以上两种情况字符串都会智能转换,
因为 mysql意识到 bithday 是时间类型的字段,所以 会将 该字段后面的内容 按照 默认的时间转化格式:
年 月 日 的转换顺序 进行自动转换。
如果后面的字符时:4-3-1992 自动转换就会失败了。同理 字符串 也会智能转换为 数值类型。

  • date_format() 将时间 转换成 字符串
select str_to_date('2020/12/30', '%c月%d日---%Y年');
  • 时间 和 字符 的 转换规则表
格式符号 功能样式
%Y 四位的年份
%y 两位的年份
%m 月份(01、02…11、12)
%c 月份(1、2…11、12)
%d 日(01、02…11、12)
%H 小时(24小时制)
%h 小时(12小时制)
%i 分钟(00、01、02…59)
%s 秒钟(00、01、02…59)
  • current_timestamp() ==> 2021-08-06 10:47:34
  • localtime()
  • now()
  • sysdate()

这四个函数作用相同,返回当前日期和时间值,格式为"YYYY_MM-DD HH:MM:SS"

  • unix_timestamp() ==> 当前时刻的 10位 时间戳

有参数,则返回指定时间的时间戳: unix_timestamp(‘2020-01-02 10:00:00’)

-- 当日0点0分0秒时刻的时间戳
SELECT UNIX_TIMESTAMP(CAST(SYSDATE()AS DATE)); 
-- 昨日0点0分0秒时刻的时间戳
SELECT UNIX_TIMESTAMP(CAST(SYSDATE()AS DATE) - INTERVAL 1 DAY)
-- 明日0点0分0秒时刻的时间戳
SELECT UNIX_TIMESTAMP(CAST(SYSDATE()AS DATE) + INTERVAL 1 DAY)
  • FROM_UNIXTIME() 时间戳 --转–> 时间

SELECT FROM_UNIXTIME(1630301610,‘%Y-%m-%d %H:%i:%S’)
====> 2021-08-30 13:33:30

4. 流程控制函数

  • if函数

类似于JavaScript的三元表达式。

select if(10 < 5, '对了', '错了');

# 多条件 
select if(1=2 or 1=1, 111, 222)  ⇒ 111
select if(1=2 and 1=1, 111, 222)  ⇒ 222

# where 之后
select * from user where age = if(sex=1, 40, 20);

# 可选 多条件
select * from user 
where city = '临沂市' 
and if(age=0, id<>0, age > 18) 
and if(sex=0, id<>0, sex = 1); 
  • ifnull 函数

对 null值 进行处理:

# 如果某条记录的 age字段为null,则该null按照 第二个参数 指定的 数字10处理。
select id * ifnull(age, 10) from tablename;
  • case函数 (类似于 switch语句)

等值判断 (case之后有东西)

-- 案例:部门号为10的显示工资为原来的10被,为20的显示5被,其余的显示原工资
select salary 原工资, dep 部门,
case dep
when 10 then salary * 10
when 20 then salary * 5
else salary
end '起个别名: 新工资'
from tableName;

范围判断 (case之后没有东西)

-- 案例:部门号为10的显示工资为原来的10被,为20的显示5被,其余的显示原工资
select salary 原工资, dep 部门,
case 
when dep < 10 then salary * 10
when dep < 20 then salary * 5
else salary
end '起个别名: 新工资'
from tableName;

5. 系统函数

  • version() ==> 5.7.35-0ubuntu0.18.04.1

  • connection_id() ==> 38

这个函数返回的是 这个连接 的 连接ID,
对于已经建立的连接的客户端,都有一个唯一的连接ID.
通过命令查看当前的链接列表:

mysql> show processlist;
+----+------+-----------------+-------+---------+------+----------+------------------+
| Id | User | Host            | db    | Command | Time | State    | Info             |
+----+------+-----------------+-------+---------+------+----------+------------------+
| 33 | root | localhost:42990 | mysql | Sleep   | 9083 |          | NULL             |
| 34 | root | localhost:44620 | mysql | Sleep   | 3701 |          | NULL             |
| 35 | root | localhost:44622 | mysql | Sleep   | 3701 |          | NULL             |
| 38 | root | localhost       | wtt   | Query   |    0 | starting | show processlist |
+----+------+-----------------+-------+---------+------+----------+------------------+
如果是root帐号,你能看到所有用户的当前连接。如果是其它普通帐号,只能看到自己占用的连接。
show processlist;只列出前100条,如果想全列出请使用show full processlist;
如何停止某个链接:     kill  id

二、统计函数

  • sum() 返回 涉及到的所有记录 的某个字段 的 和。

  • avg() 返回 涉及到的所有记录 的某个字段 的 平均值。

  • max() 返回 涉及到的所有记录 的某个字段 的 最大值。

  • min() 返回 涉及到的所有记录 的某个字段 的 最小值。

  • count() 返回 涉及到的所有记录 的某个字段 的 非空值 的总个数。
    重点说明: count 在使用的过程中,不能出现 跳过几个记录 的动作select count(*) from aaa limit 5,2; 像这种SQL语句:要求跳过两条,还要求计算所有的记录数, 明明都跳过了还得计算在内,显然是矛盾的,所以这个属于逻辑错误,而非语法错误
    说明: count仅仅统计 字段值为 非空值 的记录,所以 统计表中的记录 条数的时候我们 使用 count(*),意思是一条记录中 只要有一个字段不是null值就可以被统计上

查看更多



《利用 用户变量 进行 树结构查询》

用户变量 在 某些场景下 能极大的提升 查询 效率, 

变量 的 定义 一般写在 from 语句之后, 
变量 的 使用 一般写在 select 语句之后。
在 sql中 := 既是 声明变量 符 又是 赋值符。

select@i := @i+1 -- 这里是 使用 变量@i 区域
from(select @i := 0) wtt, -- -- 这里是 定义 初始化 变量 @i 区域
	
	users 

where@i < 10 and users.name <> ''



-- 输出:@i := @i+1|----------+1.0|2.0|3.0|4.0|5.0|6.0|7.0|8.0|9.0|10.0|

使用方式

  • 自定义变量 获取 字段数值
select@i := id,
	
name 

from(select @i := 0) wtt,
	
users 

where@i < 10 and users.name <> ''

自定义变量 的 循环次数 是由于 where 筛选出 记录数 决定的。

  • 自定义变量 获取 函数数值
select@aaa := CONCAT(@aaa , "-",name) ,
	
	name 

from(select @aaa := 'ha') wtt,
	
	users 

where 
	
	users.name <> '';


  • 自定义变量 获取 查询数值
    下面通过 树结构 的数据存储 结构的数据表, 进行 自定义变量 经典用法 的 案例展示,
    这里需求是: 根据 子id, 获取 其 父id, 以及 父id的父id …
    记录字段说明: 同一个记录总, fid 记录 uid 的 父id。
select 
	@r, 
	-- 下面 是将 查询到的 fid的结果 赋值给 变量 @r, 然后进行 下一轮的 循环
	(select @r := fid from user_friends where uid = @r) as tmp_id
from 
	(select @r := 3) wtt, 
	user_friends 
where 
	fid > 0 limit 5

-- 输出:
@r|tmp_id|
--+------+
 3|  2   |
 2| NULL |
 2| NULL |
 2| NULL |
 2| NULL |

重点说明:
从上面的输出可以看出, 如果 (select @r := fid from user_friends where uid = @r) 匹配不到查询结果时,
tmp_id 的值 被赋值为 NULL, 变量@r 不进行赋值操作, 即 @r的值 原先是啥 经过 赋值 之后 还会是啥

问题说明:
我们发现 当 (select @r := fid from user_friends where uid = @r) 查询不到结果时,没能及时的将 循环操作停下来,
如果不是靠着 limit 停下来的,会遍历 user_friends表中 所有符合 where 条件的记录。
显然 这是 没必要的, 例如上面,循环了 4次,浪费了 性能,固然是需要优化的,
具体 优化代码如下:

查找 子节点 上的所有 父节点

select 
	@wtt_compare := @r, 
	-- 下面 是将 查询到的 fid的结果 赋值给 变量 @r, 然后进行 下一轮的 循环
	(select @r := fid from user_friends where uid = @wtt_compare) as tmp_id,
	@wtt_compare := if(@wtt_compare = @r, 0, 1) is_continue
from 
	(select @r := 3, @wtt_compare := 1) wtt, 
	user_friends 
where 
	@wtt_compare <> 0 and fid > 0

-- 输出:
@r|tmp_id| is_continue
--+------+------------+
 3|  2   | 1
 2| NULL | 0

通过 比对 @r变量 赋值前后的 变化 进而 确定 赋值的select语句是否查询到结果,  
进而 控制 循环的 次数, 这样以来 仅仅 多进行一次循环,这个是完全可以接受的。

-- 重点说明 ··(select @r := fid from user_friends where uid = @wtt_compare) 中, 
uid 的赋值 用的是 变量 @wtt_compare 而不是 @r, 
fid 的给值 给的是 变量 @r 而不是 @wtt_compare。
这一点一定要注意,否则 会报错。


-- 扩展说明 ··
@wtt_compare := @r as abc
这个 起别名的操作 实际上是给 左值(等号左侧) 变量起的别名,即 @wtt_compare 变量的 别名。
  
所以 (select @r := fid from user_friends where uid = @wtt_compare) 可以改为:
(select @r := fid from user_friends where uid = abc) 

典例二:

查找 父节点 下的所有 父节点

CREATE TABLE `tree`  (
  `id` bigint(11) NOT NULL,
  `pid` bigint(11) NULL DEFAULT NULL,
  `name` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL,
  PRIMARY KEY (`id`) USING BTREE
) ENGINE = InnoDB CHARACTER SET = utf8 COLLATE = utf8_general_ci ROW_FORMAT = Dynamic;


INSERT INTO `tree` VALUES (1, 0, '中国');
INSERT INTO `tree` VALUES (2, 1, '四川省');
INSERT INTO `tree` VALUES (3, 2, '成都市');
INSERT INTO `tree` VALUES (4, 3, '武侯区');
INSERT INTO `tree` VALUES (5, 4, '红牌楼');
INSERT INTO `tree` VALUES (6, 1, '广东省');
INSERT INTO `tree` VALUES (7, 1, '浙江省');
INSERT INTO `tree` VALUES (8, 6, '广州市');


-- 
SELECT *  
FROM (
        SELECT 
        	t1.*,
        	IF(FIND_IN_SET(pid, @pids) > 0, @pids := CONCAT(@pids, ',', id), '0') AS ischild
        FROM 
        	(SELECT * FROM tree AS t ORDER BY t.id ASC) t1,
        	(SELECT @pids := 1) t2
) t3 
WHERE ischild != '0';

-- 字符包含 函数作用说明:
-- 以逗号 为分割, 判断 第一个参数 在 第二个参数 中的位置:
SELECT FIND_IN_SET('y','ax,y,z'); -- 2

附加重点:执行顺序

到此知道了 自定义变量 ,就可以更好的 说明 select 语句的执行顺序的问题了:

  • select 和 from:
    先执行 form 后的内容,得到一个数据集 后, 再根据 select 后的内容 对 数据集 相应操作 然后将数据 展示出来。
    注意,展示多条数据 不是 一次 form一次 select,而是 一次性 from 多次select。
    因此 select 之后 给字段 加的别名 信息, from 之后的 where 是 获取不到的。
  • 同一 select下:
    前面的 先执行。
select 
	@wtt_compare := @r, 
	-- 下面 是将 查询到的 fid的结果 赋值给 变量 @r, 然后进行 下一轮的 循环
	(select @r := fid from user_friends where uid = @r) as tmp_id,
	@wtt_compare := if(@wtt_compare = @r, 0, 1) is_continue
from 
	(select @r := 3, @wtt_compare := 1) wtt, 
	user_friends 
where 
	@wtt_compare <> 0 and fid > 0

-- 将 上面的sql语句,一次循环 按照 执行顺序 向下 铺开:
  (select @r := 3, @wtt_compare := 1) wtt, 
  user_friends 
  @wtt_compare <> 0 and fid > 0
  @wtt_compare := @r, 
  (select @r := fid from user_friends where uid = @r) as tmp_id,
  @wtt_compare := if(@wtt_compare = @r, 0, 1) is_continue

《三大设计范式》

当数据库比较复杂的时候,就需要设计了
糟糕的设计:

  • 数据冗余,浪费空间
  • 数据库插入和删除麻烦
  • 程序的性能差

良好的设计:

  • 节省内存空间
  • 保证数据库的完整性
  • 方便开发系统

三大范式: 可以规避信息重复、更新异常、插入异常、删除异常等问题

  • 第一范式:保证每一列不可再分

例如:
地址: 山东省临沂市兰山区
上面 地址 的 字段值 是可以再拆分的,所以不满足 第一范式,这样写就满足了:
省份: 山东省,
市区: 临沂市,
县区: 兰山区,

  • 第二范式: 在满足第一范式的基础上,每张表只描述一件事情,所有的属性必须完全依赖主键。
  • 第三范式: 在满足第二范式的基础上,确保数据库表中的每一列数据和主键直接相关,而不是间接相关。



《数据表 设计的 相关规则》

命名规则(严格遵守,否则会有大隐患!!!)

  • 库名、表名、字段名: 小写字母 + 下划线禁止出现大写字母,禁止用-连接
  • 表名: 表前缀user_*** 可以把相同关系的表显示在一起;
  • 索引: 普通(组合)索引idx_表名_字段名_字段名; 唯一索引uk_表名_字段名

数据类型 的 设计原则

  • 尽可能使用 小的数据类型

小的数据类型占用更少的磁盘、内存和CPU缓存,所以操作效率也会更快。
举例:
处理日期的时候,存储用户日期,应该选择date类型而不是datetime,datetimek可以精确到时分秒。
在需要存储年龄、性别、状态 之类的应用场景中,应该选择 tinyint 来存储,而不是int。
能使用int就不要使用varchar、char,能用varchar(16)就不要使用varchar(255);
特别说明:
int(5)和int(10)的区别是什么?
int(x) 的x不管是什么值,存储数字的取值范围还是int本身数据类型的取值范围,x只是数据的 显示长度 而已。

  • 尽可能使用 固定长度

存储字符时,应优先考虑char数据类型,因为char是定长的,而varchar 是变长的,mysql处理char比varchar要快一点。
char类型的最大宽度为255 字节,varchar 最大宽度为 65535 个字节。
特别说明:
char(10)和varchare(10)的区别是什么?
定义一个char[10]和varchar[10],如果存进去的是‘abcd’,那么char所占的长度依然为10,除了字符‘abcd’外,后面跟六个空格,而varchar就立马把长度变为4了。
取数据的时候,char类型的要用trim()去掉多余的空格,而varchar是不需要的。
因为固定长度的数据便于程序的存储与查找,所以char的存取数度还是要比varchar要快得多。char为此付出的代价是空间,可谓是以空间换取时间效率。

  • 财务相关数据,使用decimal类型

用decimal来存储金额字段,不要用float和double,会出现数据精度丢失。


数据表 的 设计原则

  • 合理的使用 映射表

若多张表之间的关系复杂,建议采用第三张映射表来关联维护表之间的关系,以降低表之间的直接耦合度。

  • 添加必要的(冗余)字段

像“创建时间”、“修改时间”、“备注”、“操作用户IP”和一些用于其他需求(如统计)的字段等,在每张表中必须都要有,不是 说只有系统中用到的数据才会存到数据库中,一些冗余字段是为了便于日后维护、分析、拓展而添加的,这点是非常重要的。

  • 每个表中必须有自增主键;
  • 最好给每个字段一个默认值,最好不能为空,null字段 很难查询优化 且 占用额外索引空间null字段的 组合索引无效。

SQL的 操作规范

  • 避免在数据库中进行数学运算,MySQL不擅长数学运算和逻辑判断,且容易导致索引失效。
  • 使用in代替or,in的值不超过1000个。
  • INSERT语句必须显式的指明字段名称,不使用INSERT INTO table(),容易在增加字段后出现 对位顺序错乱的 程序BUG。



《用户管理》

1. 创建用户

create user 'tom'@'%' identified by '666666';

说明:

  • tom: 将创建的用户名
  • %: 指定该用户在哪个主机上可以登陆,如果是本地用户可用localhost,如果想让该用户可以从任意远程主机登陆,可以使用通配符%
  • 666666: 该用户的登陆密码,密码可以为空,如果为空则该用户可以不需要密码登陆服务器。
  • 初创的 用户 是没有 任何的操作权限, 也无法查看 任一个 数据库。

2. 删除用户

drop user 'tom'@'%';

3. 更改密码

SET PASSWORD FOR 'tom'@'%' = PASSWORD("123456");

4. 设置权限

# 指定 权限 的 指定 表
grant select, insert, update on database_name.table_name to 'tom'@'%';

# 指定 权限的 所有表
grant select, insert, update on * to 'tom'@'%';

# 全部操作权限,以及全部的数据库
grant all on *.* to 'tom'@'%';

刷新权限 ,如果还是没有生效,可以重启mysql服务

 flush privileges

说明:

  • grant: 后面指定 用户的操作权限,如SELECT,INSERT,UPDATE等,如果要授予所的权限则使用ALL
  • wtt.c: 指定用户可以访问的 数据库,或数据表。如果要访问某个数据库wtt.* ,如果要访问所有数据库则使用*.*

5. 查看数据库的所有用户

mysql -uroot -p123456
SELECT User, Host FROM mysql.user;

你可能感兴趣的:(数据库,mysql)