【Mysql】33k字带你入门mysql

📝前言:

3w字带你入门mysql

🎬个人简介:努力学习ing
📋个人专栏:Linux
🎀CSDN主页 愚润求学
🌄其他专栏:C++学习笔记,C语言入门基础,python入门基础,C++刷题专栏

一、理解数据库

1. 为什么要有数据库

普通文件存储数据:

  • 安全性不高(无约束)
  • 查询、管理和控制数据不方便

数据库:按照特定数据结构组织、存储和管理数据的仓库

  • 数据库存储的介质:磁盘 / 内存(和文件类似,也都是用来存数据的)

数据库管理系统:用于管理数据库的软件系统(比如MySQL)

  • 为程序员提供了更好管理和控制数据库数据的接口
  • 并且对程序员的插入删除操作做约束,确保数据库中的数据是正确的(即:按一定规则有效组织的)

2. mysql 和 mysqld

mysql是客户端, mysqld是服务端

  • mysql 连接到运行中的 mysqld 服务
  • 我们写的sql语句会被发送到服务端执行
  • 服务端执行完sql语句以后会向客户端返回查询结果或操作反馈

3. 服务器、库和表关系

  • 所谓安装数据库服务器,只是在机器上安装了一个数据库管理系统程序,这个管理程序可以管理多个数据库

  • 一般开发人员会针对每一个应用创建一个数据库。

  • 为保存应用中实体的数据,一般会在数据库中创建多个表,以保存程序中实体的数据

  • 我们创建的一个database在Linux下就是/var/lib/mysql中的一个目录

  • 在某一个 database 里面创建的一个 table 就是在这个目录下的一个文件

  • /var/lib/是 一个存储动态数据的目录,用于存放应用程序运行时生成的持久化数据。(持久化数据就是指存在磁盘这种“非易失”的数据)

4. sql语句的分类

  • DDL【data definition language】 数据定义语言,用来维护存储数据的结构 代表指令: create, drop, alter
  • DML【data manipulation language】 数据操纵语言,用来对数据进行操作 代表指令: insert,delete,update
    • DML中又单独分了一个DQL,数据查询语言,代表指令: select
  • DCL【Data Control Language】 数据控制语言,主要负责权限管理和事务 代表指令: grant,revoke,***mit

5. 存储引擎

存储引擎是:数据库管理系统如何存储数据、如何为存储的数据建立索引和如何更新、查询数据以及并发控制等技术的实现方法。不同引擎如同不同的 “工具”,适用于不同的业务场景。

常用的:MyISAM、 InnoDB

二、mysql 服务登陆和退出操作

1. 启动mysqld

检查mysql的服务端mysqld的运行状态

sudo systemctl start mysql    # 启动服务
sudo systemctl status mysql   # 检查状态

2. 登陆操作

mysql -h 127.0.0.1 -P 3306 -u root -p  # 这个操作登陆的是mysql的客户端,mysqld 应该早被启动好了
  • 参数解读:-h:主机IP,-P:端口号,-u:用户,-p:密码(如果后面不跟密码,则在按回车以后输入)

  • 如果没有写 -h 默认是连接本地

  • 如果没有写 -P 默认是 3306 端口

3. 退出操作

Quit

三、库的操作(DDL)

1. 创建、删除和修改

  • 创建:create database [if exists] 库名 // 这些sql用大写也可以
  • 删除:drop database 库名
  • 修改:alter database 库名 修改内容

上述操作的选项不具体展开讲了

2. 字符集和校验集

  • 字符集:是存入数据的格式规则
  • 校验集:是我们在查询/管理数据时的比较格式规则
    • 每个字符集对应多个校验集,命名格式通常为:字符集名称_语言_比较规则

示例

如在创建database时指定字符集gbk和校验集gbk_chinese_cici表示不区分大小写)

create database data1 charset=gbk collate=gbk_chinese_ci;

show databases;查看当前库:

show create database data1可以查看创建data1时的语句和配置信息(把;换成\G可以让查看的结果更好看一点)

在该库下创建的表会默认使用该库的字符集和校验集

3. 使用库

使用库

use databasename

查看当前在哪个库

select database();

4. 备份和还原

备份:其实就是把在该库中执行过的所有指令记录在了一个文件里

mysqldump -P 3306 -u root -p 密码 -B 数据库名 > 数据库备份存储的文件路径

如:将data1库备份到文件(这条语句要在mysql外,即:Linux终端执行)

mysqldump -P 3306 -u root -p -B data1 > /var/lib/mysql/mytest.sql;

还原(在mysql里面执行),当然也可以在外面还原(不过不是这个命令)

source /var/lib/mysql/mytest.sql;

注意事项

  • 如果备份一个数据库时,没有带上-B参数, 在恢复数据库时,需要先创建空数据库,然后使用数据库,再使用source来还原

  • 如果备份的是数据库中的一张表

    mysqldump -u root -p 数据库名 表名1 表名2 > 备份存储的文件路径
    
  • 同时备份多个数据库

    mysqldump -u root -p -B 数据库名1 数据库名2 ... > 备份数据库存放路径
    

5. 查看连接情况

show processlist

四、表的操作(DDL)

1. 创建表

CREATE TABLE table_name (
	field1 datatype,
	field2 datatype,
	field3 datatype
);
  • 表名区分大小写
  • field:列名(默认不区分大小写)
  • datatype:数据类型
  • 也可以在创建表的时候设置表的字符集和校验集,如果没有就使用库的
  • 也可以设置表引擎(默认继承库的引擎),不同的存储引擎,创建表的文件不一样

create table table_name like xxx表:可以创建和xxx表一样表结构的空表

销毁表也是drop

2. 查看表结构

desc 表名

3. 修改表

也是使用alter

  • 往表里新增列
ALTER TABLE tablename ADD (column datatype [DEFAULT expr][,column
datatype]...);
  • 对已有列进行修改(column是列名)
ALTER TABLE tablename MODIfy (column datatype [DEFAULT expr][,column
datatype]...);

ADDMODIFY后面跟的语句就跟我们创建表的时候定义列一样(语句要完整)

  • 删除列
ALTER TABLE tablename DROP (column);
  • 修改表名
alter table tablename rename to newtablename;
  • 修改列名
alter table tablename change col_name new_col_name varchar(60); -- 新字段需要完整定义

五、数据类型

数据类型是一种约束,不符合数据类型的数据无法插入表,凡是表中的数据一定是符合约束的数据

1. 数据类型分类

2. 数值类型

  • 越界插入就会报错

  • 使用的时候,在类型名后加unsigned就说明是无符号的了

3. bit类型

  • 以二进制方式存储,每一位存储01

  • bit[(M)] : 位字段类型。M表示bit位个数,范围从1到64。如果M被忽略,默认为1。

  • 我们往bit类型插入的数据本质上是ASCII值

示例:

4. 小数类型

