MySql笔记

数据库()语言

DDL:定义

DML:操作

DQL:查询

DCL:控制

模板

select [all | distinct]
{* | table.* | [table.field1[as alias1],[table.field2[as alias2]]}
from table_name[as table_alias]
	[left | right | inner join table2_name]-- 联合查询
    [where...] 		-- 指定结果满足的条件
    [group by...] 	-- 指定结果按照那几个字段分组
    [having...] 	-- 过滤分组的记录必须满足的次要条件
    [order by...]   -- 指定查询记录按一个或多个条件排序
    [limit startindex, size] -- 分页

操作数据库

CREATE DATABASE IF NOT EXISTS WESTOS
DROP DATABASE IF EXISTS WESTOS
USE  SCHOOL
/*如果字段名是一个特殊字符,	需要使用符号包裹,USE `SCHOOL`*/
SHOW DATABASES

字段数据类型

数值

tinyint(m) 1个字节 范围(-128~127)
smallint(m) 2个字节 范围(-32768~32767)
mediumint(m) 3个字节 范围(-8388608~8388607)
int(m) 4个字节 范围(-2147483648~2147483647)
bigint(m) 8个字节 范围(+-9.22*10的18次方)

float:单精度浮点数,4个字节

double:双精度浮点数,8个字节

decimal:字符串形式的浮点数,防止精度丢失

字符串
char(n) 固定长度,最多255个字符
varchar(n) 固定长度,最多65535个字符
tinytext 可变长度,最多255个字符
text 可变长度,最多65535个字符
mediumtext 可变长度,最多2的24次方-1个字符
longtext 可变长度,最多2的32次方-1个字符

时间日期

date 日期 '2008-12-2'
time 时间 '12:25:36'
datetime 日期时间 '2008-12-2 22:06:44'
timestamp 1970.1.1到现在的毫秒数

字段属性

自增

0填充

不为空

主键

默认值

创建表

create table if not exists `student`(
`id` int(5) not null auto_increment comment'学生id',
`name` varchar(30) not null default'匿名' comment'姓名',
`pwd` varchar(30) not null default'123456' comment'密码',
`gender` varchar(1) not null default '男' comment'性别',
`birthday` datetime default null comment'生日',
`address` varchar(100) default null comment'地址',
`email` varchar(50) default null comment'邮箱',
primary key (`id`)
)ENGINE =innodb default charset=utf8;

show create database school
show create table student

DESCRIBE student/*查看表的结构*/

关于数据库引擎

MYISAM INNODB(默认使用)
事务支持 不支持 支持
数据行锁定 不支持 支持
外键 不支持 支持
全文索引 支持 不支持
表空间大小 较小 较大,约为2倍MYISAM
节约空间,速度较快 安全性高,支持事务,多表多用户操作

修改删除

alter table teacher rename as teachers; -- 重命名表
alter table teachers add age int(3); -- 添加字段 add
alter table teachers modify `name` varchar(30); -- 修改字段数据类型  modify
alter table teachers change `name` `teachername` varchar(30); -- 字段重命名  change
alter table teachers drop age; -- 删除字段
drop table  if exists teachers; -- 删除表

外键(约束)

-- 设置外键
`gradeid` int(2) not null,
constraint `FK_gradeid` foreign key(`gradeid`) references `grade` (`gradeid`);
...

-- 添加外键
ALTER TABLE `school`.`student` ADD INDEX `FK_gradeid_idx` (`gradeid` ASC) VISIBLE;
ALTER TABLE `school`.`student` ADD CONSTRAINT `FK_gradeid`FOREIGN KEY (`gradeid`)REFERENCES `school`.`grade` (`gradeid`)ON DELETE NO ACTION ON UPDATE NO ACTION;

不建议使用外键

insert

INSERT INTO `grade` (`gradename`) VALUES ('大三');
INSERT INTO `school`.`grade` (`gradename`) VALUES ('大二');
insert into `grade` (`gradename`) values('大四'),('大五');
insert into `student`(`name`,`pwd`,`gender`,`birthday`,`address`,`email`,`gradeid`)
values('aaa','123456','男','1999-01-08','地球','123456@qq.com','1');
-- 插入失败id也会自增,处理办法
alter table `student` drop `id`;
alter table `student` add `id` int(5) not null primary key auto_increment first;

update

-- update
update `student` set `name`='张三' where `id`=1;
update `student` set `name`='张三',`address`='中国' where `id`=1;
update `student` set `name`='张三',`address`='中国' where `id` between 1 and 2;
update `student` set `name`='张三',`address`='中国' where `id`=1 and `gradeid`=1;
update `student` set `name`='张三',`address`='中国' where `id`=1 or `gradeid`=1;
update `student` set `birthday`=curdate() where `id`=1;

update关闭安全模式

-- SET SQL_SAFE_UPDATES = 0

delete&truncate

delete from `student` where `id`=1;
delete from `student`;-- 也可以删除一张表
truncate `student`;-- 清空一个表,并且自增计数器归零,不会影响事务

-- 使用delete删除表后,重启数据库
-- innodb:自增列从1开始(内存中,断电即失)
-- myisam:自增列从不会从1开始

select

select * from student;
select `studentno`,`studentname` from `student`;
select `studentno` as '学号',`studentname` as '姓名' from `student`;-- as 别名
select concat('姓名:',`studentname`)as '姓名' from `student`;-- concat()

select * from result;
select `studentno` from `result`;
select distinct `studentno` from `result`;-- 去重
select version();
select 100-99 as'result';
select `studentno`,`studentresult`+99 from `result`;
select `studentno`,`studentresult` from `result` where `studentresult`>=70 and `studentresult`<=90;
select `studentno`,`studentresult` from `result` where `studentresult`between 70 and 90;
select `studentno`,`studentresult` from `result` where not `studentno` =1000;

模糊查询

-- 模糊查询
-- is null
-- is not null
-- between and
-- like %代表多个字符,_代表一个字符
select `studentno`,`studentname` from `student` where `studentname`like '张_';
select `studentno`,`studentname` from `student` where `studentname`like '张&';
select `studentno`,`studentname`,`address` from `student` where `address`like('%北京%') or `address`like('%广东%');
-- in 是一个多个具体的值,不能用%
select `studentno`,`studentname` from `student` where `studentno`in (1000,1001);
-- null,not null
select `studentno`,`studentname` from `student` where `address`='' or `address` is null;-- is not null

联表查询

-- 联表查询
use school;
-- inner join
select `s`.`studentno`,`studentname`,`subjectno`,`studentresult` 
from `student` as `s`, `result` as `r` 
where`s`.`studentno`=`r`.`studentno`;

select `s`.`studentno`,`studentname`,`subjectno`,`studentresult` 
from `student` as `s`inner join`result` as `r` 
on`s`.`studentno`=`r`.`studentno` where `s`.`studentno`=1000;

-- left join
select `s`.`studentno`,`studentname`,`subjectno`,`studentresult`
from `student` as `s`
left join `result` as `r` 
on `s`.`studentno`=`r`.`studentno`;

-- right join
select `s`.`studentno`,`studentname`,`subjectno`,`studentresult`
from `student` as `s`
right join `result` as `r` 
on `s`.`studentno`=`r`.`studentno`;

-- 学号,姓名,科目名,分数
select `s`.`studentno`,`studentname`,`subjectname`,`studentresult`
from `student` as `s`
right join `result` as `r`
on `s`.`studentno`=`r`.`studentno` 
inner join `subject` as `sub`
on `r`.`subjectno`=`sub`.`subjectno`;

自连接

-- 自连接
CREATE TABLE `school`.`category`( 
`categoryid` INT(3) NOT NULL COMMENT 'id', 
`pid` INT(3) NOT NULL COMMENT '父id 没有父则为1', 
`categoryname` VARCHAR(10) NOT NULL COMMENT '种类名字', 
PRIMARY KEY (`categoryid`) 
) ENGINE=INNODB CHARSET=utf8 COLLATE=utf8_general_ci; 

INSERT INTO `school`.`category` (`categoryid`, `pid`, `categoryname`) VALUES ('2', '1', '信息技术');
insert into `school`.`CATEGOrY` (`categoryid`, `pid`, `categoryname`) values ('3', '1', '软件开发');
insert into `school`.`category` (`categoryid`, `PId`, `categoryname`) values ('5', '1', '美术设计');
insert iNTO `School`.`category` (`categoryid`, `pid`, `categorynamE`) VAlUES ('4', '3', '数据库'); 
insert into `school`.`category` (`CATEgoryid`, `pid`, `categoryname`) values ('8', '2', '办公信息');
insert into `school`.`category` (`categoryid`, `pid`, `CAtegoryname`) values ('6', '3', 'web开发'); 
inserT INTO `SCHool`.`category` (`categoryid`, `pid`, `categoryname`) valueS ('7', '5', 'ps技术');
-- 自连接
select `a`.`categoryname` as '父类',`b`.`categoryname` as '子类'
from `category` as `a`,`category` as `b`
where `a`.`categoryid` =`b`.`pid`;

分页和排序

-- 排序 asc, desc
select `s`.`studentno`,`studentname`,`subjectname`,`studentresult`
from `student` `s`
inner join `result` `r`
on `s`.`studentno`=`r`.`studentno`
inner join `subject` `sub`
on `r`.`subjectno`=`sub`.`subjectno`
where `subjectname`='高等数学-1'
order by `studentresult` desc;-- asc

-- 分页 limit startindex(从0开始),pagesize;
select `s`.`studentno`,`studentname`,`subjectname`,`studentresult`
from `student` `s`
inner join `result` `r`
on `s`.`studentno`=`r`.`studentno`
inner join `subject` `sub`
on `r`.`subjectno`=`sub`.`subjectno`
where `subjectname`='高等数学-1'
order by `studentresult` asc
limit 1,1;

嵌套查询、子查询

-- 1.连接查询
select `studentno`,`subjectname`,`studentresult`
from `result` `s`
inner join `subject` `sub`
on `s`.`subjectno`=`sub`.`subjectno`
where `subjectname`='高等数学-1'
order by `studentresult` desc;
-- 2.子查询
select `studentno`,`subjectno`,`studentresult`
from `result`
where `subjectno`=(
	select `subjectno` from `subject`
    where `subjectname`='高等数学-1'
)order by `studentresult` desc;


select `s`.`studentno`,`studentname`
from `student` `s`
inner join `result` `r`
on `s`.`studentno`=`r`.`studentno`
where `subjectno` =(-- in
	select `subjectno` from `subject` where `subjectname`='高等数学-1' -- or `subjectname`='高等数学-2'
);

select `studentno`,`studentname` from `student` 
where `studentno`in (
	select `studentno` from `result` where `studentresult`>10 and `subjectno`=(
		select `subjectno` from `subject` where `subjectname`='高等数学-1'
    )
);

select `s`.`studentno`,`studentname`
from `student` `s`
inner join `result` `r`
on `s`.`studentno`=`r`.`studentno`
inner join `subject` `sub`
on `r`.`subjectno`=`sub`.`subjectno`
where `subjectname`='高等数学-1';

常用函数

数学函数

(1)ABS(x)

返回x的绝对值

(2)PI()

返回圆周率π,默认显示6位小数

(3)SQRT(x)

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

(4)MOD(x,y)

返回x被y除后的余数

(5)CEIL(x)、CEILING(x)

返回不小于x的最小整数

(6)FLOOR(x)

返回不大于x的最大整数

(7)ROUND(x)、ROUND(x,y)

前者返回最接近于x的整数,即对x进行四舍五入;
后者返回最接近x的数,其值保留到小数点后面y位,若y为负值,则将保留到x到小数点左边y位

(8)SIGN(x)

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

(9)POW(x,y)和、POWER(x,y)

返回x的y次乘方的值

(10)EXP(x)

返回e的x乘方后的值

(11)LOG(x)

返回x的自然对数,x相对于基数e的对数

(12)LOG10(x)

返回x的基数为10的对数

(13)RADIANS(x)

返回x由角度转化为弧度的值

(14)DEGREES(x)

返回x由弧度转化为角度的值

(15)SIN(x)、ASIN(x)

前者返回x的正弦,其中x为给定的弧度值;后者返回x的反正弦值,x为正弦

(16)COS(x)、ACOS(x)

前者返回x的余弦,其中x为给定的弧度值;后者返回x的反余弦值,x为余弦

(17)TAN(x)、ATAN(x)

前者返回x的正切,其中x为给定的弧度值;后者返回x的反正切值,x为正切

(18)COT(x)

返回给定弧度值x的余切

(19)RAND()

返回一个0-1之间的随机数

字符串函数

(1)CHAR_LENGTH(str)

计算字符串字符个数

(2)CONCAT(s1,s2,...)

返回连接参数产生的字符串,一个或多个待拼接的内容,任意一个为NULL则返回值为NULL

(3)CONCAT_WS(x,s1,s2,...)

返回多个字符串拼接之后的字符串,每个字符串之间有一个x

(4)INSERT(s1,x,len,s2)

返回字符串s1,其子字符串起始于位置x,被字符串s2取代len个字符

(5)LOWER(str)和LCASE(str)、UPPER(str)和UCASE(str)

前两者将str中的字母全部转换成小写,后两者将字符串中的字母全部转换成大写

(6)LEFT(s,n)、RIGHT(s,n)

前者返回字符串s从最左边开始的n个字符,后者返回字符串s从最右边开始的n个字符

(7)LPAD(s1,len,s2)、RPAD(s1,len,s2)

前者返回s1,其左边由字符串s2填补到len字符长度,假如s1的长度大于len,则返回值被缩短至len字符;前者返回s1,其右边由字符串s2填补到len字符长度,假如s1的长度大于len,则返回值被缩短至len字符

(8)LTRIM(s)、RTRIM(s)

前者返回字符串s,其左边所有空格被删除;后者返回字符串s,其右边所有空格被删除

(9)TRIM(s)

返回字符串s删除了两边空格之后的字符串

(10)TRIM(s1 FROM s)

删除字符串s两端所有子字符串s1,未指定s1的情况下则默认删除空格

(11)REPEAT(s,n)

返回一个由重复字符串s组成的字符串,字符串s的数目等于n

(12)SPACE(n)

返回一个由n个空格组成的字符串

(13)REPLACE(s,s1,s2)

返回一个字符串,用字符串s2替代字符串s中所有的字符串s1

(14)STRCMP(s1,s2)

若s1和s2中所有的字符串都相同,则返回0;根据当前分类次序,第一个参数小于第二个则返回-1,其他情况返回1

(15)SUBSTRING(s,n,len)、MID(s,n,len)

两个函数作用相同,从字符串s中返回一个第n个字符开始、长度为len的字符串

(16)LOCATE(str1,str)、POSITION(str1 IN str)、INSTR(str,str1)

三个函数作用相同,返回子字符串str1在字符串str中的开始位置(从第几个字符开始)

(17)REVERSE(s)

将字符串s反转

(18)ELT(N,str1,str2,str3,str4,...)

返回第N个字符串

(19)INSTR(s,str)

str在s中的首次出现的坐标,不存在则返回0

日期和时间函数

(1)CURDATE()、CURRENT_DATE()

将当前日期按照"YYYY-MM-DD"或者"YYYYMMDD"格式的值返回,具体格式根据函数用在字符串或是数字语境中而定

(2)CURRENT_TIMESTAMP()、LOCALTIME()、NOW()、SYSDATE()

这四个函数作用相同,返回当前日期和时间值,格式为"YYYY_MM-DD HH:MM:SS"或"YYYYMMDDHHMMSS",具体格式根据函数用在字符串或数字语境中而定

(3)UNIX_TIMESTAMP()、UNIX_TIMESTAMP(date)

前者返回一个格林尼治标准时间1970-01-01 00:00:00到现在的秒数,后者返回一个格林尼治标准时间1970-01-01 00:00:00到指定时间的秒数

(4)FROM_UNIXTIME(date)

和UNIX_TIMESTAMP互为反函数,把UNIX时间戳转换为普通格式的时间

(5)UTC_DATE()和UTC_TIME()

前者返回当前UTC(世界标准时间)日期值,其格式为"YYYY-MM-DD"或"YYYYMMDD",后者返回当前UTC时间值,其格式为"YYYY-MM-DD"或"YYYYMMDD"。具体使用哪种取决于函数用在字符串还是数字语境中

(6)MONTH(date)和MONTHNAME(date)

前者返回指定日期中的月份,后者返回指定日期中的月份的名称

(7)DAYNAME(d)、DAYOFWEEK(d)、WEEKDAY(d)

DAYNAME(d)返回d对应的工作日的英文名称,如Sunday、Monday等;
DAYOFWEEK(d)返回的对应一周中的索引,1表示周日、2表示周一;
WEEKDAY(d)表示d对应的工作日索引,0表示周一,1表示周二

(8)WEEK(d)、WEEKOFYEAD(d)

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

(9)DAYOFYEAR(d)、DAYOFMONTH(d)

前者返回d是一年中的第几天,后者返回d是一月中的第几天

(10)YEAR(date)、QUARTER(date)、MINUTE(time)、SECOND(time)

YEAR(date)返回指定日期对应的年份,范围是1970~2069;
QUARTER(date)返回date对应一年中的季度,范围是1~4;
MINUTE(time)返回time对应的分钟数,范围是0~59;
SECOND(time)返回制定时间的秒值

(11)EXTRACE(type FROM date)

从日期中提取一部分,type可以是YEAR、YEAR_MONTH、DAY_HOUR、DAY_MICROSECOND、DAY_MINUTE、DAY_SECOND

(12)TIME_TO_SEC(time)

返回以转换为秒的time参数,转换公式为"3600小时 + 60分钟 + 秒"

(13)SEC_TO_TIME()

和TIME_TO_SEC(time)互为反函数,将秒值转换为时间格式

(14)DATE_ADD(date,INTERVAL expr type)、ADD_DATE(date,INTERVAL expr type)

返回将起始时间加上expr type之后的时间,比如DATE_ADD('2010-12-31 23:59:59', INTERVAL 1 SECOND)表示的就是把第一个时间加1秒

(15)DATE_SUB(date,INTERVAL expr type)、SUBDATE(date,INTERVAL expr type)

返回将起始时间减去expr type之后的时间

(16)ADDTIME(date,expr)、SUBTIME(date,expr)

前者进行date的时间加操作,后者进行date的时间减操作

条件判断函数

(1)IF(expr,v1,v2)

如果expr是TRUE则返回v1,否则返回v2

(2)IFNULL(v1,v2)

如果v1不为NULL,则返回v1,否则返回v2

(3)CASE expr WHEN v1 THEN r1 [WHEN v2 THEN v2] [ELSE rn] END

如果expr等于某个vn,则返回对应位置THEN后面的结果,如果与所有值都不想等,则返回ELSE后面的rn

系统信息函数

(1)VERSION()

查看MySQL版本号

(2)CONNECTION_ID()

查看当前用户的连接数

(3)USER()、CURRENT_USER()、SYSTEM_USER()、SESSION_USER()

查看当前被MySQL服务器验证的用户名和主机的组合,一般这几个函数的返回值是相同的

(4)CHARSET(str)

查看字符串str使用的字符集

(5)COLLATION()

查看字符串排列方式

加密函数

(1)PASSWORD(str)

从原明文密码str计算并返回加密后的字符串密码,注意这个函数的加密是单向的(不可逆),因此不应将它应用在个人的应用程序中而应该只在MySQL服务器的鉴定系统中使用

(2)MD5(str)

为字符串算出一个MD5 128比特校验和,改值以32位十六进制数字的二进制字符串形式返回

(3)ENCODE(str, pswd_str)

使用pswd_str作为密码,加密str

(4)DECODE(crypt_str,pswd_str)

使用pswd_str作为密码,解密加密字符串crypt_str,crypt_str是由ENCODE函数返回的字符串

其他函数

(1)FORMAT(x,n)

将数字x格式化,并以四舍五入的方式保留小数点后n位,结果以字符串形式返回

(2)CONV(N,from_base,to_base)

不同进制数之间的转换,返回值为数值N的字符串表示,由from_base进制转换为to_base进制

(3)INET_ATON(expr)

给出一个作为字符串的网络地址的点地址表示,返回一个代表该地址数值的整数,地址可以使4或8比特

(4)INET_NTOA(expr)

给定一个数字网络地址(4或8比特),返回作为字符串的该地址的点地址表示

(5)BENCHMARK(count,expr)

重复执行count次表达式expr,它可以用于计算MySQL处理表达式的速度,结果值通常是0(0只是表示很快,并不是没有速度)。另一个作用是用它在MySQL客户端内部报告语句执行的时间

(6)CONVERT(str USING charset)

使用字符集charset表示字符串str

聚合函数

-- count();
-- sum();
-- avg();
-- max();
-- min();
select count(`studentno`) from `student`;-- 会忽略字段为null的记录
select count(*) from `student`;-- 不忽略null
select count(1) from `student`;-- 不忽略null
-- group by
select `subjectname`,avg(`studentresult`),max(`studentresult`),min(`studentresult`)
from `result` `r`
inner join `subject` `sub`
on `r`.`subjectno`=`sub`.`subjectno`
group by `subjectname`
having avg(`studentresult`)>30;

数据库MD5加密

use `school`;
set sql_safe_updates = 0;
update testmd5 set pwd=md5(pwd) where char_length(`pwd`)<30;-- 应该插入的时候就加密

事务

-- 事务
-- 事务的原子性(Atomicity):是指一个事务要么全部执行,要么不执行,
-- 也就是说一个事务不可能只执行了一半就停止了。比如你从取款机取钱,这个事务可以分成两个步骤:1划卡,2出钱。不可能划了卡,而钱却没出来。这两步必须同时完成,要么就不完成。
-- 事务的一致性(Consistency):是指事务的运行并不改变数据库中数据的一致性。
-- 例如,完整性约束了a+b=10,一个事务改变了a,那么b也应该随之改变。
-- 独立性(Isolation):事务的独立性也称作隔离性,是指两个以上的事务不会出现交错执行的状态。
-- 因为这样可能会导致数据不一致,更加具体的来讲,就是事务之间的操作是独立的。
-- 持久性(Durability):事务的持久性是指事务执行成功以后,该事务对数据库所作的更改便是持久的保存在数据库之中,不会无缘无故的回滚。

-- mysql默认开启事务
-- 开启事务
set autocommit = 0;
-- 开始事务
start transaction;
-- 提交
commit;
-- 回滚 
rollback;
-- 事务结束
set autocommit = 1;

savepoint 保存点;
rollback to savepoint 保存点;
release savepoint 保存点;

CREATE SCHEMA `shop` DEFAULT CHARACTER SET utf8 ;
use `shop`;
create table `account`(
`id` int(5) not null auto_increment,
`name` varchar(20) not null,
`money` decimal(9,2) not null,
primary key(`id`)
)engine=innodb default charset=utf8;

insert into `account`(`name`,`money`) values('张三',2000),('李四',1000);

-- 转账事务
set sql_safe_updates =0;
set autocommit = 0;-- 关闭自动提交
start transaction;-- 开始事务
update `account` set `money`=`money`-500 where `name`='张三';
update `account` set `money`=`money`+500 where `name`='李四';-- 执行更新后数据就会发生变化
commit;
rollback;
set autocommit = 1;

索引

索引分类:

主键索引(primary key):主键唯一标识

唯一索引(unique key):是指所有记录中字段的值不能重复出现,唯一约束与主键约束相似的是它们都可以确保列的唯一性。不同的是,唯一约束在一个表中可有多个,并且设置唯一约束的列允许有空值,但是只能有一个空值。而主键约束在一个表中只能有一个,且不允许有空值。比如,在用户信息表中,为了避免表中用户名重名,可以把用户名设置为唯一约束。

常规索引(key / index)

全文索引(FullText)

-- 索引
use `school`;
show index from `student`;
-- 添加索引
alter table `student` add unique key `studentname_index`(`studentname`);
-- 删除索引
ALTER TABLE `school`.`student` DROP INDEX `studentname_index` ;

权限管理与备份

-- 用户管理
-- 创建用户
create user 'user_test' identified by '123456';
-- 修改当前用户密码
-- set password =password('123');
-- 修改指定用户密码
-- set password for user_test = password('123456');
-- 修改密码
alter user user_test IDENTIFIED by '666666';
-- 重命名
rename user 'user_test' to 'user_t';
-- 授权
grant all privileges on *.* to 'user_t';
-- 撤销权限
revoke all privileges on *.* from 'user_t';
-- 显示权限
show grants for 'user_t';
show grants for root@localhost;
-- 删除用户
drop user 'user_t';

三大范式

-- 三大范式
-- 第一范式(确保每列保持原子性)
-- 第二范式(确保表中的每列都和主键相关)
-- 第三范式(确保每列都和主键列直接相关,而不是间接相关)

JDBC

上一篇:3、MySQL的数据管理


下一篇:MySQL入门看这一篇就够了