MySQL 高级SQL操作(数据的增删改查)

Posted YuLong~W

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了MySQL 高级SQL操作(数据的增删改查)相关的知识,希望对你有一定的参考价值。

数据新增

1、批量插入

批量插入:是一种优化数据逐条插入的方式

  • 批量插入数据的语法与简单数据插入的语法差不多

  • 批量插入分为两种:

    • 全字段批量插入: insert into 表名 values(值列表1),(值列表2),...(值列表N);
    • 部分字段批量插入(注意字段默认值): insert into 表名 (字段列表) values (值列表1),(值列表2),...(值列表N);
  • 批量插入可以针对性解决数据的批量导入之类的业务,可以一次性解决多条数据插入,能够有效降低客户端占用问题,提升数据操作效率

    • mysql8以后默认有事务安全,即批量要么都成功要么都失败,不会出现部分问题

示例

1、批量插入学生成绩(t_1全字段)

insert into t_1 values(null,'Tom','Computer',90),(null,'Lily','Computer',92);

2、批量插入学生考试信息(t_1不包含成绩)

insert into t_1 (stu_name,course) values('Tony','English'),('Ray','Math');

2、蠕虫复制

蠕虫复制:从已有表中复制数据直接插入到另外一张表(同一张表)

  • 蠕虫复制的目标是快速增加表中的数据

    • 实现表中数据复制(用于数据备份或者迁移)
    • 实现数据的指数级递增(多用于测试)
  • 蠕虫复制语法:insert into 表名 [(字段列表)] select 字段列表 from 表名;

  • 注意事项:

    • 字段列表必须对应上
    • 字段类型必须匹配上
    • 数据冲突需要事先考虑

示例

1、创建一张新表,将t_1表中的数据迁移到新表t_2中

create table t_1(
	id int primary key auto_increment,
    stu_name varchar(20) not null,
    course varchar(20) not null,
    score decimal(5,2)
)charset utf8;

insert into t_2 select * from t_1;

2、快速让t_2表中的数据增长(重复执行)

insert into t_2 (stu_name,course,score) select stu_name,course,score from t_2;

3、主键冲突

主键冲突:在数据进行插入时包含主键指定,而主键在数据表已经存在

  • 主键冲突的业务通常是发生在业务主键上(业务主键本身有业务意义)

  • 主键冲突的解决方案:

    • 忽略冲突:保留原始记录: insert ignore into 表名 [(字段列表)] values(值列表);
    • 冲突更新:冲突后部分字段变成更新: insert into 表名 [(字段列表)] values(值列表) on duplicate key update 字段 = 新值[,字段=新值...];
    • 冲突替换:先删除原有记录,后新增记录: replace into 表名 [(字段列表)] values(值列表); # 效率没有insert高(需要检查是否冲突)
  • 从效率上来讲,insert into不考虑冲突的效率最高,三种解决冲突的方式都会有效率下降(需要检索),其中三种本身的效率依次是:忽略新数据 > 更新部分数据 > 替换全部

示例

1、用户名作为主键的用户注册(冲突不能覆盖):username,password,regtime

create table t_3(
	username varchar(50) primary key,
    password char(32) not null,
    regtime int unsigned not null
)charset utf8;

insert into t_3 values('username','password',12345678);
# 冲突忽略
insert ignore into t_3 values('username','12345678',12345678);

2、用户名作为主键的记录用户使用信息(不存在新增、存在则更新时间):username,logintime

create table t_4(
	username varchar(50) primary key,
    logintime int unsigned
)charset utf8;

insert into t_4 values('username',12345678);	# 当前时间戳

# 冲突更新(替换部分字段数据)
insert into t_4 values('username',12345678) on duplicate key update logintime = unix_timestamp();	# 当前时间戳
  • 如果主键不冲突:新增
  • 如果主键冲突:更新指定字段
  • 上述方式适用于字段较多,但是可能冲突时数据变化的字段较少

3、用户名作为主键的记录用户使用信息(不存在新增、存在则更新全部):username,logintime、clientinfo

create table t_5(
	username varchar(50) primary key,
    logintime int unsigned,
    clientinfo varchar(255) not null
)charset utf8;

insert into t_5 values('username',unix_timestamp(),'{PC:chrome}');

# 替换插入
replace into t_5 values('username',unix_timestamp(),'{phone:uc}');
  • replace遇到主键重复就会先删除、后新增
  • 如果有较多字段需要更新:建议使用替换

数据查询

1、查询选项 distinct

查询选项:用于对查询结果进行简单数据筛选

  • 查询选项是在select关键字之后,有两个互斥值
    • all默认,表示保留所有记录(关键字可以没有)
    • distinct:去重,重复的记录(针对所选字段构成的记录,而不是某个字段)