float[(m, d)] [unsigned] : M指定显示长度,d指定小数位数(每个位的取值范围是0-9),占用空间4个字节
  • 严格模式:要求小数位数必须为 d 位,所有位数加起来 <= m

  • 非严格:小数位会四舍五入

  • unsigned类型是直接把负数范围给去掉,只保留正数(且正数不会扩展)

float会有精度丢失问题,想用高精准的可以用decimal(牺牲空间换取精确性)

decimal(m, d) [unsigned] : 定点数m指定长度,d表示小数点的位数

5. 字符串类型

char(L): 定长字符串L是最大长度限制(单位字符)最大长度值(字符数)可以为255

varchar(L): 变长字符串L是最大长度限制

注意

  • 行长度限制:65,535 字节
  • 一个中文汉字,占三个字节(和C不一样),但是只算一个字符

区别

  • char每个字符串都开固定的L个字符
  • varchar根据字符串实际占用来分配大小(但是需要额外的1-2个字节来存储该字符串长度)。
特性 CHAR(L) VARCHAR(L)
长度单位 字符(最大 255,特殊情况扩展) 字符(最大受行长度和字符集限制)
存储方式 固定长度,右侧补空格 可变长度,需 1-2 字节存储长度
空间效率 适合长度固定的数据(如 UUID) 适合长度可变的数据(如用户名)
性能 读写更快(无需计算长度) 节省空间,但需额外开销

6. 时间类型

  • date :日期 'yyyy-mm-dd' ,占用三字节
  • datetime 时间日期格式 'yyyy-mm-dd HH:ii:ss' ,占用八字节
  • timestamp :时间戳,从1970年开始的 yyyy-mm-dd HH:ii:ss 格式和 datetime 完全一致,占用
    四字节(设置自动更新:ON UPDATE CURRENT_TIMESTAMP,当我们插入或更新记录,该记录的timestamp会自动更新)

示例

mysql> create table t2(
    -> d1 date,
    -> d2 datetime,
    -> d3 timestamp ON UPDATE CURRENT_TIMESTAMP
    -> );

7. enum和set

  • enum:多选一,set:多选多(可以不选)
  • 选择enum中的元素可用数组下标代替,下标从1开始
  • 选择set中的元素可用位图代替:如有五个元素:3 == 00011(相当于选前两个),5 == 00101(相当于选第一个和第三个)

find_ in_ set

我们在查询的时候where=xxx是绝对查询,如果我们想要查询在set里选了某个元素的记录可以用find_in_set

  • find_in_set(sub,str_list) :如果 substr_list 中,则返回下标;如果不在,返回0

  • str_list 是用逗号分隔的字符串。

select * from votes where find_in_set('登山', hobby); 

则记录在hobby这一列有选'登山'的就会被选出来。

六、约束

创建表时,我们可以后跟约束,来对要插入的数据进行规范(约束)

1. NULL

  • NULL不等于’ '(空串),空串代表有内容,内容为空串。但是 NULL代表无内容,无意义。

    • NULL 的运算结果(比如 + / - )也是NULL(无意义)
  • 当列不指明为 not null,插入时,如果省略该列,则:默认为 NULL(因为 DEFAULT 为 NULL)

  • 当列指明为 not NULL,我们就不能显式的插入 NULL。且如果插入时省略该列,必须要求设置了 DEFAULT 值,否则会报错(因为原来的DEFAULT 的 NULL 不适用了)

2. DEFAULT

缺省值,设置以后,如果插入时省略该列,就自动用DEFAULT 值。

语法:

create table t1(
	id int defult 10, # 设置缺省值 10
    name varchar(10) not null# 设置 not null 约束
	);

3. ***ment

列描述:***ment,不是强约束,只是描述字段,会根据表创建语句保存,用来给程序员或DBA 来进行了解

使用 desc看不到,用show可以看到具体的创表语句。

4. zerofill

  • 我们在创表的时候,使用 int 类型后面可以带数字,来设置显示宽度(但是不能超过取值范围)

  • MySQL 会用默认的显示宽度(一般是 INT(11)

我们在显示的时候,如果想要填满宽度,可以设置 zerofill

  • 当原数据位数不够时,左边补 0,
  • 如果够了 / 超了原来设置的宽度,则正常显示

示例:

# 创建
create table t5(
    -> id int(5) zerofill ***ment "这是一个有zerofill的列"
    -> );
    
# 插入
insert t5 values(10),(10000000);

查询结果:

5. 主键

  • 一张表中只能有一个主键,主键可以是由单个属性构成的(即单列),也可以有多列共同构成(复合主键,此时把多列当一个整体)

  • 主键用来标识记录的唯一性,任意两条记录的主键不能相同(即同一列上不能有相同的单元格,如果是单列构成的主键的话)

  • 主键不能为空

设置方式:

  1. 在创建的时候后跟:primary key

  2. 当表创建好以后但是没有主键的时候,可以再次追加主键

    alter table 表名 add primary key(字段列表) # 字段列表即:(id)单列 或者 (id, name)多列组合
    
  3. 删除主键

    alter table 表名 drop primary key;
    

6. auto_increment

  • 自增长:当对应的字段不给值,系统会从当前字段(列)中已经有的最大值 +1操作,得到一个新的不同的值设置在该位置
  • 通常和主键搭配使用,作为逻辑主键(能够确保唯一性)

auto_increment的特点:

  • 任何一个字段要做自增长,前提是本身是一个索引(key一栏有值)
  • 自增长字段必须是整数
  • 一张表最多只能有一个自增长

示例:

# 建表
mysql> create table t6(
    -> id int primary key auto_increment, # 设置 primary key 和 auto_increment
    -> name varchar(10) unique   # 设置唯一键(下面有介绍)
    -> );
# 插入
insert t6(name) values('Tom'),('Jim'),('Sam');

结果:

如果你企图往这两个有唯一性的列再插入相同数据:

7. 唯一键

unique标记

  • 和主键一样,该列不能出现重复数据。

  • 意义:主键用来标识记录的唯一性,但是一条记录的其他单元格也可能需要确保唯一性(比如:身份证做主键,但是电话号码也要有唯一性)

  • 唯一键可以为空

8. 外键

  • 外键是表与表之间的一种连接机制,用于建立和强制两个表中数据的参照完整性,通过外键,可以确保一个表中的数据在另一个表中存在对应记录,从而维护数据的一致性。
  • 外键约束主要定义在从表上,主表则必须是有主键约束或 unique 约束。
  • 当定义外键后,要求外键列数据必须在主表的 unique 列存在 或 该外键列数据为 null。

语法:

foreign key (字段名) references 主表(列)

示例:

学生表是从表,班级表是主表。我们可以通过stu表的class_id关联到myclass中唯一的班级id,进而得到有且唯一的班级name

insert into stu values(103, '王五',30);# 创建 myclass 表(主表)
create table myclass (
    -> id int primary key,
    -> name varchar(30) not null ***ment'班级名'
    -> );
# 创建 stu 从表
create table stu (
    -> id int primary key,
    -> name varchar(30) not null ***ment '学生名',
    -> class_id int,
    -> foreign key (class_id) references myclass(id) # 该从表的 class_id 这一列 受到 id 的约束
    -> );
# 合理插入数据
insert into myclass values(10, 'C++大牛班'),(20, 'java大神班');
insert into stu values(100, '张三', 10),(101, '李四',20);
# 非法插入数据
insert into stu values(103, '王五',30); # 因为没有 id 为 30 的班级(这就是一种约束,交给 mysql 检查)
# 但是可以外键为NULL(如果外键字段允许为 NULL 的话)
insert into stu values(103, '赵六',NULL);

七,表的增删改查(DML)(重点)

CURD : Create(创建), Update(更新),Retrieve(读取),Delete(删除)

1. create

create是从语义上来的,指的是Insert操作,Insert插入数据就是 → 创建用户

1.1 Insert

有:单行全列,单行指定列,多行全列,多行指定列…

INSERT [INTO] table_name [(column [, column] ...)] # 不指定列就是全列
VALUES (value_list) [, (value_list)] ... # 多列之间用 , 分割
value_list: value, [, value] ...

示例

insert stu values(1, 'Tom', 68,75,80), (2,'Jim',80,59,96),(3,'Sam',98,95,93);

insert也可以直接插入表(查询结果后的表也行),反正合理就行。(mysql下一切皆表)

INSERT INTO table_name [(column [, column ...])] SELECT ... # 插入select的查询结果表
1.2 插入否则更新

我们的insert可能由于主键或唯一键的值已经存在而导致插入失败,这时候我们可以用 UPDATE来同步更新(如果存在的话)

INSERT ... ON DUPLICATE KEY UPDATE column = value [, column = value] ... # duplicate 单词意思:重复
  • 如果这个 KEY 不存在,直接 INSERT
  • 如果 KEY 存在,则执行 update(把原来数据覆盖)

示例

insert into stu values(3, 'David',88,75,23) on duplicate key update sno = 4, name = 'David', Chinesenese = 88, Math = 75, English = 23;
# 结果:Query OK, 2 rows affected (0.01 sec)  : 有数据冲突(键存在),然后更新(即:1. 检测到冲突,2. 更新)

直接把原来冲突的数据记录给覆盖式更新了。

# 还可能出现:
# 1 rows affected : 没有数据冲突(主键不存在),直接 INSERT(即:1. 插入一条新数据)
# 0 rows affected : 主键存在且 update 后的内容和原来表中数据一样,mysql会认为没有冲突(即,无冲突 无插入)
1.3 replace
-- 主键 或者 唯一键 没有冲突,则直接插入;
-- 主键 或者 唯一键 如果冲突,则删除后再插入
replace into stu values(5,'Sam', 93, 92, 93);
# 结果:Query OK, 1 row affected (0.01 sec)

-- 1 row affected: 表中没有冲突数据,数据被插入
-- 2 row affected: 表中有冲突数据,删除后重新插入

2. update

对查询到的列值更新

语法

UPDATE table_name SET column = expr [, column = expr ...] # 选择指定的列更新
[WHERE ...] [ORDER BY ...] [LIMIT ...] # 后面也可以跟判断语句...(来选择具体的行)(如果不带的话,全列都会被更新,很危险)

示例(把David的English成绩改成43

update stu set English = 43 where name = 'David';

3. retrieve(select)

  • SELECT查询,除了基本的查询,还可以作为函数的触发语句,select 会执行后面的表达式
    • 单独的函数不是完整的 sql 语句(需要由select来触发)
  • select会将函数返回的结果转换成查询结果,然后把查询结果按照结果集(表格形式)展示。select的结果依然是一张表,依然可以被使用。
  • 查询方式:1. *全列查询; 2. 指定列查询
  • as可以给查询后的结果取别名(不会影响原表,只是对展示结果影响)
  • select 后跟DISTINCT 可以对查询结果去重(不会影响原表)

基本用法

select * from stu; # 全列查询
select sno, name from stu; # 指定列查询

给查询结果指定别名:

SELECT column [AS] alias_name [...] FROM table_name;

from 后面也可以重命名,并且因为 from 的语句执行在前,所以select中可以用 stu2 这个新表名

4. delete

4.1 delete
DELETE FROM table_name [WHERE ...] [ORDER BY ...] [LIMIT ...] # 用来删除记录,如果不筛选则是整表内容删除,但是表结构还在(即:表为空)
4.2 TRUNCATE

截断表

TRUNCATE [TABLE] table_name  # 只能删除整表,不能用where筛选单条记录
  • 比 delete 更快
  • 但是,删除数据的时候,并不经过真正的事物,所以无法回滚
  • 会重置 AUTO_INCREMENT 项

5. where

进行条件筛选,select可用,update也可用…,合理就行。

比较运算符

运算符 说明(MySQL 环境下)
>, >=, <, <= 大于,大于等于,小于,小于等于
= 等于,NULL 不安全,例如 NULL = NULL 的结果是 NULL
<=> 等于,NULL 安全,例如 NULL <=> NULL 的结果是 TRUE (1)
!=, <> 不等于
BETWEEN a0 AND a1 范围匹配,[a0, a1],如果 a0 <= value <= a1,返回 TRUE (1)
IN (option, …) 如果是 option 中的任意一个,返回 TRUE (1)
IS NULL 是 NULL
IS NOT NULL 不是 NULL
LIKE 模糊匹配% 表示任意多个(包括 0 个)任意字符;_ 表示任意一个字符

注意:

  • 等于是一个=
  • mysql的字符串支持使用=直接比较
  • NULL 本身代表无内容,无意义,最好用 is NULL / is not null 比较

逻辑运算符(和 C 差不多)

AND 多个条件必须都为 TRUE (1),结果才是 TRUE (1)
OR 任意一个条件为 TRUE (1),结果为 TRUE (1)
NOT 条件为 TRUE (1),结果为 FALSE (0)

6. order by

order by 用于对查询后的结果排序

语法

-- ASC 为升序(从小到大)
-- DESC 为降序(从大到小)
-- 默认为 ASC
SELECT ... FROM table_name [WHERE ...]ORDER BY column [ASC|DESC], [...]; # order by 中也可以用表达式

注意:

  • 没有 ORDER BY 子句的查询,返回的顺序是未定义的,永远不要依赖这个未定义的顺序

  • NULL 视为比任何值都小

**LIMIT **筛选结果

语法:

LIMIT [偏移量,] 行数  # 或者:LIMIT 行数 OFFSET 偏移量;
  • LIMIT n,默认从第 0 条记录开始,筛选 n 条记录
  • LIMIT 3 OFFSET 2,从第 2 条记录开始(offset从 0 开始记录),展示 3 条记录

示例

# 错误示范
select sno, name, Chinese+Math+English total from stu where total > 240 order by total asc;
# 报错:Unknown column 'total' in 'where clause'
# 因为执行顺序为:1. from; 2. where; 3. select (这时候才设置好 total 别名); 4. order by

# 正确写法
select sno, name, Chinese+Math+English total from stu where Chinese+Math+English > 240 order by
total asc;

结果:

我们可以用降序,然后加一个limit 1筛选出第一名

7. 聚合函数

函数 说明(MySQL 聚合函数)
COUNT([DISTINCT] expr) 返回查询结果集中的记录数(处理 NULL 值) 示例COUNT(id) 统计 id 列的非空值数量
SUM([DISTINCT] expr) 计算数值列的总和(忽略 NULL 值) 示例SUM(sales) 计算销售总额
AVG([DISTINCT] expr) 计算数值列的平均值(忽略 NULL 值) 示例AVG(price) 计算平均价格
MAX([DISTINCT] expr) 返回列中的最大值(支持数值、字符串、日期类型) 示例MAX(date) 返回最新日期
MIN([DISTINCT] expr) 返回列中的最小值(支持数值、字符串、日期类型) 示例MIN(score) 返回最低分数

常用于把同一组内的数据进行统计(都不区分大小写)。一般结合group by

示例:

select MIN(Math) from stu; # 把整张表当成一组来聚合

结果:

但是:

select name, MIN(Math) from stu; # 错误,name 各不相同,无法聚合成一个值

8. group by

先分组,再聚合!

示例

准备工作,创建一个雇员信息表(来自 oracle 9i 的经典测试表) ,可以让 AI 生成,省得找

  • EMP员工表
  • DEPT部门表
  • SALGRADE工资等级表

需求1:显示每个部门的平均工资和最高工资

select DEPTNO, AVG(SAL) avg_sal, MAX(SAL) from EMP group by DEPTNO;

需求2:显示每个部门的每种岗位的平均工资和最低工资

select DEPTNO, JOB, AVG(SAL) avg_sal, MIN(SAL) min_sal from EMP group by DEPTNO, JOB order by DEPTNO, JOB;
# order by 将结果排序

需求3:显示平均工资低于 2000 的部门和它的平均工资

select DEPTNO, AVG(SAL) avg_sal from EMP group by DEPTNO having avg_sal < 2000;
# having 也是条件筛选(是分组后过滤,即:最后执行,所以才可以使用新列名 avg_sal )
# where 是分组前筛选,反正:group by 就用 having,外面用 where

9. 各个关键字执行顺序

SQL查询中各个关键字的执行先后顺序:

from > on> join > where > group by > with > having > select > distinct > order by > limit

八、内置函数

1. 时间函数

函数名称 功能
current_date() 返回当前日期
current_time() 返回当前时间
current_timestamp() 返回当前时间戳(日期 + 时间)
date(datetime) 返回 datetime 参数的日期部分
date_add(date, interval d_value_type) 在 date 中添加日期或时间,interval 后的数值单位可以是:year、minute、second、day
date_sub(date, interval d_value_type) 在 date 中减去日期或时间,interval 后的数值单位可以是:year、minute、second、day
datediff(date1, date2) 两个日期的差,单位是天
now() 当前日期时间
  • 可以理解为所有的时间函数底层用的都是同一个函数,只是输出结果不同。

  • 字符串符合YYYY-MM-DD HH:MM:SS这种标准格式,插入timestamp类型会自动发生转换,插入date类型(会截取日期部分)…也会(有点像隐式类型转换)。

2. 字符串函数

函数名称 功能
charset(str) 返回字符串字符集
concat(string2 [, …]) 连接字符串
instr(string, substring) 返回 substring 在 string 中出现的位置(从 1 开始),没有返回 0
ucase(string2) 转换成大写
lcase(string2) 转换成小写
left(string2, length) 从 string2 中的左边起取 length 个字符
right(string2, length) 从 string2 中的右边起取 length 个字符(还是正序的)
length(string) string 的长度
replace(str, search_str, replace_str) 在 str 中用 replace_str 替换 search_str
strcmp(string1, string2) 逐字符比较两字符串大小
substring(str, position [, length]) 从 str 的 postion 开始,取 length 个字符
ltrim(string)、rtrim(string)、trim(string) 去除前空格或后空格

3. 数学函数

abs(number) 绝对值函数
bin(decimal_number) 十进制转换二进制
hex(decimalNumber) 转换成十六进制
conv(number, from_base, to_base) 进制转换
ceiling(number) 向上去整
floor(number) 向下去整
format(number, decimal_places) 格式化,保留decimal_places位小数
hex(decimalNumber) 转换成十六进制 (注:与前面函数名重复,可能是表格录入失误 )
rand() 返回随机浮点数,范围 [0.0, 1.0)
mod(number, denominator) 取模,求余
  • ceiling:4.1 取 5,-3.2 取 - 3
  • floor:3.8 取 3, -1.3 取 - 2
  • MySQL 里 MOD(a, b) 运算结果符号遵循 a - (b × FLOOR(a / b)) 的计算逻辑
  • rand() * 100,即得到范围为[0.0, 100.0)的随机数

4. 其他函数

函数名称 功能描述 示例及结果展示
user() 查询当前用户 select user(); 执行后返回当前数据库连接的用户信息,如 root@localhost(因环境而异 )
md5(str) 对一个字符串进行 MD5 摘要,摘要后得到一个 32 位字符串 select md5('admin'); 结果如 21232f297a57a5a743894a0e4a801fc3
database() 显示当前正在使用的数据库 select database(); 若当前使用数据库为 test_db,则返回 test_db(无当前库则返回 NULL
ifnull(val1, val2) 如果 val1null,返回 val2,否则返回 val1 的值 select ifnull('abc', '123'); 返回 abc select ifnull(null, '123'); 返回 123
  • 密码通常不同用明文保存,要加密,比如用md5()转换

九、复合查询(重点)

  • 子查询,即:嵌套查询。我们可以把select语句(或其他语句)的结果也当做一张表来嵌套在其他sql语句(fromwhere…都可以)。

  • 多表查询:当我们需要的数据不只来源于一张表时,就会用到多表查询,

    • select * from table1, table2,同时选两张表,两张表做的是笛卡尔积运算,假如table1n个元素,table2m个,select出的结果有n * m个元素(table1的每一行都与table2进行拼接)【但是有一说一,笛卡尔积 + where 不好用,后面会介绍内外连接更好用】
    • 笛卡尔积的结果中可能存在很多无效信息,我们可以通过where来筛选
  • 自连接:在同一张表内嵌套查询

示例(还是继续用oracle 9i 的经典测试表)

1. 基本查询练习

  • 查询工资高于500或岗位为MANAGER的雇员,同时还要满足他们的姓名首字母为大写的J

    select ENAME, SAL, JOB from EMP where (SAL>500 or JOB='MANAGER') and ENAME like 'J%';
    
  • 使用年薪进行降序排序

    select ename, sal*12 + ifnull(***m,0) "年薪" from EMP order by 年薪 desc;
    
  • 显示工资最高的员工的名字和工作岗位

    # 错误示范(where 子句是在分组和聚合前执行的,MAX(sal)还没有结果)
    select ename, job from EMP where sal=MAX(sal);
    # 我们可以嵌套一个子查询 select 先把结果算出来(相当于我们把整张表作为一个分组,然后用聚合函数得到一个MAX(sal)结果)
    select ename, job from EMP where sal=(select MAX(sal) from EMP);
    
  • 显示工资高于平均工资的员工信息

    select * from EMP where sal>(select AVG(sal) from EMP);
    
  • 显示每个部门的平均工资和最高工资

    select DEPTNO, avg(sal), max(sal) from EMP group by DEPTNO;
    
  • 显示平均工资低于2000的部门号和它的平均工资

    select DEPTNO, avg(sal) from EMP group by DEPTNO having avg(sal) < 2000;
    
  • 显示每种岗位的雇员总数,平均工资

    select job, count(*), avg(sal) from EMP group by job;
    

2. 多表查询练习

  • 显示雇员名、雇员工资以及所在部门的名字

    select ename, sal, dname from DEPT, EMP where DEPT.deptno= EMP.deptno;
    
  • 显示部门号为10的部门名,员工名和工资

    select dname, ename, sal from DEPT, EMP where DEPT.deptno= EMP.deptno and EMP.deptno=10;
    
  • 显示各个员工的姓名,工资,及工资级别

    # 选择了多表以后,我们就拥有了两张表的列字段,再通过 where 来删选记录
    select ename, sal, grade from SALGRADE, EMP where sal < hisal and sal >= losal;
    

3. 自连接练习

  • 显示员工FORD的上级领导的编号和姓名(mgr是员工领导的编号–empno)

    • 方法1(使用子查询)

      select empno, ename from EMP where empno=(select mgr from EMP where ename='FORD');
      # 子查询选出领导编号,然后再在表中选出标号和领导编号相同的人的编号和姓名
      
    • 方法2(使用多表查询,自连接)

      # 自连接必须要给同一张表取别名来区分不同实体
      select leader.empno, leader.ename from EMP leader, EMP worker where worker.mgr=leader.empno and worker.ename = 'FORD';
      

4. 子查询练习

4.1 单行子查询(返回一行记录的子查询)
  • 显示SMITH同一部门的员工

    select ename from EMP where job=(select job from EMP where ename='SMITH');
    
4.2 多行子查询(返回多行记录的子查询)

常用关键字:1. in:属于(也支持not in); 2. all:和全部比较,全称量词; 3. any:和任意比较,存在量词

  • 查询和10号部门的工作岗位相同的雇员的名字,岗位,工资,部门号,但是不包含10自己的

    select ename, job, sal, deptno from EMP where (job in (select job from EMP where deptno=10)) and deptno != 10;
    
  • 显示工资比部门30的所有员工的工资高的员工的姓名、工资和部门号

    select ename, sal, deptno from EMP where sal > all(select sal from EMP where deptno=30);
    
  • 显示工资比部门30的任意员工的工资高的员工的姓名、工资和部门号(包含自己部门 的员工)

    select ename, sal, deptno from EMP where sal > any(select sal from EMP where deptno=30);
    
4.3 多列子查询(返回多列的子查询)
  • 查询和SMITH的部门和岗位完全相同的所有雇员,不含SMITH本人
4.4 在from子句中使用子查询(把select的结果当做一张表来使用)
  • 显示每个高于自己部门平均工资的员工的姓名、部门、工资,以及部门的平均工资

    select ename, deptno, sal, avg_sal 
    from EMP, (select deptno dt, AVG(sal) avg_sal from EMP groupby deptno) tmp  # 临时表生成每个部门的平均工资
    where EMP.sal > tmp.avg_sal and EMP.deptno=tmp.dt
    
  • 查找每个部门工资最高的人的姓名、工资、部门

    select ename, sal, deptno from (select deptno dt, MAX(sal) ms from EMP group by deptno) tmp, EMP where EMP.sal=tmp.ms;  # 在临时表中对列进行取别名是为了防止在外层select的时候有歧义
    
4.5 合并查询

合并多个select的执行结果,可以使用集合操作符 union,union all(纵向拼接)

union:用于取得两个结果集的并集,会自动去掉结果集中的重复行

  • 将工资大于2500或职位是MANAGER的人找出来

    select ename, sal, job from EMP where sal > 2500 
    union select ename, sal, job from EMP where job='MANAGE='MANAGER';
    

union all:取得两个结果集的并集,不会去掉结果集中的重复行

  • 将工资大于2500或职位是MANAGER的人找出来

    select ename, sal, job from EMP where sal > 2500 
    union all select ename, sal, job from EMP where job='MANAGER';
    

十、内外连接(重点)

内外连接就是在提前做好筛选的笛卡尔积

1. 内连接

select 字段 from 表1 inner join 表2 on 连接条件 [and 其他条件];

对 表1 和 表2 的笛卡尔积做连接条件的删选(我们上面的复合查询都可以用内连接来优化)

示例(显示SMITH的名字和部门名称):

select ename, dname from EMP join DEPT on DEPT.deptno=EMP.deptno where ename='SMITH';
# 两个表连接的时候确保是有意义的,即:仅把和自己部门号对应的部门的名字拼接到自己的记录上

2. 外连接

语法(分为左外连接和右外连接)

select 字段 from 表1 left(或者 right) join 表2 on 连接条件 [and 其他条件];

左外连接:确保左表的记录都被保留,如果和右表匹配不上的记录,字段内容可为空。(即:以左表为基础,把右表拼过来的感觉),右外连接同理。

示例(列出部门名称和这些部门的员工信息,没有员工的部门也要列出)

# 方法一(左外连接):
select dname, ename from DEPT d left join EMP e on d.deptno=e.deptno;
# 方法二(右外连接):
select ename, dname from EMP right join DEPT on DEPT.deptno=EMP.deptno;

十一、索引(重点)

1. mysql与存储

  • 我们的 CURD 操作需要通过计算找到对应的插入位置(因为要用磁盘的LAB地址),涉及计算就需要CPU,就需要把数据加载到内存里(因为冯诺依曼体系结构)

  • mysql是一个软件,mysql 服务器在内存中运行的时候,会申请大内存空间 Buffer Pool 用来进行各种缓存。当数据加载到mysql,需要:磁盘 → OS → mysql 客户端(缓冲区)。

  • 当我们查找一个数据的时候,要 IO。假如查找一条 IO 把数据从磁盘加载到缓冲区一次,那太慢了。为了减少 IO 的次数(IO影响效率的是IO的次数,不是IO单次的数据量),mysql中数据的存储 / 读取,也是以 页(page) 为单位的(大小 4 * 4 KB)。(InnoDB 引擎存储)

2. 理解索引

没有索引:

  1. 第一步:定位表的数据范围

    • MySQL 通过表名找到对应的数据文件(如.ibd),确定该表在磁盘上的物理存储范围
  2. 第二步:按顺序访问数据页

    • 从第一个数据页开始,检查页是否在 Buffer Pool 中(如果不在:触发磁盘 IO 加载该页到 Buffer Pool)

    • 遍历页内记录(检查/加载 一页,遍历一页)

  3. 时间复杂度: O ( n ) O(n) O(n)

有索引:索引就像书本的目录,先找索引,一次淘汰多页(利用索引的有序性)。时间复杂度: O ( l o g n ) O (log n) O(logn)(在 mysql 中通常用 B+ 树来作为索引数据结构)

  • 单个page中的记录(如果有索引,记录中会存放索引)用链表组织起来,page内会存在页内目录。

  • page和page之间用链表链接起来

3. 索引原理

  • 索引本质就是 mysql 的一种数据结构,mysql 中用B+ 树来存储索引。

  • 索引可以提高效率,但是会降低 insert/update等操作的效率(并且会额外使用空间存储 B+ 树,空间换时间)

  • 为了迅速寻找page,可以使用多级目录(目录页的本质也是页,普通页中存的数据是用户数据,而目录页中存的数据是普通页的地址

  • 目录页只放各个下级page的最小键值(其实也就是指向下级page的地址)

  • 查找的时候,自顶向下找,只需要加载部分目录页到内存,即可完成算法的整个查找过程,大大减少了IO次数

B+ vs B

B树

B+ 树(一个索引一颗 B+ 树)

  • B树节点,既有数据,又有Page指针。而B+,只有叶子节点有数据,其他目录页,只有键值和Page指针
  • B+叶子节点,全部相连,而B没有

选择 B+ 的优点

  • 节点不存储data,只用于导航,这样一个节点就可以存储更多的key。可以使得树更矮,所以IO操作次数更少。
  • 叶子节点相连(且按索引值升序排序,形成有序链表,即:创建索引的时候就是自动有序的),更便于进行范围查找
    • 哈希表存储不支持范围查找

聚簇索引 VS 非聚簇索引

  • InnoDB这种直接在叶子节点上存数据的,叫做聚簇索引。(MyISAM:在叶子节点处存放数据的地址,叫做非聚簇索引)

  • 对于 MyISAM ,建立辅助(普通)索引和主键索引没有差别,无非就是主键不能重复,而非主键可重复

  • 主键索引(讨论的是 InnoDB)

    • 叶子节点:存储 整行数据(包括所有列),{索引值 : 数据}

    • 非叶子节点:存储 {索引值:指向子节点的指针}

  • 非主键索引

    • 叶子节点:存储索引键值对应的主键值,即:{普通索引值:主键值}(用于回表)
    • 非叶子节点:存储{普通索引值:指向叶子节点的指针}
    • 所以通过辅助(普通)索引,找到目标记录,需要两遍索引:首先检索辅助索引获得主键,然后用主键到主索引中检索获得记录。这种过程,就叫做回表查询

4. 索引操作

1. 添加主键 / 唯一键索引

方法一

create table t1(
    id int primary key,        # mysql 会针对主键, 自动创建主键索引
    name varchar(20),
    tele varchar(11) unique    # mysql 针对唯一键, 自动创建唯一键索引
);

方法二

-- 在表创建最后指明主键和唯一键
create table t1(
	id int,
    name varchar(20),
    tele varchar(11),
    primary key(id),     # 创建主键索引
    unique(tele)         # 创建唯一键索引
);

方法三

-- 建表以后添加
ALTER TABLE users ADD PRIMARY KEY (id);
ALTER TABLE users ADD unique (tele);
-- 当然也可以是复合键索引: ALTER TABLE users ADD unique(name, tele)
2. 普通索引

方法一

-- 在表创建最后指明普通索引
create table t2(
	id int,
    index(id)
)

方法二

-- 创建完表以后指定某列为普通索引
alter table t2 add index(id);  -- 会自动生成索引名

方法三

create index idx_name on t2(id);  # 注意 idx_name 和 id不同(一个是索引名,一个是字段名)
  • 索引都有索引名,主键的索引名固定位primary
3. 索引对比
索引类型 唯一性 NULL 值 数量限制 自动创建 主要用途
主键索引 强制唯一 不允许 1 个 / 表 行标识、关联外键
唯一键索引 强制唯一 允许 1 个 多个 / 表 数据唯一性约束
普通索引 不强制 允许 多个 / 表 提高查询速度

注意事项

  • 复合索引顺序:遵循 “最左前缀原则”,例如INDEX (a, b, c)支持WHERE a=1WHERE a=1 AND b=2,但不支持WHERE b=2

  • 避免冗余索引:如已有INDEX (a, b),则无需单独创建INDEX (a)

  • 索引覆盖:指的是查询所需的数据全部来自索引本身,无需回表(访问数据表)获取数据,提高了查询效率

    • 如(用复合索引实现):
    -- 创建复合索引
    CREATE INDEX idx_name_age_email ON users(name, age, email);
    
    -- 查询可被覆盖
    SELECT name, age, email FROM users WHERE name = 'Alice';
    

索引创建原则

  • 比较频繁作为查询条件的字段应该创建索引
  • 唯一性太差的字段不适合单独创建索引,即使频繁作为查询条件
  • 更新非常频繁的字段不适合作创建索引
  • 不会出现在where子句中的字段不该创建索引
4. 查询和删除

查询索引

show index from 表名;   # 或者:show keys from 表名

删除索引

drop index 索引名 on 表名   # 索引名就是show keys from 表名中的 Key_name 字段
5. 全文索引

全文索引(Full-Text Index) 是 MySQL 中专门用于快速检索文本内容的索引类型,与普通索引(如 B+ 树索引)相比,它能够高效处理模糊匹配语义搜索,特别适合大型文本字段(如文章、评论、日志)的搜索场景。(要求表的存储引擎是MyISAM,且默认的全文索引只支持英文,如果对中文进 行全文检索,可以使用sphinx的中文版)

ps:选项比较多,在这里不细讲了

十二、事务

可以参见这篇文章:【mysql】事务

十三、视图

视图(View) 是一种虚拟表,它不存储实际数据,而是基于 SQL 查询定义的结果集。同真实的表一样(在database数据目录下是会创建对应的表文件的)

在数据访问、安全性和简化复杂查询等方面有重要作用。(比如:你不想让别人看到原表,那可以直接把查询结果视图给他;比如你有一个查询很复杂,你查询出来结果了,就可以保存到视图里,下次直接看视图)

视图包含一系列带有名称的列和行数据。视图的数据变化会影响到基表,基表的数据变化也会影响到视图

创建视图

create view [新列名, ...] 视图名 as (select子句);

示例::

create view v1 as (select sno, name from stu where English>80) order by asc;

# 查询, 直接查视图
mysql> select * from v1;
# 结果
+-----+-------+
| sno | name  |
+-----+-------+
|   5 | Sam   |
|   2 | Jim   |
|   3 | James |
+-----+-------+

注意事项

  • 与表一样,必须唯一命名(不能出现同名视图)

  • 创建视图数目无限制,但要考虑复杂查询创建为视图之后的性能影响

  • 视图不能添加索引,也不能有关联的触发器或者默认值

  • 视图可以提高安全性,必须具有足够的访问权限

  • order by 可以用在视图中。如果外层查询中使用了 ORDER BY,它会完全取代视图中的排序规则(就是生成视图时数据的排序规则)

  • 视图可以和表一起使用

十四、用户管理

1. 管理用户

用户信息

MySQL中的用户,都存储在系统数据库mysql的user表中

select host, user, authentication_string from user;  # 直接查这个表就能看到
  • host:表示这个用户可以从哪个主机登陆,如果是localhost,表示只能从本机登陆

    • 对于mysql,不仅要确认登陆用户的密码正确,还要会限制用户登录的IP
  • user:用户名

  • authentication_string用户的密码(脱敏后的)

创建用户

create user '用户名'@'登陆主机/ip' identified by '密码';

示例

create user 'tr'@'localhost' identified by '123456';
  • 这时候 mysql 库中的 user表就会多一条记录(所以创建用户本质也就是往表里面插入数据)

  • 不要轻易添加一个可以从任意地方登陆的user。

删除用户

drop user '用户名'@'主机名'

示例

drop user 'tr'@'localhost';

修改用户密码

修改密码影响的只是登录mysql服务器的“凭证”

  • 自己改自己密码
ALTER USER CURRENT_USER IDENTIFIED BY '密码'
  • root用户修改指定用户的密码
ALTER USER '用户名'@'主机名' IDENTIFIED BY '密码';

新增用户登录的IP

GRANT ALL PRIVILEGES ON *.* TO 'tr'@'新IP' IDENTIFIED BY '密码';3
  • 如果这个用户和 ip 存在,覆盖原这个IP的用户权限(如密码、权限范围)

  • 如果用户不存在,则创建新的用户,并且赋予这些权限【grant的万能之处】

2. 管理用户权限

数据库提供的权限列表

查看权限

  • SHOW GRANTS; # 查看当前用户权限
    
  • SHOW GRANTS FOR 'app_user'@'localhost'; # root 查看指定用户权限
    
  • 新创建的用户默认没有任何权限

授予权限

grant 权限列表 on 库.对象名 to '用户名'@'主机号';
  • 权限列表

    • 权限之间用,隔开
    • 也可以是:ALL PRIVILEGES代表所有权限,不过危险
  • 库.*这个 *代表通配,代表所有表,如果是:*.*代表所有库和所有表

  • 在主机号里,可以用%代表通配,如127.0.0.%

  • 还可以列级别细粒度权限:如grant update(id, name) ...

示例:

grant select, insert on test1.* to 'tr'@'127.0.0.1';
  • 授权完就能多看到一个库了,就可以对里面的库进行符合权限的操作

权限生效

发现没有生效可以刷新

flush privileges;
  • MySQL 中,当前已建立的数据库连接(会话)会保留权限变更前的状态。即使执行了 REVOKEFLUSH PRIVILEGES当前登录的 tr 用户会话仍能继续使用旧权限,需重新登录才会加载新权限

回收权限

revoke 权限列表 on 库.对象名 from '用户名'@'主机号';
  • REVOKE 语句要和 GRANT 语句权限层级和对象范围完全对应,否则会报错

    • 比如对于grant select, insert on test1.* to 'tr'@'127.0.0.1';,如果单独revoke select on test1.stu from 'tr'@'127.0.0.1'; 就会报错
  • MySQL 的权限分为全局 > 数据库 > 表 > 列四个层级,

    • 之前的test1.*就是数据库级权限,具体到特定的表是表层级,具体到特定的列是列层级

示例

revoke select on test1.* from 'tr'@'127.0.0.1';
  • 重新登陆tr后权限更新就select不了test1的表了

十五、mysql连接

mysql除了可以在命令行连接,还可以实现其他连接,便于以后更方便的使用

1. C/C++连接

1. 安装开发包
  • 要使用C语言连接mysql,需要下载mysql官网提供的库Connector/C++(软件),找到符合自己系统版本的安装包,然后下载并安装到自己的操作系统上(查看系统和应用程序的配置文件可以去/etc目录下看)
  • 也可以:apt install libmysqlcppconn-dev(注意这是 C++ 的, C的是:libmysqlclient-dev)直接用apt自动从配置好的软件源中查找指定的软件包,并自动下载和安装(前提是你要知道自己要安装什么包,apt会自动安装符合系统版本的软件包,以及处理依赖关系,甚至都不用去官网下载安装包了)【强推

如果你去了官网找包,会发现一堆包:

这些包有什么区别呢?

  • 前两个是动态库:开发好的程序要在其他机器部署,就仅需这个动态库包

  • 第三个-dev结尾的是:开发库包,包含编译、开发依赖 Connector/C++ 程序所需的文件(比如头文件,以及库):开发、编译新的 C++ 连接 MySQL 的程序,必须装这个包

  • 后面两个调试包:需要调试,配合调试工具的时候,需要这个包

使用

  • 当准备好库和有效的mysql服务后,我们就可以在C语言中通过包含mysql的头文件mysql.h,来调用对应的接口访问mysql
  • 编译时要连接上库(C的动静态库链接)
  • 我们可以在官网的:C API Basic Function Reference文档里面查看具体的接口

在语言中使用 mysql_get_client_info 函数(其实就是随便调一个函数),来验证我们的引入是否成功

#include <mysql/mysql.h>
#include <iostream>

int main()
{
    printf("mysql client Version: %s\n", mysql_get_client_info());
    return 0;
}

正常输出版本信息就是成功了

2. C API 介绍

我们介绍C的 API,因为封装度小,便于理解

2.1 初始化和连接

要使用库,必须先进行初始化

MYSQL *mysql_init(MYSQL *mysql);
  • 作用:初始化一个用于连接 MySQL 数据库的句柄(MYSQL 结构体,类似FILE
  • 用法:传入参数 NULL ,函数会自动分配一个新的 MYSQL 结构体并返回其指针

初始化后,连接数据库(像登陆一样)

MYSQL *mysql_real_connect(MYSQL *mysql, // 初始化时返回的句柄
                          const char *host,  // 主机号
                            const char *user,  // 用户名
                            const char *passwd,  // 用户密码
                            const char *db,  // 要链接的数据库
                            unsigned int port,  // 端口号
                            const char *unix_socket,  // 本地 socket 文件路径,远程连接或不需要时传 NULL
                            unsigned long clientflag);  // 连接选项标志,没特殊需求传 0

关闭连接

void mysql_close(MYSQL *sock);
2.2 中文乱码

如果出现中文是乱码,代表客户端字符集和服务端不匹配,手动设置客户端字符集:

mysql_set_character_set(myfd, "utf8");
2.3 下发命令

mysql_query用于下发sql请求

int mysql_query(MYSQL *mysql, const char *q);  // query: 请求,查询
  • q为要下发的sql命令,如select * from stu

  • 正确执行返回0,错误执行返回错误码

  • 对应增删改操作直接执行完,对于select查询操作,查询结果会被保存在MYSQL结构体里

2.4 获取结果属性

mysql_store_result:获取全部查询结果

MYSQL_RES *mysql_store_result(MYSQL *mysql);  // store: 存储,保存
  • 调用此函数可以一次性把所有查询结果从MYSQL服务器取回到本地内存(会在本地malloc一块空间存放结果)
  • 返回一个结果集指针(MYSQL_RES*),查询失败或结果为空时,返回值为 NULL
  • 用完后要调用 mysql_free_result 释放内存
  • 如果结果集很大,可以用 mysql_use_result,它是边取边处理,不会一次性加载全部数据

mysql_num_rows:获取结果行数

mysql_num_fields:获取结果列数

my_ulonglong mysql_num_rows(MYSQL_RES *res);  // rows: 行  columns: 列

unsigned int mysql_num_fields(MYSQL_RES *res);  // field: 字段, 属性,即:列
  • 返回值不一样是因为列有字节上限,但是行没有

mysql_fetch_fields:获取所有列的信息

MYSQL_FIELD *mysql_fetch_fields(MYSQL_RES *res);  // fetch: 获取,提取
  • 返回一个指向字段信息数组的指针,每个元素是一个 MYSQL_FIELD 结构体,描述一列的元数据(如列名、类型、长度等)
  • 常用成员(假如用fields接收)
    • fields[i].name:字段名
    • fields[i].type:字段类型
    • fields[i].length:字段长度
    • 其他元数据信息
2.5 获取结果内容

mysql_fetch_row:用于获取结果集下一行数据

MYSQL_ROW mysql_fetch_row(MYSQL_RES *result);   // MYSQL_RES是mysql_store_result获取到的结果集指针
  • 每调用一次,返回结果集的下一行数据,类型为 MYSQL_ROW(本质是 char **,即字符串数组)。
    • 就像“文件指针”一样,每调用一次就读取结果集的下一行,指针自动往下移动,不能回头(除非重新查询,或者mysql_data_seek 移动指针)
  • 如果没有更多数据,返回 NULL

结果集指针长啥样呢?结果是怎么存储的呢?如下:

2.6 示例
#include <mysql/mysql.h>
#include <iostream>

int main()
{
    // printf("mysql client Version: %s\n", mysql_get_client_info());

    MYSQL *my = mysql_init(nullptr);
    mysql_real_connect(my, LOCAL_HOST, "tr", "123456", "test1", 3306, nullptr, 0);

    mysql_set_character_set(my, "utf8");

    std::cout << "---------插入数据前查询---------" << std::endl;
    mysql_query(my, "select * from stu");    // 这里的sql 可以不带 ;号 (懒得做返回值判断了)
    MYSQL_RES *res = mysql_store_result(my); // 获得完整的查询结果

    // 获得结果的行列数信息
    unsigned int r = mysql_num_rows(res);
    unsigned int c = mysql_num_fields(res);
    std::cout << "结果有: " << r << " 行 " << c << " 列" << std::endl;
    MYSQL_FIELD *my_fd = mysql_fetch_fields(res);

    // 打印表头
    for (int i = 0; i < c; i++)
        std::cout << my_fd[i].name << " "; // 获取每一列的列名
    std::cout << std::endl;

    // 读取每一行结果
    MYSQL_ROW row;
    while (row = mysql_fetch_row(res))
    {
        for (unsigned int i = 0; i < c; i++)
            std::cout << (row[i] ? row[i] : "NULL") << " ";
        std::cout << std::endl;
    }

    mysql_free_result(res);
    std::cout << "------------修改数据-----------" << std::endl;
    mysql_query(my, "insert into stu values(7, 'Yao', 99, 99, 99)");
    mysql_query(my, "insert into stu values(8, 'Curry', 88, 88, 88)");
    // mysql_query(my, "delete stu where name='Tom'"); 如果写错了, 就不会被执行
    mysql_query(my, "delete from stu where name='Tom'");

    std::cout << "---------插入数据后查询---------" << std::endl;
    mysql_query(my, "select * from stu");
    MYSQL_RES *res2 = mysql_store_result(my);
    unsigned int r2 = mysql_num_rows(res2);
    unsigned int c2 = mysql_num_fields(res2);

    MYSQL_FIELD *my_fd2 = mysql_fetch_fields(res2);
    for (int i = 0; i < c2; i++)
        std::cout << my_fd2[i].name << " "; // 获取每一列的列名
    std::cout << std::endl;

    MYSQL_ROW row2;
    while (row2 = mysql_fetch_row(res2))
    {
        for (int i = 0; i < c2; i++)
            std::cout << (row2[i] ? row2[i] : "NULL") << " ";
        std::cout << std::endl;
    }
    mysql_free_result(res2);
    mysql_close(my);
    return 0;
}

运行结果:

2. 图形化界面的mysql

以navicat为例,安装下载好以后,连接mysql

连接时要搞清楚几个概念

  • 本地windows的localhost和云服务器的localhost不是一个东西
    • 如果你要用localhost连接:那要确保你启动的Navicat的机器和mysql服务运行的机器是同一台。
    • 如果你人在 本地电脑(想远程连云服务器的 MySQL),直接填云服务器公网 IP,会被权限拦截(因为 trhost 不允许远程 IP 连接)

解决方法:

  • 把windows的IP绑定到mysql登录的用户上,比如绑定%(不安全)
  • 或者绑定你自己windows电脑的内网 / 公网IP
    • windows 和 云服务器在同一内网的时候绑定内网IP
    • windows 和 云服务器在不同内网的时候可以绑定公网IP,查看windows公网IP

遇到其他连接失败问题可以看这篇文章


🌈我的分享也就到此结束啦🌈
要是我的分享也能对你的学习起到帮助,那简直是太酷啦!
若有不足,还请大家多多指正,我们一起学习交流!
📢公主,王子:点赞👍→收藏⭐→关注🔍
感谢大家的观看和支持!祝大家都能得偿所愿,天天开心!!!

转载请说明出处内容投诉
AJ教程_站长资源网 » 【Mysql】33k字带你入门mysql

发表评论

欢迎 访客 发表评论

一个令你着迷的主题!

查看演示 官网购买