示例

1、查看商品表中所有品类的商品信息:重复的商品只保留一次(名字、价格、属性都一致)

create table t_6(
	id int primary key auto_increment,
    goods_name varchar(50) not null,
    goods_price decimal(10,2) default 0.00,
    goods_color varchar(20),
    goods_weight int unsigned comment '重量,单位克'
)charset utf8;

insert into t_6 values(null,'mate10',5499.00,'blue',320),
(null,'mate10',5499.00,'gray',320),
(null,'nokia3301',1299,'black',420);

# 考虑所有字段的去重(不含逻辑主键)
select distinct goods_name,goods_price,goods_color,goods_weight from t_6;
select goods_name,goods_price,goods_color,goods_weight from t_6; # 保留所有

# 不考虑颜色去重
select distinct goods_name,goods_price,goods_weight from t_6;
select all goods_name,goods_price,goods_weight from t_6;

2、字段选择&别名 as

字段选择:根据实际需求选择的要获取数据的字段信息

  • 根据实际需求,明确所需要的字段名字,使用英文逗号,分隔

  • 获取所有字段,使用星号*通配所有字段

  • 字段数据可以不一定是来自数据源(select只要有结果即可)

    • 数据常量:select 1
    • 函数或者变量:select unix_timestamp(),@@version (@@是系统变量的前缀,后面跟变量名)
  • 字段的选择只要在保证数据需求能实现的情况下,尽可能少使用*代替(MySQL优化)

    • 减少服务器的数据读取压力
    • 减少网络传输压力
    • 让客户端能够精确解析数据(不用大海捞针)

字段别名:给字段取的临时名字

  • 字段别名使用as语法实现:
    • 字段名 as 别名
    • 字段名 别名
  • 字段别名的目的通常为了保护数据
    • 字段冲突:多张表同时操作有同名字段(系统默认覆盖),想保留全部
    • 数据安全:对外提供数据不使用真实字段名字
  • 字段别名的灵活使用一方面可以保证原始数据的安全,也可以为数据使用者提供便利
    • 同名字段覆盖问题(连表操作学习时会遇到)
    • 原始字段保护
    • 数据字段的简化
  • select是SQL中用于取出数据的一种指令,这种指令未必一定需要从数据表取出数据,只要是本身能够有数据的表达式,都可以使用select获取

示例

1、查询商品信息

# 全部查询
select * from t_6;

# 需求为商品名字和价格
select goods_name,goods_price from t_6;

# 别名使用
select goods_name as gn,goods_price gp from t_6;

2、不需要数据源的数据获取:select的表达式本身能算出结果)

# 获取当前时间戳和版本号
select unix_timestamp() as now,@@version as version,@@version;

3、数据源 from

数据源from关键字之后,数据的来源。只要最终结果是一个二维表,都可以当做数据源

  • 单表数据源:数据源就是一张表 from 表名

  • 多表数据源:数据来源是多张表(逗号分隔) from 表名1,表名2,...表名N

  • 子查询数据源:数据来源是一个查询结果 from (select 字段列表 from 表名) as 别名

    • 数据源要求必须是一个
    • 如果是查询结果必须给起一个表别名
  • 数据表也可以指定别名

    • 表名 as 别名
    • 表名 别名
  • 数据源是为查询、检索提供数据支持的,使用时需要明确指定

  • 通常情况下数据源不会使用简单的多表数据源(笛卡尔积)

  • 数据表的别名在负责SQL查询操作时非常有用,而且有些地方是必须使用(如子查询数据源)

示例

1、单表数据源:最简单的数据源,直接从一个数据表获取

select * from t_7;

2、多表数据源:利用一张表的一条数据匹配另外一张表的所有记录,记录结果为:

  • 记录数 = 表1记录数 * 表2记录数
  • 字段数 = 表1字段数 + 表2字段数(笛卡尔积)
select * from t_7,t_8;

3、子查询数据源:数据来源是一个select对应的查询结果

  • 查询语句需要使用括号包裹
  • 查询结果需要指定别名
select * from (select * from t_7,t_8) t; # 数据有冲突查不出来
select * from (select * from t_7) as t;

4、如果有时候名字较长或者使用不方便,可以利用表别名

select * from t_1 as t;

select t1.*,t2.stu_name from t_1 as t1,t_2 t2;
  • 一般情况下别名设置是为了后续条件中可以直接使用别名
  • 如果多表操作下,可以使用表别名来明确提取表字段

4、where子句

where子句:跟在from数据源之后,对数据进行条件匹配,筛选数据的

  • where是在磁盘读取后,进入内存之前进行筛选
    • 不符合条件的数据不会进入内存
  • where筛选的内容因为还没进入内存,所以数据是没有被加工过
    • 字段别名不能在where中使用

示例

1、查询t_5表中学生为lily的成绩信息

select * from t_5 where stu_name = 'Lily';

2、因为where是在磁盘取数据时进行条件筛选,此时数据没有进入内存,所以字段别名是无效的

# 错误 
select stu_name name,score from t_5 where name = 'Lily';

运算符:用于进行运算的符号

  • 运算符可以用来进行字段数据运算,配合where进行条件筛选
  • 比较运算符
    • >(大于)、<(小于)、=(等于)、>=(大于等于)、<=(小于等于)、<>(不等于)
    • between A and B:A和B之间(A小于B),包括A和B本身(数值比较)
    • in (数据1,数据2,...数据N):在列举的数据之中
    • like 'pattern':像上面样的,用于字符串比较
      • _:单下划线,匹配对应位置的一个任意字符(ab_:ab开头+一个字符,匹配abc,ab1,但不能匹配abcd)
      • %:匹配当前位置(往后)任意数量任意字符(ab%:ab开头+任意数量任意字符,匹配abc,ab1,abcd)
  • 逻辑运算符
    • and(逻辑与)、or(逻辑或)、not(逻辑非)
  • null运算符
    • is null(为空)、is not null(不为空)

示例

1、查询成绩不及格的所有学生信息

# 成绩条件:成绩是数值,又是比大小,可以直接使用比较运算符
select * from t_5 where score < 60;

2、查询成绩在60-90间的学生信息

# 成绩条件:区间60到90,可以有两种解决方案
select * from t_5 where score between 60 and 90;
select * from t_5 where score >= 60 and score <= 90;

3、查询还没有成绩的学生

# 成绩条件:成绩为null,所以不能用比较符号查,只能使用is null实现
select * from t_5 where score is null;

5、group by子句

group by子句:分组统计,根据某个字段将所有的结果分类,并进行数据统计分析

  • 分组的目的不是为了显示数据,一定是为了统计数据
  • group by 子句一定是出现在where子句之后(如果同时存在)
  • 分组统计可以进行统计细分:先分大组,然后大组分小组

聚合函数:

  • 分组统计需要使用统计函数:
    • group_concat():将组里的某个字段全部保留
    • any_value():不属于分组字段的任意一个组里的值
    • count():求对应分组的记录数量
      • count(字段名):统计某个字段值的数量(NULL不统计)
      • count(*):统计整个记录的数量(较多)
    • sum():求对应分组中某个字段是和
    • max()/min():求对应分组中某个字段的最大/最小值
    • avg():求对应分组中某个字段的平均值
  • 分组统计使用数据数据的查询只能依赖统计函数和被分组字段,而不能是其他字段(MySQL7以前可以,不过数据没意义:因为系统只保留组里的第一个)

示例

1、创建一张表,存储学生信息

create table t_0(
	id int primary key auto_increment,
	name varchar(10) not null,gender enum('男','女','保密'),
	age tinyint unsigned not null,
	class_name varchar(10) not null comment '班级名称'
)charset utf8;

insert into t_0 values
(null,'鸣人','男',18,'木叶1班'),(null,'佐助','男',18,'木叶1班'),(null,'佐井','男',19,'木叶2班'),
(null,'大蛇丸','男',28,'木叶0班'),(null,'卡卡西','男',29,'木叶0班'),(null,'小樱','女',18,'木叶1班'),
(null,'雏田','女',18,'木叶1班'),(null,'我爱罗','男',19,'木叶1班'),(null,'向日葵','女',6,'木叶10班'),
(null,'博人','男',8,'木叶10班'),(null,'鼬','男',28,'木叶0班');

2、统计每个班的人数

select count(*),class_name from t_0 group by class_name;

3、多分组:统计每个班的男女学生数量

select count(*),class_name,gender from t_0 group by class_name,gender;

4、统计每个班里的人数,并记录班级学生的名字

select count(*),group_concat(name),class_name from t_0 group by class_name;
select count(*),any_value(name),class_name from t_0 group by class_name;

分组原理:

以统计班级学生为例

以上是关于MySQL 高级SQL操作(数据的增删改查)的主要内容,如果未能解决你的问题,请参考以下文章

mysql的增删改查语句

mysql增删改查语句

mysql 记录的增删改查

连接到sql数据库的c#语言的增删改查

java连接sql数据库,怎么实现增删改查?

Mysql数据的增删改查