当前位置: 首页 > article >正文

python全栈-MySQL知识

python全栈-MySQL知识

文章目录

  • 数据库基本概念
    • 关系型数据库
    • 非关系型数据库
  • MySQL基本知识
    • 连接MySQL
  • SQL语言
  • DDL创建/删除数据库
    • 创建数据库
    • 删除数据库
    • 选择数据库
    • 数据类型
      • 整数
      • 浮点类型
      • 字符类型
      • 日期类型
      • 二进制数据(BLOB)
    • 创建表/删除表CREATE/DROP
    • 修改表ALTER
      • 修改表名 RENAME
      • 修改列名 CHANGE COLUMN
      • 修改列的类型 MODIFY
      • 添加新列 ADD COLUMN
      • 删除指定列 DROP COLUMN
    • 约束
    • 添加主键约束(Primary Key) PK
    • 修改表添加主键约束 ADD PRIMARY KEY
    • 主键自增长
    • 删除主键
    • 添加外键约束(Foreign Key) ADD CONSTRAINT
    • 删除外键约束
    • 添加唯一性约束(Unique)
    • 删除唯一性约束
    • 非空约束(Not Null)
    • 删除非空约束
    • 创建表时添加约束
  • DML操作
    • 添加数据(INSERT)INSERT INTO
    • 默认值处理(DEFAULT)
    • 更新数据(UPDATE)
    • 删除数据(DELETE)
  • DQL查询数据
    • SELECT基本查询
    • 列选择
    • 定义空值
    • 别名
    • 去重
    • 行选择
    • where后面的条件
    • where子句之间的条件关系
    • ORDER BY 排序
  • SQL函数
    • 单行函数
      • 字符函数
      • 数字函数
      • 日期函数
      • 转换函数
      • 通用函数
    • 多行函数/聚合函数
  • 多表查询
    • 等值连接
    • 非等值链接
    • 自链接
    • 交叉链接cross join
    • 自然链接 NATURAL JOIN
    • 内连接 INNER JOIN ON
    • 外连接 OUTER JOIN
    • 全外连接FULL OUTER JOIN
  • 数据分组GROUP BY
    • 在多列上使用分组
    • 约束分组结果HAVING
  • 子查询/内查询
    • 单行子查询
    • 多行子查询
  • 索引
  • 事务TCL(面试可能问)
    • 事务并发
  • 用户管理
  • MySQL 分页查询
  • python操作MySQL
    • 修改数据库
  • 基于MySQL的音乐器
    • 解读代码
    • 难点

数据库基本概念

  • 基本概念

数据存放在数据库里面,数据库需要数据库管理系统来使用

最后的重点就是数据库管理系统

数据库管理系统需要管理员来维护

数据库管理系统还要提供接口给外面的应用去使用

  • 数据库的分类

关系型数据库 MySQL

非关系型数据库 Redis

关系型数据库

最经典的数据结构是表。

可以使用SQL进行操作

  • 优点

易于维护

使用方便

复杂操作

  • 缺点

读写性能差

表的格式太固定,不能灵活变通

不能高并发读写,只能使用硬盘

非关系型数据库

NoSQL数据库,不支持SQL语言

  • 优点

格式灵活

速度快:可以使用硬盘或者内存作为载体

扩展性高

成本低

  • 缺点

不支持SQL

复杂查询方面较差

没有事务处理

MySQL基本知识

需要下载5.7版的MySQL

因为企业用稳定的数据库,最新的数据库有一定的风险

连接MySQL

使用MySQL的客户端,与MySQL的服务端进行交互,使用SQL语言交互

使用MySQL自带的客户端,

自带的客户端在MySQL的安装目录,即programfile里面的MySQL目录,然后打开bin目录,mysql.exe,就是自带的客户端工具了

programdata目录里面是数据目录,不是安装目录

Windows版,需要去命令行里面运行这个exe程序,使用cd进入安装目录,然后执行程序

写命令,mysql -uroot -proot

其中,第一个root是用户名,第二个root是密码.

第二个root可以不写在p的后面,直接回车,然后再输入密码

因为这种软件的操作比较繁琐,就像python可以使用命令行写一样,太难用了.我们要用pycharm编辑python程序

MySQL也要使用外部工具,这里使用了Navicat

SQL语言

概念:结构化查询语言,一种数据库查询和程序设计语言,用于存取数据,查询,更新,管理数据库系统。

  • SQL语言分类
  • 1 数据查询语言(DQL:Data Query Language)其语句,也称为“数据检索语句”,用以从表中获得数据,确定数据怎样在应用程
    序给出。关键字 SELECT 是 DQL(也是所有 SQL)用得最多的关键字。
    • SELECT
    • FROM
    • WHERE
    • ORDER BY
    • HAVING
  • 2 数据操作语言(DML:Data Manipulation Language)其语句包括动词 INSERT,UPDATE 和 DELETE。它们分别用于添加,
    修改和删除表中的行。
    • INSERT:添加数据
    • UPDATE:更新数据
    • DELETE:删除数据
  • 3 数据定义语言(DDL:Data Definition Language)定义数据库对象语言,其语句包括动词 CREATE 和 DROP 等。
    • CREATE:创建数据库对象
    • ALTER:修改数据库对象
    • DROP:删除数据库对象
  • 4 数据控制语言(DCL:Data Control Language)它的语句通过GRANT 或 REVOKE 获得许可,确定用户对数据库对象的访问。
    • GRANT:授予用户某种权限
    • REVOKE:回收授予的某种权限
  • 5 事务控制语言(TCL :Transaction Control Language)它的语句能确保被 DML 语句影响的表的所有行及时得以更新。
    • COMMIT:提交事务
    • ROLLBACK:回滚/撤销事务
    • SAVEPOINT:设置回滚点

数据操纵语言DML(insert、update、delete)针对表中的数据 ;

而数据定义语言DDL(create、alter、drop)针对数据库对象,比如数据库database、表table、索引index、视图view、存储过程procedure、触发器trigger;

  • SQL语言语法
    • 1 SQL语句不区分大小写,关键字建议大写。
    • 2 SQL语句可以单行或多行书写,以分号结尾。

DDL创建/删除数据库

创建数据库

语法:

CREATE DATABASE test DEFAULT CHARACTER SET utf8;

CREATE DATABASE 创建数据库关键字

test 数据库名

DEFAULT CHARACTER SET 指定编码类型关键字

utf8; 字符编码

  • 示例:创建一个test 的数据库,并查看该数据库,以及该数据库的编码。
    • 创建数据库:create database test default character set utf8;
    • 查看数据库:show databases;
    • 查看数据库编码:select schema_name,default_character_set_name
      from information_schema.schemata
      where schema_name = ‘test’;
  • 使用navicat创建数据库,直接对着数据库右键创建数据库,起个名字,指定一下编码方式

删除数据库

语法:

DROP DATABASE 数据库名称;
  • 示例:删除 test 数据库
  • drop database test;

选择数据库

在创建表时,需要先选择数据库

USE 数据库名;

选择好数据库之后,以后的操作都是只针对这个库操作

数据类型

五种数据类型:

  • 整数
  • 浮点数
  • 字符
  • 日期
  • 二进制

整数

MySQL数据类型含义(有符号)
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次方)

数值类型中的长度 m 是指显示长度,并不表示存储长度,只有字段指定 zerofill 时有用

例如: int(3) ,如果实际值是 2 ,如果列指定了 zerofill ,查询结果就是 002 ,左边用 0 来 填充

浮点类型

  • float(m,d) 单精度浮点型 8位精度(4字节) m总位数,d小数位
  • double(m,d) 双精度浮点型 16位精度(8字节) m总位数,d小数位

字符类型

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

有括号参数的命令,必须写,不能省略

  • char和varchar:
    1 char长度固定, 即每条数据占用等长字节空间;适合用在身份证号码、手机号码等定长。
    2 varchar可变长度,可以设置最大长度;适合用在长度可变的属性。
    3 text不设置长度, 当不知道属性的最大长度时,适合用text。
    按照查询速度: char最快, varchar次之,text最慢

  • 字符串型使用建议:
    1 经常变化的字段用varchar
    2 知道固定长度的用char
    3 尽量用varchar
    4 超过255字符的只能用varchar或者text
    5 能用varchar的地方不用text

日期类型

  • date 日期 YYYY-MM-DD
  • time 时间 HH:MM:SS
  • datetime 日期时间 YYYY-MM-DD HH:MM:SS
  • timestamp 时间戳YYYYMMDD HHMMSS

二进制数据(BLOB)

  • 1 BLOB和TEXT存储方式不同,TEXT以文本方式存储,英文存储区分大小写,而Blob是以二进制方式存储,不分大小写。
  • 2 BLOB存储的数据只能整体读出。
  • 3 TEXT可以指定字符集,BLOB不用指定字符集

创建表/删除表CREATE/DROP

  • 创建表
    • 使用DDL语句:CREATE TABLE 表名(列名 类型,列名 类型…);
      • CREATE TABLE 创建表的关键字
      • 表名(列名 类型,列名 类型…); 表名,列名,类型
    • 示例:创建一个 employees 表包含雇员 ID ,雇员名字,雇员薪水。
    • create table employees(employee_id int,employee_name varchar(10),salary float(8,2));
    • 查看已创建的表。show tables;
  • 使用navicat创建表
    • 选择表所在的数据库
    • 新建表
    • 按照参数创建列,元素
    • CTRL+s保存,同时给表命名
  • 删除表
    • 使用DDL删除表:DROP TABLE 表名;
    • 示例:删除 employees 表
    • drop table employees;

修改表ALTER

修改表名 RENAME

  • 使用DDL语句修改表名:ALTER TABLE 旧表名 RENAME 新表名;
    • ALTER TABLE 修改表关键字
    • 旧表名
    • RENAME 修改表名关键字
    • 新表名;
  • 在navicat里面修改表名
    • 点击表,然后按下F2,就可以重命名了
    • 也可以双击表名

修改列名 CHANGE COLUMN

  • 使用DDL:
  • ALTER TABLE 表名 CHANGE COLUMN 旧列名 新列名 类型;
  • 示例:将 emp 表中的 employee_name 修改为 name。
  • alter table emp change column employee_name name varchar(20); 缺一不可
  • 在navicat里面直接点击表,然后点击设计表,对列重命名,最后CTRL+s保存

修改列的类型 MODIFY

  • DDL:ALTER TABLE 表名 MODIFY 列名 新类型;
    • ALTER TABLE
    • 表名
    • MODIFY 修改列类型关键字
    • 列名
    • 新类型;
  • 示例:将 emp 表中的 name 的长度指定为 40。
  • alter table emp modify name varchar(40);

添加新列 ADD COLUMN

  • DDL:ALTER TABLE 表名 ADD COLUMN 新列名 类型;
  • 示例:在 emp 表中添加佣金列,列名为 commission_pct。
  • alter table emp add column commission_pct float(4,2);

删除指定列 DROP COLUMN

  • ALTER TABLE 表名 DROP COLUMN 列名;
  • 示例:删除 emp 表中的 commission_pct。
  • alter table emp drop column commission_pct;

约束

就是对数据库的数据进行校验

  • 1 主键约束(Primary Key) PK
    主键约束是使用最频繁的约束。在设计数据表时,一般情况下,都会要求表中设置一个主键。主键是表的一个特殊字段,该字段能唯一标识该表中的每条信息。例如,学生信息表中的学号是唯一的。
    • 不允许为空
    • 不允许有重复值
    • 保证数据的唯一性
  • 外键约束(Foreign Key) FK
    外键约束经常和主键约束一起使用,用来确保数据的一致性。
    • 可以有空值
    • 可以重复
    • 必须含于另一个列,就是该列的值,必须是在指定列里面
  • 3 唯一性约束(Unique)
    唯一约束与主键约束有一个相似的地方,就是它们都能够确保列的唯一性。与主键约束不同的是,唯一约束在一个表中可以有多个,并且设置唯一约束的列是允许有空值的。
    • 不允许重复
    • 可以空值
  • 4 非空约束(Not Null)
    非空约束用来约束表中的字段不能为空。
    • 允许重复
    • 不允许空值
  • 5 检查约束(Check)
    检查约束也叫用户自定义约束,是用来检查数据表中,字段值是否有效的一个手段,但目前 MySQL 数据库不支持检查约束。
    • 自定义约束条件,MySQL不支持

添加主键约束(Primary Key) PK

  • 单一主键
    使用一个列作为主键列,当该列的值有重复时,则违反唯一约束。就像学校考试的时候,只看学号,哪怕10个同名同姓的人在一起,只要学号不同就可以
  • 联合主键
    使用多个列作为主键列,当多个列的值都相同时,则违反唯一约束。就是一个人有很多属性,其他人只要和这个人的属性不是一模一样,就允许其他人的存在。

修改表添加主键约束 ADD PRIMARY KEY

  • ALTER TABLE 表名 ADD PRIMARY KEY(列名)
  • 示例:将 emp 表中的 employee_id 修改为主键。
  • alter table emp add primary key(employee_id);

主键自增长

  • alter table 表名 modify 主键 类型 auto_increment;

  • MySQL 中的自动增长类型要求:

    • 一个表中只能有一个列为自动增长。
    • 自动增长的列的类型必须是整数类型。
    • 自动增长只能添加到具备主键约束与唯一性约束的列上。
    • 删除主键约束或唯一性约束,如果该列拥有自动增长能力,则需要先去掉自动增长然 后在删除约束。
  • 示例:将 emp 表中的 employee_id 主键修改为自增

  • alter table emp modify employee_id int auto_increment;

这一列设为主键,并且设置了主键自增,在添加数据的时候,可以不用管这一列。它会自动加1

如果只是设为主键,不是自增,就不能在创建数据的时候为空

删除主键

  • 使用DDL语句删除主键:ALTER TABLE 表名 DROP PRIMARY KEY;
  • 删除主键时,如果主键列具备自动增长能力,需要先去掉自动增长,然后在删除主键
  • 示例:删除emp表中的 employee_id 主键约束。
    • 去掉自动增长:alter table emp modify employee_id int;
    • 删除主键:alter table emp drop primary key;

添加外键约束(Foreign Key) ADD CONSTRAINT

缘由:

一个公司有很多员工,同样也有很多部门

我们可以根据部门分配权限。

只需要把员工添加到对应的部门,该员工就可以获得该部门的权限。

所以,一个员工在公司里面,必须要属于一个部门。当然,刚入职的员工由于没有分配部门,允许空值。

如何知道员工所属的部门呢,可以给员工增加一个属性,这个属性专门填写部门的编号。这个属性的值和部门的编号就是外键约束。

  • 修改表添加外键约束
  • ALTER TABLE 表名 ADD CONSTRAINT 约束名 FOREIGN KEY( 列名 ) REFERENCES 参照的表名(参照的列名);
  • 示例:向 emp 表中的 dept_id 列添加外键约束。
  • alter table emp add constraint emp_fk foreign key(dept_id) references departments(department_id);

删除外键约束

使用DDL语句删除外键约束。ALTER TABLE 表名 DROP FOREIGN KEY 约束名;

  • 示例:删除 dept_id 的外键约束。
  • alter table emp drop foreign key emp_fk;

添加唯一性约束(Unique)

  • ALTER TABLE 表名 ADD CONSTRAINT 约束名 UNIQUE(列名);
  • 示例:向 emp 表中的 name 添加唯一约束。
  • alter table emp add constraint emp_uk unique(name);

删除唯一性约束

  • ALTER TABLE 表名 DROP KEY 约束名;
  • 示例:删除 name 的唯一约束。
  • alter table emp drop key emp_uk;

非空约束(Not Null)

  • ALTER TABLE 表名 MODIFY 列名 类型 NOT NULL;

  • 示例:向 emp 表中的 salary 添加非空约束。

  • alter table emp modify salary float(8,2) not NULL;

删除非空约束

  • ALTER TABLE 表名 MODIFY 列名 类型 NULL;
  • 示例:删除emp表中salary 的非空约束。
  • alter table emp modify salary float(8,2) NULL;

创建表时添加约束

刚刚添加约束条件都是在表创建完成后再对表进行调整

我们可以在表创建的时候直接添加约束条件

查询表中的约束信息:SHOW KEYS FROM 表名;

  • 示例:创建 depts 表包含
    • department_id 该列为主键且自动增长,
    • department_name 列不允许重复,
    • location_id 列不允含有空值。
  • create table depts(department_id int primary key auto_increment,department_name varchar(30) unique,location_id int not null);
    • create table depts(
    • department_id int primary key auto_increment,
    • department_name varchar(30) unique,
    • location_id int not null);
    • 格式:列名 列类型 约束条件

DML操作

刚刚的一切都是DDL操作

  • DML操作:
    • insert 增
    • delete 删
    • update 改

添加数据(INSERT)INSERT INTO

  • 选择插入 给指定的列添加信息,如果有非空约束,也要填入数据

INSERT INTO 表名(列名 1 ,列名 2 ,列名3…) VALUES(值 1 ,值 2 ,值 3…);

示例:向 departments 表中添加一条数据,部门名称为 market ,工作地点 ID 为 1。

insert into departments(department_name,location_id) values(“market”, 1);

  • 完全插入

INSERT INTO 表名 VALUES(值 1 ,值 2 ,值3…); 这些值会从左到右依次填入列

如果主键是自动增长,需要使用 default 或者 null 或者 0 占位。

示例:向 departments 表中添加一条数据,部门名称为 development ,工作地点 ID 为 2 。使用 default 占位。

insert into departments values(default,“development”,2);

默认值处理(DEFAULT)

在 MySQL 中可以使用 DEFAULT 为列设定一个默认值。如果在插入数据时并未指定该列的值,那么 MySQL 会将默认值添加到该列中。

CREATE TABLE 表名(列名 类型 default 默认值,…);

示例:创建 emp3 表,该表包含 emp_id 主键且自动增长,包含 name ,包含 address 该列默认 值为”未知”。

create table emp3(emp_id int primary key auto_increment,name varchar(10),address varchar(50) default ‘Unknown’);

  • 修改表添加新列并指定默认值

ALTER TABLE 表名 ADD COLUMN 列名 类型 DEFAULT 默认值;

就是在原来增加列的语句基础上,在列的类型后面加上default 和默认值

示例:修改 emp3 表,添加job_id 该列默认值为 0。

alter table emp3 add column job_id int default 0;

  • 插入数据时的默认值处理

如果在插入数据时并未指定该列的值,那么MySQL 会将默认值添加到该列中。

如果是 完全项插入需要使用 default 来占位。

示例:向 emp3 表中添加数据,要求 address 列与job_id 列使用默认值作为该列的值。

选择插入:insert into emp3(name) values(“admin”);

完全插入:insert into emp3 values(default,“oldlu”,default,default);

对于完全插入时,有主键自增的不用管,有默认值的也可以不管

更新数据(UPDATE)

UPDATE 表名 SET 列名=值,列名=值 WHERE 条件;

更新语句中一定要给定更新条件,否则表中的所有数据都会被更新。

where后面跟的是修改条件,如果没有修改条件,默认是全改。

示例:更新 emp3 表中的 id 为 1 的数据,添加 address 为 BeiJing。

update emp3 set address = “BeiJing” where emp_id = 1;

删除数据(DELETE)

DELETE FROM 表名 WHERE 条件;

在DELETE语句中,如果没有给定删除条件则会删除表中的所有数据

  • 示例:删除 emp3 表中 emp_id 为 1 的雇员信息。

delete from emp3 where emp_id = 1;

  • TRUNCATE清空表

TRUNCATE TABLE 表名;

  • 示例:删除 emp3 表中的所有数据。

truncate table emp3;

delete from emp3; 也可以

  • 清空表时DELETE与 TRUNCATE 区别
  1. truncate 是整体删除(速度较快), delete 是逐条删除(速度较慢);
  2. truncate 不写服务器 log,delete 写服务器 log,也就是 truncate 效率比 delete 高的原因;
  3. truncate 是会重置自增值,相当于自增列会被置为初始值,又重新从 1 开始记录,而 不是接着原来的值。而 delete 删除以后, 自增值仍然会继续累加。

DQL查询数据

SELECT基本查询

SELECT语句的功能

  • SELECT 语句从数据库中返回信息。使用一个 SELECT 语句,可以做
    下面的事:
  • 列选择:能够使用 SELECT 语句的列选择功能选择表中的列,这些列是想要用查询返回的。当查询时,能够返回列中的数据。
  • 行选择:能够使用 SELECT 语句的行选择功能选择表中的行,这些行是想要用查询返回的。能够使用不同的标准限制看见的行。
  • 连接:能够使用 SELECT 语句的连接功能来集合数据,这些数据被存储在不同的表中,在它们之间可以创建连接,查询出我们所关心的数据。

基本 SELECT 语句,在最简单的形式中,SELECT 语句必须包含下面的内容:

  • 一个 SELECT 子句,指定被显示的列

  • 一个 FROM 子句,指定表,该表包含 SELECT 子句中的字段列表

  • SELECT 是一个或多个字段的列表

  • *选择所有的列

  • DISTINCT 禁止重复

  • column | expression 选择指定的字段或表达式

  • alias 给所选择的列不同的标题

  • FROM table 指定包含列的表

列选择

SELECT 列名称 FROM 表名称;

示例:查询 departments 表中所有部门名称。

select department_name from departments;

  • 算术表达式
    • 需要修改数据显示方式,如执行计算,或者作假定推测,这些都可能用到算术表达式。一个算术表达式可以包含列名、固定的数字值和算术运算符。
    • 就是我们在获得数据之后,进行了一个算术运算。就像结算工资的时候,先算所有人的业绩。然后统一加上底薪。并且我们看到的结果,不会影响原来表的数据。
    • 算术运算符也有优先级,跟正常的算术运算一样
    • 如果算术表达式包含有一个以上的运算,乘法和除法先计算。如果在一个表达式中的运算符优先级相同,计算从左到右进行。可以用圆括号强制其中的表达式先计算。
  • 示例:查询雇员的年薪,并显示他们的雇员ID,名字。
    • select employees_id,last_name, 12*salary from employees;
    • 从employees表中,找到id和名字,并且把薪水乘12,然后显示到面板上

定义空值

在MySQL里面null,不是0也不是空格,就是空。

  • 如果一行中的某个列缺少数据值,该值被置为 null, 或者说包含一个空。
  • 空是一个难以获得的、未分配的、未知的,或不适用的值。空和 0或者空格不相同。 0 是一个数字,而空格是一个字符。

包含空值的算术表达式结果是空。

别名

  • 列别名

SELECT 列名 (AS) 列别名 FROM 表名 WHERE 条件;

就是列名后面,跟一个空格,然后输入列别名即可。中间可以有as,也可以没有。

作用:我们使用select语句查询的时候,如果使用了算术表达式,那边在显示结果的时候,就是算术表达式作为列名。现在使用起别名的方式,可以不显示算术表达式,只显示我们的别名。

比如:select number*12 number from employees;

  • 表别名

SELECT 表别名.列名 FROM 表名 (as) 表别名 WHERE 条件;

示例:查询 employees 表为表定义别名为emp,将雇员 last_name 列定义别名为 name。

select emp.last_name name from employees emp;

去重

因为select默认是显示所有行,无论有没有重复的行

SELECT DISTINCT 列名 FROM 表名;

我们只需要在查询列的时候,在列名前面加上distinct,就可以把这一列相同的结果剔除了

如果我们在查询的时候,选择多个列。只要这一行的元素不是完全相等,就不会被剔除。

也就是说,如果有很多列,只要有一个列元素与其他的行不同,这一行就不会被剔除。

知道去重的时候,是看一行,就完了

行选择

用 WHERE 子句限制从查询返回的行。一个 WHERE 子句包含一个必须满足的条件,WHERE 子句紧跟着 FROM 子句。如果条件是true,返回满足条件的行。

就是where在from后面。

SELECT * | 投影列 FROM 表名 WHERE 选择条件;

  • 在语法中:
    • WHERE 限制查询满足条件的行
    • 选择条件 由列名、表达式、常数和比较操作组成
  • 示例:查询 departments 表中部门 ID 为 90 的部门名称与工作地点 ID。
  • select department_name,location_id from departments where department_id =90;

where后面的条件

就是python里面的判断语句

  • 算术运算的判断条件

比如=,<,><=,>=,<>。。。

在这里有一个特殊的<>,两个尖括号,也可以表示不等于

示例:查询 employees 表中员工薪水大于等于 3000 的员工的姓名与薪水。

select last_name,salary from employees where salary >= 3000;

between 下限 and 上限在上限和下限之间的数据可以显示,包括上下限本身,也就是闭区间
in(值1,值2。。。)可以匹配多个参数,只要有一个值在参数列表里面就可以
like字符模板,只要有字符在like里面就可以
is null只要是空值就可以
  • BETWEEN条件

示例:查询 employees 表,薪水在 3000-8000 之间的雇员ID、名字与薪水。

select employee_id,last_name,salary from employees where salary between 3000 and 8000;

  • like

只可以筛选字符类型

select 列名 from 表名 where like ‘字符模板’

字符模板中有两个通配符

  1. % 百分号,类似正则表达式里面的*,就是任意内容,任意数量
  2. _ 是占位符,如果我们想要筛选的信息是一个人的名字的第二个字符是a,就可以写’_a%’ 意思就是a在第二位,后面的内容不限

%a% 像是这样的字符模板,意思就是,在字符串中含有a字符,就可以匹配

示例:查询 employees 中雇员名字第二个字母是 e 的雇员名字。

select last_name from employees where last_name like ‘_e%’;

  • is null

在这个条件判断中,有is null空值和is not null 非空值两个类型。

IS NULL 条件用于空值测试。空值的意思是难以获得的、未指定的、未知的或者不适用的。因此,你不能用 = ,因为 null 不能等于或不等于任何值。

因为我们在上传数据的时候,有的数据的一些属性可以是空值。我们在使用数据库找这些空值的时候,只能使用where 列名 is null。

where子句之间的条件关系

就是在where后面允许同时有多个条件

这些条件相互之间也有逻辑关系

有三种:

  • and 就是并,一假即假。
  • or 就是或,一真即真
  • not 就是非,取反,如果后面跟着的条件不成立,则。。。

示例:查询 employees 表中雇员薪水是 8000 的并且名字中含有e 的雇员名字与薪水。

select last_name,salary from employees where salary = 8000 and last_name like ‘%e%’;

示例:查询 employees 表中雇员名字中不包含 u 的雇员的名字。

select last_name from employees where last_name not like ‘%u%’;

  • 逻辑优先级

从上到下依次降低

  1. 算术表达式
  2. 连字操作
  3. 比较操作
  4. is null ,like,in
  5. not
  6. and
  7. or

同样的是,使用圆括号可以改变优先级

示例:select 列名 from 表名 where A or B and C;

这是有两个条件:

  1. and的优先级比or高。所以是BandC
  2. 然后是BandC的结果,在和A取or的关系

ORDER BY 排序

对结果进行排序的子句

  • ASC 是升序,默认的,由小到大
  • DESC是降序

order by子句需要放在select语句命令的最后面

  • SELECT 语句的执行顺序如下:
  1. FROM 子句 找表
  2. WHERE 子句 找行
  3. SELECT 子句 找列
  4. ORDER BY 子句 排序

示例:查询 employees 表中的所有雇员,显示他们的ID、名字与薪水,并按薪水升序排序。

select employee_id,last_name,salary from employees order by salary (asc);

  • 使用列别名排序

示例:显示雇员ID,名字。计算雇员的年薪,年薪列别名为annsal,并对该列进行升序排序,

select employee_id,last_name ,12*salary annsal from employees order by annsal;

  • 多列排序

示例:以升叙排序显示 DEPARTMENT_ID 列,同时以降序排序显示SALARY 列。

select department_id,salary from employees order by department_id asc ,salary desc;

这里我们对两列数据进行排序,因为是多个列,在多个列之间也有先来后到

哪一个列排序在前,就先按哪一个列进行排序。在排完序之后,再对另一列排序。

因为使用第一个列排序之后,肯定有大量相同值挤在一起,在使用第二个列排序之后,这些相同的值之间也有了次序。

  • 排序的列可以不在列选择内。
    • 就是我们使用select选择的时候,会选择一些列。
    • 现在要排序了,我们可以使用刚刚没选择的列。当然这个要排序的列要在同一个表里面。

SQL函数

由于每个数据库厂商提供的函数大不相同,所以我们只能针对一个数据库软件去学习,这里是MySQL数据库所提供的函数。

记住,这些函数不是通用的。

  • 函数是 SQL 的一个非常强有力的特性,函数能够用于下面的目的:

    • 执行数据计算
    • 修改单个数据项
    • 操纵输出进行行分组
    • 格式化显示的日期和数字
    • 转换列数据类型
  • 单行函数
    单行函数仅对单个行进行运算,并且每行返回一个结果。
    常见的函数类型:
    字符
    数字
    日期
    转换

  • 多行函数
    多行函数能够操纵成组的行,每个行组给出一个结果,这些函数也被称为组函数。

单行函数

  • 操纵数据项
  • 接受多个参数并返回一个值
  • 作用于每一个返回行
  • 每行返回一个结果
  • 可以修改数据类型
  • 可以嵌套
  • 接受多个参数,参数可以是一个列或者一个表达式

字符函数

  • 大小写处理函数
    • LOWER(str)|LCASE(str) 将给定的字符串变成小写
    • UPPER(str)|UCASE(str) 将给定的字符串变成大写

示例:显示雇员 Davies 的雇员号、姓名和部门号,将姓名转换为大写。

select employee_id,UPPER(last_name),department_id from employees where last_name = ‘davies’;

和之前查询列的方法是一样的,只是在需要使用函数的列用函数包住,function(列名)

  • 字符处理函数
    • LENGTH(s) 返回字符串s的长度
    • CONCAT(s1,s2…sn) 字符串s1,s2等多个字符串合并为一个字符串
    • LPAD(s1,len,s2) 在字符串s1的开始处填充字符串s2,使字符串长度达到len。右对齐。
    • RPAD(s1,len,s2) 在字符串s1的结尾处添加字符串s2,使字符串的长度达len。左对齐
    • LTRIM(s) 去掉字符串s开始处的空格,去掉左侧的空格
    • RTRIM(s) 去掉字符串s结尾处的空格,去掉右侧的空格
    • REPLACE(S,S1,s2)将字符串s2替代字符串s中的字符串 s1
    • REVERSE(s) 将字符串s的顺序反过来
    • SUBSTR(S, start,length) 从字符串s的start位置截取长度为length的子字符串。下标从1开始
    • SUBSTRING(S,start, length)从字符串s的start位置截取长度为length的子字符串
    • TRIM(s) 去掉字符串s开始和结尾处的空格
    • INSTR(字符串,子串) 返回字符串中子串第一次出现的下标

示例:显示所有工作岗位名称从第 4 个字符位置开始,包含字符串 REP的雇员的ID信息,将雇员的姓和名连接显示在一起,还显示雇员名的的长度。

SELECT employee_id, CONCAT(last_name,first_name) NAME,job_id, LENGTH(last_name) FROM employees WHERE SUBSTR(job_id, 4) = ‘REP’;

数字函数

  • ABS(x) 返回 x 的绝对值

  • ACOS(x) 求 x 的反余弦值(参数是弧度)

  • ASIN(x) 求反正弦值(参数是弧度)

  • ATAN(x) 求反正切值(参数是弧度)

  • ATAN2(n, m) 求反正切值(参数是弧度)

  • AVG(expression)返回一个表达式的平均值,expression 是一个字段

  • CEIL(x) 返回大于或等于 x 的最小整数

  • CEILING(x) 返回大于或等于 x 的最小整数

  • COS(x) 求余弦值(参数是弧度)

  • COT(x) 求余切值(参数是弧度)

  • COUNT(expression)返回查询的记录总数,expression 参数是一个字段或者 * 号

  • DEGREES(x) 将弧度转换为角度

  • n DIV m 整除,n 为被除数,m 为除数

  • EXP(x) 返回 e 的 x 次方

  • FLOOR(x) 返回小于或等于 x 的最大整数

  • GREATEST(expr1,expr2, expr3, …)返回列表中的最大值

  • LEAST(expr1,expr2, expr3, …)返回列表中的最小值

  • LN 返回数字的自然对数,以 e 为底。

  • LOG(x) 或 LOG(base, x)返回自然对数(以 e 为底的对数),如果带有 base 参数,则base 为指定带底数。

  • LOG10(x) 返回以 10 为底的对数 SELECT LOG10(100) – 2

  • LOG2(x) 返回以 2 为底的对数

  • MAX(expression)返回字段 expression 中的最大值

  • MIN(expression)返回字段 expression 中的最小值

  • MOD(x,y) 返回 x 除以 y 以后的余数

  • PI() 返回圆周率(3.141593)

  • POW(x,y) 返回 x 的 y 次方

  • POWER(x,y) 返回 x 的 y 次方

  • RADIANS(x) 将角度转换为弧度

  • RAND() 返回 0 到 1 的随机数

  • ROUND(x) 返回离 x 最近的整数 ,四舍五入

  • SIGN(x)返回 x 的符号,x 是负数、0、正数分别返回 -1、0 和 1

  • SIN(x) 求正弦值(参数是弧度)

  • SQRT(x) 返回x的平方根

  • SUM(expression) 返回指定字段的总和

  • TAN(x) 求正切值(参数是弧度)

  • TRUNCATE(x,y)返回数值 x 保留到小数点后 y位的值(与 ROUND 最大的区别是不会进行四舍五入)

  • ROUND(column|expression, n) 函数

    ROUND 函数四舍五入列、表达式或者 n 位小数的值。如果第二个参数是 0 或者缺少,值被四舍五入为整数。如果第二个参数是 2值被四舍五入为两位小数。如果第二个参数是–2,值被四舍五入到小数点左边两位。

    SELECT ROUND(45.923,2),ROUND(45.923,0),ROUND(45.923,-1);

    分别是45.92 46 50

  • TRUNCATE(column|expression,n) 函数

    TRUNCATE函数的作用类似于 ROUND 函数。如果第二个参数是 0或者缺少,值被截断为整数。如果第二个参数是 2,值被截断为两位小数。如果第二个参数是–2,值被截断到小数点左边两位。与ROUND 最大的区别是不会进行四舍五入。

    和round函数一样,只是不会四舍五入

    SELECT TRUNCATE(45.923,2);

日期函数

在MySQL中允许直接使用字符串表示日期,但是要求字符串的日期格式必须为:‘YYYY-MM-DD HH:MI:SS’ 或者‘YYYY/MM/DDHH:MI:SS’;

下面这些函数是没有参数的,可以不用写参数,直接调用

  • CURDATE()|CURRENT_DATE() 返回当前日期
  • CURTIME()|CURRENT_TIME() 返回当前时间
  • DATE() 从日期或日期时间表达式中提取日期值
  • DATEDIFF(d1,d2) 计算日期 d1->d2 之间相隔的天数
  • DAY(d) 返回日期值 d 的日期部分
  • DAYNAME(d)返回日期 d 是星期几,如Monday,Tuesday
  • DAYOFMONTH(d) 计算日期 d 是本月的第几天
  • DAYOFWEEK(d)日期 d 今天是星期几,1 星期日,2 星期一,以此类推
  • DAYOFYEAR(d) 计算日期 d 是本年的第几天
  • HOUR(t) 返回 t 中的小时值
  • LAST_DAY(d) 返回给给定日期的那一月份的最后一天
  • MONTHNAME(d) 返回日期当中的月份名称,如 November
  • MONTH(d) 返回日期d中的月份值,1 到 12
  • NOW()|SYSDATE() 返回当前日期和时间
  • SECOND(t) 返回 t 中的秒钟值
  • TIMEDIFF(time1,time2) 计算时间差值
  • TO_DAYS(d) 计算日期 d 距离 0000 年 1 月 1 日的天数
  • WEEK(d) 计算日期 d 是本年的第几个星期,范围是0 到 53
  • WEEKDAY(d)日期 d 是星期几,0 表示星期一,1 表示星期二
  • WEEKOFYEAR(d)计算日期 d 是本年的第几个星期,范围是0 到 53
  • YEAR(d) 返回年份

转换函数

  • 隐式转换 就是MySQL自动执行的操作,比如字符串转换成日期类
  • 显式转换 需要依赖转换函数来完成相关类型的转换。
    • DATE_FORMAT(date,format) 将日期转换成字符串;
    • STR_TO_DATE(str,format) 将字符串转换成日期;
    • format就是字符串的格式,如果是2000年01月01日,format就是%Y年%m月%d日

示例:向 employees 表中添加一条数据,雇员ID:400,名字:oldlu ,email:oldlu@sxt.cn ,入职时间:2049 年 5 月 5 日,工作部门:‘IT_PROG’。

insert into employees(EMPLOYEE_ID,last_name,email,HIRE_DATE,JOB_ID) values(400,‘oldlu’,‘oldlu@sxt.cn’,STR_TO_DATE(‘2049 年 5 月 5 日’,‘%Y 年%m 月%d日’),‘IT_PROG’);

示例:查询 employees 表中雇员名字为 King 的雇员的入职日期,要求显示格式为 yyyy 年 MM 月 dd 日。

select DATE_FORMAT(hire_date,‘%Y 年%m 月%d日’) from employees where last_name = ‘King’;

  • format的格式
    • %a 缩写星期名
      %b 缩写月名
      %C 月,数值
      %D 带有英文前缀的月中的天
      %d 月的天,数值(00-31)
      %e 月的天,数值(0-31)
      %f 微秒
      %H 小时(00-23)
      %h 小时(01-12)
      %I 小时(01-12)
      %i 分钟,数值(00-59)
      %j 年的天(001-366)
      %k 小时(0-23)
      %l 小时(1-12)
      %M 月名
      %m 月,数值(00-12)
      %p AM或PM
      %r 时间,12-小时(hh:mm:ssAM或PM)
      %S 秒(00-59)
      %S 秒(00-59)
      %T 时间,24-小时(hh:mm:ss)
      %U 周(00-53)星期日是一周的第一天
      %u 周(00-53)星期一是一周的第一天
      %V 周(01-53)星期日是一周的第一天,与%X使用
      %v 周(01-53)星期一是一周的第一天,与%x使用
      %W 星期名
      %w 周的天(0=星期日,6=星期六)
      %X 年,其中的星期日是周的第一天,4位,与%V使用
      %x 年,其中的星期一是周的第一天,4位,与%v使用
      %Y 年,4位
      %y 年.2位

通用函数

因为处理数据的时候需要进行判断,比单纯的转换要复杂的多

  • IF(expr,v1,v2) 如果表达式expr成立,返回结果v1;否则,返回结果v2。类似三元运算
  • IFNULL(v1,v2) 如果v1的值不为NULL,则返回v1,否则返回v2。
  • ISNULL(expression) 判断表达式是否为NULL,返回布尔值
  • NULLIF(expr1, expr2) 比较两个参数是否相同,如果参数expr1与expr2相等返回NULL,否则返回expr1
  • COALESCE(expr1, expr2,…,expr_n) 返回参数中的第一个非空表达式(从左向右)
  • CASE expression WHEN condition1 THEN result1 WHEN condition2 THEN result2 … WHEN conditionN THEN resultN ELSE result END CASE 表示函数开始,END 表示函数结束。如果 condition1 成立,则返回 result1, 如果condition2 成立,则返回 result2,当全部不成立则返回 result,而当有一个成立之后,后面的就不执行了。
    • 类似switch语句
    • CASE expression参数
    • WHEN condition1条件1 THEN result1
    • WHEN condition2条件2 THEN result2
    • WHEN conditionN THEN resultN
    • ELSE result
    • END

示例:查询部门编号是50或者80的员工信息,包含他们的名字、薪水、佣金。在income列中,如果有佣金则显示‘SAL+COMM’,无佣金则显示’SAL’。

SELECT last_name, salary, commission_pct, if(ISNULL(commission_pct),‘SAL’,‘SAL+COMM’) income
FROM employees
WHERE department_id IN (50, 80);

多行函数/聚合函数

聚合函数也称之为多行函数,组函数或分组函数。聚合函数不象单行函数,聚合函数对行的分组进行操作,对每组给出一个结果。如果在查询中没有指定分组,那么聚合函数则将查询到的结果集视为一组。

  • AVG(expression) 返回一个表达式的平均值
    • 求平均值的,只能对数字类型进行运算,如果不是数字类型,返回0
    • SELECT AVG(salary) FROM employees;
  • SUM(expression) 返回总和
    • 只能接收数字类型,否则返回0
    • SELECT sum(salary) FROM employees;
  • COUNT(expression) 返回查询的记录总数
    • count(*) 统计表中的所有行数
    • count(列) 统计列里面,非空的行数
    • count(distinct 列) 过滤重复的值
    • 示例:显示员工表中部门编号是80中有佣金的雇员人数。
    • SELECT COUNT(commission_pct) FROM employees WHERE department_id = 80;
  • MAX(expression) 返回字段 最大值
    • 可以是字符串、数字、日期类型
  • MIN(expression) 返回字段最小值
    • 可以是字符串、数字、日期类型

多表查询

从两个及以上个表里面获取数据

因为是多表查询,最后我们还要组合这些列,如果不加限制条件,列与列的组合会特别多。为了避免这种情况,我们需要使用where加上条件。

  • 多表查询分类
    • sql92标准:内连接(等值连接 、非等值连接 、 自连接)。
    • sql99标准:内连接、外连接(左外、右外、全外(MySQL不支持全外连接))、交叉连接。

等值连接

等值连接特点:

1 多表等值连接的结果为多表的交集部分;

2 n表连接,至少需要n-1个连接条件;

3 多表不分主次,没有顺序要求;

4 一般为表起别名,提高阅读性和性能;

5 可以搭配排序、分组、筛选….等子句使用;

等值连接也被称为简单连接或内连接

  • SELECT 子句指定要返回的列名:
    employee last name、employee number 和 department number,这些是 EMPLOYEES 表中的列
    department number、department name 和 location ID,这些是 DEPARTMENTS 表中的列
  • FROM 子句指定数据库必须访问的两个表:EMPLOYEES 表、DEPARTMENTS 表
  • WHERE 子句指定表怎样被连接:EMPLOYEES.DEPARTMENT_ID =DEPARTMENTS.DEPARTMENT_ID,

因为 DEPARTMENT_ID 列是两个表的同名列,它必须用表名做前缀以避免混淆。

添加查询条件除连接之外,可能还要求用 WHERE 子句在连接中限制一个或多个表中的行。

  • 限制不明确的列名

    • 需要在 WHERE 子句中用表的名字限制列的名字以避免含糊不清。没有表前缀,DEPARTMENT_ID列可能来自 DEPARTMENTS 表,也可能来自 EMPLOYEES 表,这种情况下需要添加表前缀来执行查询。
    • 如果列名在两个表之间不相同,就不需要限定列。但是,使用表前缀可以改善性能,因为MySQL服务器可以根据表前缀找到对应的列。
    • 必须限定不明确的列名也适用于在其它子句中可能引起混淆的那些列,例如 SELECT子句或 ORDERBY 子句。
  • 意思就是,我们在连接两个表的时候,需要用列去匹配列。如果在两个表里面,这俩个要连接的列是同名的,我们需要去用表来调用列名,让数据库可以判断列的来源。如果两个列的名称不同,就没啥了

  • 表别名定义原则
    表别名不易过长,短一些更好。
    表别名应该是有意义的。
    表别名只对当前的 SELECT 语句有效。

  • 因为两个表里面大多数的列名是相同的,为了区分这些列的来源,需要频繁调用表名,很麻烦,我们可以给表也取别名,在from后面就可以。

示例:查询雇员 King 所在的部门名称。

select d.department_name from employees e,departments d where e.dept_id = d.department_id and e.last_name = ‘King’;

  • select d.department_name
  • from employees e,departments d 数据来源,以及表别名
  • where e.dept_id = d.department_id and e.last_name = ‘King’; 等值连接

示例:显示每个雇员的 last name、departmentname 和 city。

SELECT e.last_name, d.department_name, l.city FROM employees e, departments d, locations l WHERE e.department_id = d.department_id AND d.location_id = l.location_id;

  • SELECT e.last_name, d.department_name, l.city
  • FROM employees e, departments d, locations l
  • WHERE e.department_id = d.department_id AND d.location_id = l.location_id;

用到了三个表,需要使用两个表链接。

表e和表d,使用的departmentid链接。表d和表l使用locationid链接。

然后这三个表的数据就链接到一起了

非等值链接

除了使用等号来链接两个表的方式,都是非等值链接。

示例:查询所有雇员的薪水级别。

select e.last_name,j.grade_level from employees e ,job_grades j where e.salary between j.lowest_sal and j.highest_sal;

  • select e.last_name,j.grade_level 表e的名称,表j的级别
  • from employees e ,job_grades j 表e是雇员信息,表j是薪水
  • where e.salary between j.lowest_sal and j.highest_sal; 链接条件是:表e的salary在表j的两个列值之间。

自链接

连接一个表到它自己。有时需要连接一个表到它自己。为了找到每个雇员的经理的名字,则需要连接EMPLOYEES 表到它自己,或执行一个自连接。

通俗的解释:在一个表里面,一个人的信息有两个,一个是自己的工号,和自己上司的工号。

一个员工想要找到自己的上司,就需要拿着他的上司工号去表里面找员工工号。

因为员工的上司,他的工号和员工的工号在同一列。

所以,需要表的上司工号列,匹配员工的工号列

案例:查询每个雇员的经理的名字以及雇员的名字,雇员名字列别名为W,经理列别名为M。

SELECT worker.LAST_NAME W,manager.LAST_NAME M from employees worker,employees manager where worker.MANAGER_ID = manager.EMPLOYEE_ID;

  • SELECT worker.LAST_NAME W,manager.LAST_NAME M

  • from employees worker,employees manager 通过给一个表起两个别名的方式,去调用表

  • where worker.MANAGER_ID = manager.EMPLOYEE_ID;

以上的三种链接方式是sql92的链接方式

下面是sql99的链接方式

MySQL支持部分sql99的链接

交叉链接cross join

就是笛卡尔乘积。

即每个元素都和表里面的所有元素都匹配一次。

在sql92里面,不写where条件就是笛卡尔乘积了。

当然在日常使用中都是尽量避免出现笛卡尔乘积,这个链接方式了解即可。

示例:使用交叉连接查询 employees 表与 departments 表。

select * from employees cross join departments;

自然链接 NATURAL JOIN

会去两个表里面找列名相同,列类型也相同的列。并自动把这些列作为两个表链接的条件。

自然链接可以看作是自动的等值连接

示例:使用自然连接查询所有有部门的雇员的名字以及部门名称。

select e.last_name,d.department_name from employees e natural join departments d;

  • select e.last_name,d.department_name
  • from employees e
  • natural join departments d; 类似之前的where条件

内连接 INNER JOIN ON

  • 语法:
    SELECT 查询列表
    FROM 表1 别名
    INNER JOIN 连接表(INNER关键字可省略) 这里就是替换where的地方
    ON 连接条件;

示例:查询雇员名字为 Fox 的雇员 ID ,薪水与部门名称。

select e.employee_id,e.salary,d.department_name from employees e inner JOIN departments d on e.department_id = d.department_id where e.last_name = ‘Fox’;

  • select e.employee_id,e.salary,d.department_name
  • from employees e
  • inner JOIN departments d
  • on e.department_id = d.department_id
  • where e.last_name = ‘Fox’;

外连接 OUTER JOIN

我们之前的连接都是匹配两个表共有的数据。外连接就是两个表不匹配的数据,也能显示出来。

左外连接就是使用外连接的时候,靠左侧的表的哪些不匹配的列,显示出来。

右外连接,就是靠右侧的表的不匹配列,显示出来。

是既显示共有的数据,也显示不匹配的数据。

如果把左外连接的内容和右外连接的内容,以及匹配内容都显示出来,就是全外连接

只显示匹配的内容,就是内连接。使用where匹配出来的内容,都是内连接。

  • 孤儿数据 就是外连接中不匹配的数据
  • 左外连接left outer join

左边的表 (EMPLOYEES) 中即使没有与 DEPARTMENTS 表中匹配的行,该查询也会取回 EMPLOYEES 表中所有的行。

示例:查询所有雇员的名字以及他们的部门名称,包含那些没有部门的雇员。

select e.last_name,d.department_name from employees e LEFT OUTER JOIN departments d on e.dept_id = d.department_id;

  • select e.last_name,d.department_name
  • from employees e
  • LEFT OUTER JOIN departments d
  • on e.dept_id = d.department_id;

全外连接FULL OUTER JOIN

MySQL不支持全外连接,但是可以使用union实现

UNION: 可以将两个查询结果集合并,返回的行都是唯一的,如同对整个结果集合使用了DISTINCT。

UNION ALL: 只是简单的将两个结果合并后就返回。这样,如果返回的两个结果集中有重复的数据, 那么返回的结果集就会包含重复的数据了。

  • 语法: (SELECT 投影列 FROM 表名 LEFT OUTER JOIN 表名 ON 连接条件) UNION (SELECT 投影列 FROM 表名 RIGHT OUTER JOIN 表名 ON 连接条件)

数据分组GROUP BY

group by子句需要在where子句和order by子句之间。他可以单独出现,但是另外两个出现的时候,他要遵守规则

原则

  • 使用 WHERE 子句,可以在划分行成组以前过滤行。
  • 如果有WHERE子句,那么GROUP BY 子句必须在WHERE的子句后面。
  • 在 GROUP BY 子句中必须包含列。

下面是包含一个 GROUP BY 子句 SELECT 语句的求值过程:

  • SELECT 子句指定要返回的列:
  • 在 EMPLOYEES 表中的部门号
    • GROUP BY 子句中指定分组的所有薪水的平均值
    • FROM 子句指定数据库必须访问的表:EMPLOYEES 表。
  • WHERE 子句指定被返回的行。因为无 WHERE 子句默认情况下所有行被返回。
  • GROUP BY 子句指定行怎样被分组。行用部门号分组,所以AVG 函数被应用于薪水列,以计算每个部门的平均薪水。

示例:计算每个部门的员工总数。

SELECT DEPARTMENT_ID, COUNT(*) FROM employees GROUP BY DEPARTMENT_ID;

在多列上使用分组

在组中分组

可以列出多个 GROUP BY 列返回组和子组的摘要结果。可以用GROUP BY子句中的列的顺序确定结果的默认排序顺序。

  • select
  • group by 列1,列2

可以同时给多个列进行分组,会按照group by后面的列的顺序,依次分组。就是先根据列1,划分组,然后再根据列2,把列1分好的组,再次分组。

示例:计算每个部门的不同工作岗位的员工总数

SELECT e.DEPARTMENT_ID, e.JOB_ID,COUNT(*)FROM employees e GROUP BY e.DEPARTMENT_ID,e.JOB_ID;

约束分组结果HAVING

HAVING 子句是对查询出结果集分组后的结果进行过滤

用 WHERE 子句约束选择的行,用 HAVING 子句约束组。

语法结构:在group by后面,order by前面

子查询/内查询

语法:

  • select
  • from
  • where
    • (select
    • from。。);在圆括号里面的就是子查询

子查询在主查讯之前先执行一次

子查询的结果给主查讯,作为一个条件

示例:查询与Fox同一部门的同事,并显示他们的名字与部门ID。

select e.LAST_NAME,e.DEPARTMENT_ID FROM employees e

where e.DEPARTMENT_ID =

(select e1.DEPARTMENT_ID

from employees e1

where e1.last_name = ‘Fox’);

单行子查询

操作有:=,<,>,<=,>=,<>

这个操作就是主查讯的where参数接收子查询的值。

示例:查询 Fox的同事,但是不包含他自己。

select empl.last_name

from employees empl

where empl.department_id = (select e.department_id from employees e where e.last_name = ‘Fox’) 这里是用等号去匹配数据

and empl.last_name<> ‘Fox’;

既有子查询也有条件过略

在匹配的时候,单行子查询,只能匹配一个返回值。

只能返回一行数据。

多行子查询

多行比较符有:IN,ANY,ALL

返回内容不止一条数据,也不能使用单行子查询的操作符号

  • IN 让匹配的内容等于接受值,等于每一个接受值
  • ANY
  • ALL

ANY 运算符
ANY 运算符比较一个值与一个子查询返回的每一个值。

< ANY 意思是小于最大值。
> ANY 意思是大于最小值。
= ANY 等同于 IN。

ALL 运算符比较一个值与子查询返回的每个值。
< ALL 意思是小于最小值。
> ALL 意思是大于最大值,

内查询返回的值含有空值,并因此整个查询无返回行,原因是用大于、小于或不等于比较Null值,都返回null。所以,只要空值可能是子查询结果集的一部分,就不能用 NOT IN 运算符。NOT IN 运算符相当于 <> ALL。

示例:查找各部门收入为部门最低的那些雇员。显示他们的名字,薪水以及部门 ID。

select em.last_name,em.salary,em.department_id from employees em where em.salary in(select min(e.salary) from employees e group by e.department_id);

  • select em.last_name,em.salary,em.department_id
  • from employees em
  • where em.salary in(
  • select min(e.salary)
  • from employees e
  • group by e.department_id);

索引

就目前来说,我学习索引这块的知识很模糊,只知道创建,删除索引。完全不知道如何使用索引,只知道它的作用,还有优缺点。比较迷茫,不知道在干什么,这一块先跳过。以后用到了再回来复盘。

索引的建立对于MySQL 的高效运行是很重要的,索引可以大大提高MySQL 的检索速度。

  • 索引优点:

    1. 通过创建唯一性索引,可以保证数据库表中的每一行数据的唯一性;
    2. 可以加快数据的检索速度;
    3. 可以加速表与表之间的连接;
    4. 在使用分组和排序进行检索的时候,可以减少查询中分组和排序的时间;
  • 索引缺点

    1. 创建索引和维护索引 要耗费时间,这种时间随着数据量的增加而增加;
    2. 索引需要占用物理空间,数据量越大,占用空间越大;
    3. 会降低表的增删改的效率,因为每次增删改索引都需要进行动态维护;
  • 什么时候需要创建索引

    1. 频繁作为查询条件的字段应该创建索引;
    2. 查询中排序的字段创建索引将大大提高排序的速度(索引就是排序加快速查找);
    3. 查询中统计或者分组的字段;
  • 什么时候不需要创建索引

    1. 频繁更新的字段不适合创建索引,因为每次更新不单单是更新记录,还会更新索引,保存索引文件;
    2. where条件里用不到的字段,不创建索引;
    3. 表记录太少,不需要创建索引;
    4. 经常增删改的表;
    5. 数据重复且分布平均的字段,因此为经常查询的和经常排序的字段建立索引。注意某些数据包 含大量重复数据,因此他建立索引就没有太大的效果,例如性别字段,只有男女,不适合建立索引;
  • MySQL中的索引类型

    • 普通索引:最基本的索引,它没有任何限制。
    • 唯一索引:索引列的值必须唯一,但允许有空值,如果是组合索引,则列值的组合必须唯一。
    • 主键索引:特殊的索引,唯一的标识一条记录,不能为空,一般用primary key来约束。
    • 联合索引:在多个字段上建立索引,能够加速查询到速度。

事务TCL(面试可能问)

事务是指作为单个逻辑工作单元执行的一系列操作,要么完全地执行,要么完全地不执行。

  • 事务定义(Transaction)

    • 事务是一个最小的不可再分的工作单元;通常一个事务对应一个完整的业务(例如银行账户转账业务,该业务就是一个最小的工作单元)
    • 一个完整的业务需要批量的DML(insert、update、delete)语句共同联合完成
    • 事务只和DML语句有关,或者说DML语句才有事务。这个和业务逻辑有关,业务逻辑不同,DML语句的个数不同
  • 事务四大特征(ACID)

    • 原子性(ATOMICITY)
      事务中的操作要么都不做,要么就全做。
    • 一致性(CONSISTENCY)
      一个事务应该保护所有定义在数据上的不变的属性(例如完整性约束)。在完成了一个成功的事务时,数据应处于一致的状态。
    • 隔离性(ISOLATION)
      一个事务的执行不能被其他事务干扰。
    • 持久性(DURABILITY)
      一个事务一旦提交,它对数据库中数据的改变就应该是永久性的。
  • 事务类型

    • 显式事务
      需要我们手动的提交或回滚(撤销)。DML 语言中的所有操作都是显示事务操作。
    • 隐式事务
      数据库自动提交不需要我们做任何处理,同时也不具备回滚性。DDL、DCL 语言都是隐式事务操作
  • start transaction 事务开启

  • commit 事物提交

  • rollback 事物回滚,撤销只能在提交之前,因为提交之后,数据库就永久改变了。

就是我们在执行修改数据的操作的时候,为了避免出错,需要预先模拟一遍。

使用start tranction开启这个事务操作,也就是开始模拟,在模拟的时候出错可以使用rollback撤销上一步。

然后我们写操作语句 。等我们做完全部的操作之后,检查无误,就输入commit提交。

把模拟好的数据提交。数据库才开始真正的操作。也就是事务的持久性。

事务并发

  • 脏读(读取未提交数据)
    指一个事务读取了另外一个事务未提交的数据。
    A事务读取B事务尚未提交的数据,此时如果B事务发生错误并执行回滚操作,那么A事务读取到的数据就是脏数据。

    • 说白了就是多线程没处理好,线程之间会互相影响。
  • 不可重复读(前后多次读取,数据内容不一致)
    在一个事务内读取表中的某一行数据,多次读取结果不同。
    事务A在执行读取操作,由整个事务A比较大,前后读取同一条数据需要经历很长的时间 。而在事务A第一次读取数据,比如此时读取了小明的年龄为20岁,事务B执行更改操作,将小明的年龄更改为30岁,此时事务A第二次读取到小明的年龄时,发现其年龄是30岁,和之前的数据不一样了,也就是数据不重复了,系统不可以读取到重复的数据,成为不可重复读。

  • 幻读(前后多次读取,数据总量不一致)
    是指在一个事务内读取到了别的事务插入的数据,导致前后读取数量总量不一致。
    事务A在执行读取操作,需要两次统计数据的总量,前一次查询数据总量后,此时事务B执行了新增数据的操作并提交后,这个时候事务A读取的数据总量和之前统计的不一样,就像产生了幻觉一样,平白无故的多了几条数据,成为幻读。

  • 事务的隔离级别
    事务的隔离级别用于决定如何控制并发用户读写数据的操作。数据库是允许多用户并发访问的,如果多个用户同时开启事务并对同一
    数据进行读写操作的话,有可能会出现脏读、不可重复读和幻读问题,所以MySQL中提供了四种隔离级别来解决上述问题。

  • 事务的隔离级别从低到高依次为:

    • READ UNCOMMITTED 读 未提交,三种问题都会出现
    • READ COMMITTED 读 提交,可能出现不可重复读和幻读
    • REPEATABLE READ 重复读,可能出现幻读
    • SERIALIZABLE 串行,不会出现任何问题

查看事务的隔离级别:select @@transaction_isolation;

  • 设置事务隔离级别
    • 因为我们在使用MySQL的时候,我们是客户端,无法去修改数据库本身那个服务端的权限。我们修改的事务隔离,也只针对我们自己设置。
    • set session transaction isolation level 事务隔离级别(read uncommitted);

用户管理

  • 创建用户

CREATE USER username IDENTIFIED BY ‘password’;

  • 查看用户

SELECT USER,HOST FROM mysql.user;

  • 权限管理
    新用户创建完后是无法登陆的,需要分配权限。
    GRANT 权限 ON 数据库.表 TO 用户名@登录主机 IDENTIFIED BY “密码”
    • 主要有两种权限,一个是登录权限,一个是数据库使用权限
    • 登录权限
      • % 匹配所有主机
      • localhost localhost 不会被解析成 IP 地址,直接通过 UNIXsocket 连接,本机连接
      • 127.0.0.1 会通过 TCP/IP 协议连接,并且只能在本机访问
      • :: 1 ::1 就是兼容支持 ipv6 的,表示同 ipv4 的 127.0.0. 1
    • 使用权限
      • all [privileges] 服务器 所有权限
        select 表、列 选择行
        insert 表、列 插入行
        update 表、列 更新行
        delete 表 删除行
        create 数据库、表、索引 创建
        drop 数据库、表、视图 删除
        reload 服务器 允许使用flush语句
        shutdown 服务器 关闭服务
        process 服务器 查看线程信息
        file 服务器 文件操作
        grant option 数据库、表、存储过程 授权
        references 数据库、表 外键约束的父表
        index 表 创建/删除索引
        alter 表 修改表结构
        show databases 服务器 查看数据库名称
        super 服务器 超级权限
        create temporary tables 表 创建临时表
        lock tables 数据库 锁表
        execute 存储过程 执行
        replication client 服务器 允许查看主/从/二进制日志状态
        replication slave 服务器 主从复制
        create view 视图 创建视图
        show view 视图 查看视图
        create routine 存储过程 创建存储过程
        alter routine 存储过程 修改/删除存储过程
        create user 服务器 创建用户
        event 数据库 创建/更改/删除/查看事件
        trigger 表 触发器
        create tablespace 服务器 创建/更改/删除表空间/日志文件
        proxy 服务器 代理成为其它用户
        usage 服务器 没有权限
  • 格式:GRANT ALL PRIVILEGES ON . TO ‘username’@‘localhost’ IDENTIFIED BY ‘password’

示例:为 u_sxt 用户分配只能查询 bjsxt 库中的 emp 表,并且只能在本机登陆的权限。

grant select ON bjsxt.emp to ‘u_sxt’@‘localhost’ IDENTIFIED by ‘sxt’;

  • 刷新权限

每当调整权限后,通常需要执行以下语句刷新权限。

FLUSH PRIVILEGES;

  • 删除用户

DROP USER username@localhost;

MySQL 分页查询

在 MySQL 数据库中使用 LIMIT 子句进行分页查询。
MySQL 分页中开始位置为 0。
分页子句在查询语句的最后侧

  • LIMIT子句

SELECT 投影列 FROM 表名 WHERE 条件 ORDER BY LIMIT 开始位置,查询数量;

示例:查询雇员表中所有数据按 id 排序,实现分页查询,每次返回两条结果。

select * from employees order by employees_id limit 0,2;

  • LIMIT OFFSET子句

SELECT 投影列 FROM 表名 WHERE 条件 ORDER BY LIMIT 查询数量 OFFSET 开始位置;

示例:查询雇员表中所有数据按 id 排序,使用 LIMIT OFFSET 实现分页查询,每次返回两条结果。

select * from employees order by employees_id limit 2 offset 4;

python操作MySQL

  • 安装PyMySQL模块
  • 链接数据库 connection=connect(host,port,db,user,password,charset)
    • host 是主机
    • port 端口3306
    • db 什么库
    • charset 编码方式,默认utf8
import pymysql

connection = pymysql.connect(host='localhost',port=3306,user='root',passwd='root',db='test1',charset='utf8')
print(connection)

得到返回值,就是连接成功<pymysql.connections.Connection object at 0x000002507FE98B20>

  • cursor对象

    • cursor=connection.cursor()
    • close() 关闭
    • execute(选项,参数) 执行sql语句
  • 获取数据

    • fetchone() 获取查询的第一行数据
    • next() 获取下一条数据
    • fetchall() 获取所有数据,一行是一个元组。整个数据也是元组
import pymysql
connection = pymysql.connect(host='localhost',port=3306,user='root',passwd='root',db='test1',charset='utf8')
cursor = connection.cursor()   创建数据库对象
sql = 'select * from student'  sql语句
cursor.execute(sql)   执行sql
list1 = cursor.fetchall()   拿到返回数据,可以使用遍历打印数据
print(list1)

修改数据库

在python里面修改数据的时候,需要事务处理

import pymysql

connection = pymysql.connect(host='localhost',port=3306,user='root',passwd='root',db='test1',charset='utf8')
cursor = connection.cursor()
sql = "insert into student values('3','张三','男','24','计科');"
cursor.execute(sql)
list1 = cursor.fetchall()
print(list1)
connection.commit()  提交数据

基于MySQL的音乐器

  • 带有用户登录功能
  • 验证用户身份,用户注册等操作
  • 还有用户音乐列表,以及增删查改操作

INSERT into users(username,passwd,phone_number,created_time,role) VALUES(‘张三’,‘123456’,‘123’,‘2025-3-11’,‘1’)

三张表,users表,存放用户信息

musiclibrary表存放音乐信息

user_favorites表放用户id和音乐id,就是用户的个人喜好。所有用户的个人喜好都在这个表里面

关于音乐下载的,可以看我以前的爬虫文章,复制源代码,运行就是一个音乐下载器了。再配合这个音乐播放器,非常好用,安利一下。链接:
https://blog.csdn.net/FZ51111/article/details/143540349?spm=1011.2415.3001.5331

  • 第一次,手搓这个音乐播放器,有很多可以优化的地方
"""
基于MySQL的音乐器
有用户登录功能
还要用户注册
用户个人的音乐列表,还有对应的增删查
"""
import re
import threading

import pygame
from PyQt5.QtWidgets import QApplication, QWidget, QMessageBox, QStackedWidget, QVBoxLayout, QFileDialog
import sys
from PyQt5 import QtCore, QtGui, QtWidgets
from PyQt5.QtGui import QIcon
import pymysql
from datetime import datetime
from playsound import playsound


class Ui_denglu(QWidget):
    def setupUi(self, denglu):
        denglu.setObjectName("denglu")
        denglu.resize(420, 281)
        font = QtGui.QFont()
        font.setBold(True)
        font.setWeight(75)
        denglu.setFont(font)
        self.verticalLayout = QtWidgets.QVBoxLayout(denglu)
        self.verticalLayout.setObjectName("verticalLayout")
        self.horizontalLayout = QtWidgets.QHBoxLayout()
        self.horizontalLayout.setObjectName("horizontalLayout")
        self.label = QtWidgets.QLabel(denglu)
        self.label.setObjectName("label")
        self.horizontalLayout.addWidget(self.label)
        self.lineEdit = QtWidgets.QLineEdit(denglu)
        self.lineEdit.setObjectName("lineEdit")
        self.horizontalLayout.addWidget(self.lineEdit)
        self.verticalLayout.addLayout(self.horizontalLayout)
        self.horizontalLayout_2 = QtWidgets.QHBoxLayout()
        self.horizontalLayout_2.setObjectName("horizontalLayout_2")
        self.label_2 = QtWidgets.QLabel(denglu)
        self.label_2.setObjectName("label_2")
        self.horizontalLayout_2.addWidget(self.label_2)
        self.lineEdit_2 = QtWidgets.QLineEdit(denglu)
        self.lineEdit_2.setObjectName("lineEdit_2")
        self.lineEdit_2.setEchoMode(QtWidgets.QLineEdit.Password)
        self.horizontalLayout_2.addWidget(self.lineEdit_2)
        self.verticalLayout.addLayout(self.horizontalLayout_2)
        self.horizontalLayout_3 = QtWidgets.QHBoxLayout()
        self.horizontalLayout_3.setObjectName("horizontalLayout_3")
        spacerItem = QtWidgets.QSpacerItem(40, 20, QtWidgets.QSizePolicy.Expanding, QtWidgets.QSizePolicy.Minimum)
        self.horizontalLayout_3.addItem(spacerItem)
        self.pushButton = QtWidgets.QPushButton(denglu)
        self.pushButton.setObjectName("pushButton")
        self.horizontalLayout_3.addWidget(self.pushButton)
        spacerItem1 = QtWidgets.QSpacerItem(40, 20, QtWidgets.QSizePolicy.Expanding, QtWidgets.QSizePolicy.Minimum)
        self.horizontalLayout_3.addItem(spacerItem1)
        self.verticalLayout.addLayout(self.horizontalLayout_3)
        self.horizontalLayout_4 = QtWidgets.QHBoxLayout()
        self.horizontalLayout_4.setObjectName("horizontalLayout_4")
        self.label_3 = QtWidgets.QLabel(denglu)
        self.label_3.setStyleSheet("color:\'#EEB422\'")
        self.label_3.setObjectName("label_3")
        self.horizontalLayout_4.addWidget(self.label_3)
        spacerItem2 = QtWidgets.QSpacerItem(40, 20, QtWidgets.QSizePolicy.Expanding, QtWidgets.QSizePolicy.Minimum)
        self.horizontalLayout_4.addItem(spacerItem2)
        self.pushButton_2 = QtWidgets.QPushButton(denglu)
        self.pushButton_2.setObjectName("pushButton_2")
        self.horizontalLayout_4.addWidget(self.pushButton_2)
        self.verticalLayout.addLayout(self.horizontalLayout_4)
        self.retranslateUi(denglu)

    def retranslateUi(self, denglu):
        _translate = QtCore.QCoreApplication.translate
        denglu.setWindowTitle(_translate("denglu", "音乐播放器"))
        self.label.setText(_translate("denglu", "账号"))
        self.label_2.setText(_translate("denglu", "密码"))
        self.pushButton.setText(_translate("denglu", "登录"))
        self.label_3.setText(_translate("denglu", "没有账号请先注册>>>>>"))
        self.pushButton_2.setText(_translate("denglu", "注册"))

class Ui_zhuce(QWidget):
    def setupUi(self, zhuce):
        zhuce.setObjectName("zhuce")
        zhuce.resize(378, 230)
        font = QtGui.QFont()
        font.setBold(True)
        font.setWeight(75)
        zhuce.setFont(font)
        self.verticalLayout = QtWidgets.QVBoxLayout(zhuce)
        self.verticalLayout.setObjectName("verticalLayout")
        self.horizontalLayout = QtWidgets.QHBoxLayout()
        self.horizontalLayout.setObjectName("horizontalLayout")
        self.label = QtWidgets.QLabel(zhuce)
        self.label.setObjectName("label")
        self.horizontalLayout.addWidget(self.label)
        self.lineEdit = QtWidgets.QLineEdit(zhuce)
        self.lineEdit.setObjectName("lineEdit")
        self.horizontalLayout.addWidget(self.lineEdit)
        self.verticalLayout.addLayout(self.horizontalLayout)
        self.horizontalLayout_2 = QtWidgets.QHBoxLayout()
        self.horizontalLayout_2.setObjectName("horizontalLayout_2")
        self.label_2 = QtWidgets.QLabel(zhuce)
        self.label_2.setObjectName("label_2")
        self.horizontalLayout_2.addWidget(self.label_2)
        self.lineEdit_2 = QtWidgets.QLineEdit(zhuce)
        self.lineEdit_2.setObjectName("lineEdit_2")
        self.lineEdit_2.setEchoMode(QtWidgets.QLineEdit.Password)
        self.horizontalLayout_2.addWidget(self.lineEdit_2)
        self.verticalLayout.addLayout(self.horizontalLayout_2)
        self.horizontalLayout_3 = QtWidgets.QHBoxLayout()
        self.horizontalLayout_3.setObjectName("horizontalLayout_3")
        self.label_4 = QtWidgets.QLabel(zhuce)
        self.label_4.setObjectName("label_4")
        self.horizontalLayout_3.addWidget(self.label_4)
        self.lineEdit_4 = QtWidgets.QLineEdit(zhuce)
        self.lineEdit_4.setObjectName("lineEdit_4")
        self.lineEdit_4.setEchoMode(QtWidgets.QLineEdit.Password)
        self.horizontalLayout_3.addWidget(self.lineEdit_4)
        self.verticalLayout.addLayout(self.horizontalLayout_3)
        self.horizontalLayout_4 = QtWidgets.QHBoxLayout()
        self.horizontalLayout_4.setObjectName("horizontalLayout_4")
        self.label_3 = QtWidgets.QLabel(zhuce)
        self.label_3.setObjectName("label_3")
        self.horizontalLayout_4.addWidget(self.label_3)
        self.lineEdit_3 = QtWidgets.QLineEdit(zhuce)
        self.lineEdit_3.setObjectName("lineEdit_3")
        self.horizontalLayout_4.addWidget(self.lineEdit_3)
        self.verticalLayout.addLayout(self.horizontalLayout_4)
        self.horizontalLayout_5 = QtWidgets.QHBoxLayout()
        self.horizontalLayout_5.setObjectName("horizontalLayout_5")
        spacerItem = QtWidgets.QSpacerItem(40, 20, QtWidgets.QSizePolicy.Expanding, QtWidgets.QSizePolicy.Minimum)
        self.horizontalLayout_5.addItem(spacerItem)
        self.pushButton = QtWidgets.QPushButton(zhuce)
        self.pushButton.setObjectName("pushButton")
        self.horizontalLayout_5.addWidget(self.pushButton)
        spacerItem1 = QtWidgets.QSpacerItem(40, 20, QtWidgets.QSizePolicy.Expanding, QtWidgets.QSizePolicy.Minimum)
        self.horizontalLayout_5.addItem(spacerItem1)
        self.pushButton_2 = QtWidgets.QPushButton(zhuce)
        self.pushButton_2.setObjectName("pushButton_2")
        self.horizontalLayout_5.addWidget(self.pushButton_2)
        spacerItem2 = QtWidgets.QSpacerItem(40, 20, QtWidgets.QSizePolicy.Expanding, QtWidgets.QSizePolicy.Minimum)
        self.horizontalLayout_5.addItem(spacerItem2)
        self.verticalLayout.addLayout(self.horizontalLayout_5)

        self.retranslateUi(zhuce)
        QtCore.QMetaObject.connectSlotsByName(zhuce)

    def retranslateUi(self, zhuce):
        _translate = QtCore.QCoreApplication.translate
        zhuce.setWindowTitle(_translate("zhuce", "注册"))
        self.label.setText(_translate("zhuce", "账号    "))
        self.label_2.setText(_translate("zhuce", "密码    "))
        self.label_4.setText(_translate("zhuce", "确认密码"))
        self.label_3.setText(_translate("zhuce", "手机号  "))
        self.pushButton.setText(_translate("zhuce", "确定"))
        self.pushButton_2.setText(_translate("zhuce", "取消"))

class Ui_management(QWidget):
    def setupUi(self, management):
        management.setObjectName("management")
        management.resize(666, 555)
        font = QtGui.QFont()
        font.setBold(True)
        font.setWeight(75)
        management.setFont(font)
        self.horizontalLayout_3 = QtWidgets.QHBoxLayout(management)
        self.horizontalLayout_3.setObjectName("horizontalLayout_3")
        self.verticalLayout = QtWidgets.QVBoxLayout()
        self.verticalLayout.setObjectName("verticalLayout")
        self.label = QtWidgets.QLabel(management)
        self.label.setAlignment(QtCore.Qt.AlignCenter)
        self.label.setObjectName("label")
        self.verticalLayout.addWidget(self.label)
        self.listWidget = QtWidgets.QListWidget(management)
        self.listWidget.setObjectName("listWidget")
        self.verticalLayout.addWidget(self.listWidget)
        self.horizontalLayout = QtWidgets.QHBoxLayout()
        self.horizontalLayout.setObjectName("horizontalLayout")
        self.pushButton = QtWidgets.QPushButton(management)
        self.pushButton.setObjectName("pushButton")
        self.horizontalLayout.addWidget(self.pushButton)
        self.pushButton_2 = QtWidgets.QPushButton(management)
        self.pushButton_2.setObjectName("pushButton_2")
        self.horizontalLayout.addWidget(self.pushButton_2)
        self.verticalLayout.addLayout(self.horizontalLayout)
        self.horizontalLayout_3.addLayout(self.verticalLayout)
        self.frame = QtWidgets.QFrame(management)
        self.frame.setFrameShape(QtWidgets.QFrame.VLine)
        self.frame.setFrameShadow(QtWidgets.QFrame.Raised)
        self.frame.setObjectName("frame")
        self.horizontalLayout_3.addWidget(self.frame)
        self.verticalLayout_2 = QtWidgets.QVBoxLayout()
        self.verticalLayout_2.setObjectName("verticalLayout_2")
        self.label_2 = QtWidgets.QLabel(management)
        self.label_2.setAlignment(QtCore.Qt.AlignCenter)
        self.label_2.setObjectName("label_2")
        self.verticalLayout_2.addWidget(self.label_2)
        self.listWidget_2 = QtWidgets.QListWidget(management)
        self.listWidget_2.setObjectName("listWidget_2")
        self.verticalLayout_2.addWidget(self.listWidget_2)
        self.horizontalLayout_2 = QtWidgets.QHBoxLayout()
        self.horizontalLayout_2.setObjectName("horizontalLayout_2")
        self.pushButton_4 = QtWidgets.QPushButton(management)
        self.pushButton_4.setObjectName("pushButton_4")
        self.horizontalLayout_2.addWidget(self.pushButton_4)
        self.pushButton_5 = QtWidgets.QPushButton(management)
        self.pushButton_5.setObjectName("pushButton_5")
        self.horizontalLayout_2.addWidget(self.pushButton_5)
        self.pushButton_6 = QtWidgets.QPushButton(management)
        self.pushButton_6.setObjectName("pushButton_6")
        self.horizontalLayout_2.addWidget(self.pushButton_6)
        self.verticalLayout_2.addLayout(self.horizontalLayout_2)
        self.horizontalLayout_3.addLayout(self.verticalLayout_2)

        self.retranslateUi(management)
        QtCore.QMetaObject.connectSlotsByName(management)

    def retranslateUi(self, management):
        _translate = QtCore.QCoreApplication.translate
        management.setWindowTitle(_translate("management", "数据库管理"))
        self.label.setText(_translate("management", "用户管理"))
        self.pushButton.setText(_translate("management", "增加用户"))
        self.pushButton_2.setText(_translate("management", "删除用户"))
        self.label_2.setText(_translate("management", "音乐管理"))
        self.pushButton_4.setText(_translate("management", "增加音乐"))
        self.pushButton_5.setText(_translate("management", "删除音乐"))
        self.pushButton_6.setText(_translate("management", "刷新页面"))

class Ui_MusicLibrary(QWidget):
    def setupUi(self, MusicLibrary):
        MusicLibrary.setObjectName("MusicLibrary")
        MusicLibrary.resize(651, 481)
        self.verticalLayout_2 = QtWidgets.QVBoxLayout(MusicLibrary)
        self.verticalLayout_2.setObjectName("verticalLayout_2")
        self.horizontalLayout_2 = QtWidgets.QHBoxLayout()
        self.horizontalLayout_2.setObjectName("horizontalLayout_2")
        self.verticalLayout = QtWidgets.QVBoxLayout()
        self.verticalLayout.setObjectName("verticalLayout")
        self.label = QtWidgets.QLabel(MusicLibrary)
        self.label.setObjectName("label")
        self.verticalLayout.addWidget(self.label)
        self.listWidget = QtWidgets.QListWidget(MusicLibrary)
        self.listWidget.setObjectName("listWidget")
        self.verticalLayout.addWidget(self.listWidget)
        self.horizontalLayout_2.addLayout(self.verticalLayout)
        self.frame = QtWidgets.QFrame(MusicLibrary)
        self.frame.setFrameShape(QtWidgets.QFrame.VLine)
        self.frame.setFrameShadow(QtWidgets.QFrame.Raised)
        self.frame.setObjectName("frame")
        self.horizontalLayout_2.addWidget(self.frame)
        self.verticalLayout_3 = QtWidgets.QVBoxLayout()
        self.verticalLayout_3.setObjectName("verticalLayout_3")
        self.label_3 = QtWidgets.QLabel(MusicLibrary)
        self.label_3.setObjectName("label_3")
        self.verticalLayout_3.addWidget(self.label_3)
        self.listWidget_2 = QtWidgets.QListWidget(MusicLibrary)
        self.listWidget_2.setObjectName("listWidget_2")
        self.verticalLayout_3.addWidget(self.listWidget_2)
        self.horizontalLayout_2.addLayout(self.verticalLayout_3)
        self.verticalLayout_2.addLayout(self.horizontalLayout_2)
        self.frame_2 = QtWidgets.QFrame(MusicLibrary)
        self.frame_2.setFrameShape(QtWidgets.QFrame.HLine)
        self.frame_2.setFrameShadow(QtWidgets.QFrame.Raised)
        self.frame_2.setObjectName("frame_2")
        self.verticalLayout_2.addWidget(self.frame_2)
        self.horizontalLayout = QtWidgets.QHBoxLayout()
        self.horizontalLayout.setObjectName("horizontalLayout")
        spacerItem = QtWidgets.QSpacerItem(40, 20, QtWidgets.QSizePolicy.Expanding, QtWidgets.QSizePolicy.Minimum)
        self.horizontalLayout.addItem(spacerItem)
        self.pushButton_3 = QtWidgets.QPushButton(MusicLibrary)
        self.pushButton_3.setObjectName("pushButton_3")
        self.horizontalLayout.addWidget(self.pushButton_3)
        spacerItem1 = QtWidgets.QSpacerItem(40, 20, QtWidgets.QSizePolicy.Expanding, QtWidgets.QSizePolicy.Minimum)
        self.horizontalLayout.addItem(spacerItem1)
        self.pushButton = QtWidgets.QPushButton(MusicLibrary)
        self.pushButton.setObjectName("pushButton")
        self.horizontalLayout.addWidget(self.pushButton)
        spacerItem2 = QtWidgets.QSpacerItem(40, 20, QtWidgets.QSizePolicy.Expanding, QtWidgets.QSizePolicy.Minimum)
        self.horizontalLayout.addItem(spacerItem2)
        self.pushButton_2 = QtWidgets.QPushButton(MusicLibrary)
        self.pushButton_2.setObjectName("pushButton_2")
        self.horizontalLayout.addWidget(self.pushButton_2)
        spacerItem3 = QtWidgets.QSpacerItem(40, 20, QtWidgets.QSizePolicy.Expanding, QtWidgets.QSizePolicy.Minimum)
        self.horizontalLayout.addItem(spacerItem3)
        self.pushButton_4 = QtWidgets.QPushButton(MusicLibrary)
        self.pushButton_4.setObjectName("pushButton_4")
        self.horizontalLayout.addWidget(self.pushButton_4)
        spacerItem4 = QtWidgets.QSpacerItem(40, 20, QtWidgets.QSizePolicy.Expanding, QtWidgets.QSizePolicy.Minimum)
        self.horizontalLayout.addItem(spacerItem4)
        self.verticalLayout_2.addLayout(self.horizontalLayout)

        self.retranslateUi(MusicLibrary)
        QtCore.QMetaObject.connectSlotsByName(MusicLibrary)

    def retranslateUi(self, MusicLibrary):
        _translate = QtCore.QCoreApplication.translate
        MusicLibrary.setWindowTitle(_translate("MusicLibrary", "音乐播放"))
        self.label.setText(_translate("MusicLibrary", "音乐库"))
        self.label_3.setText(_translate("MusicLibrary", "个人喜欢"))
        self.pushButton_3.setText(_translate("MusicLibrary", "播放"))
        self.pushButton.setText(_translate("MusicLibrary", "添加"))
        self.pushButton_2.setText(_translate("MusicLibrary", "移除"))
        self.pushButton_4.setText(_translate("MusicLibrary", "刷新"))

class Ui_Form(QWidget):
    def setupUi(self, Form):
        Form.setObjectName("Form")
        Form.resize(254, 285)
        font = QtGui.QFont()
        font.setBold(True)
        font.setWeight(75)
        Form.setFont(font)
        self.verticalLayout = QtWidgets.QVBoxLayout(Form)
        self.verticalLayout.setObjectName("verticalLayout")
        self.horizontalLayout = QtWidgets.QHBoxLayout()
        self.horizontalLayout.setObjectName("horizontalLayout")
        self.label = QtWidgets.QLabel(Form)
        self.label.setObjectName("label")
        self.horizontalLayout.addWidget(self.label)
        self.lineEdit = QtWidgets.QLineEdit(Form)
        self.lineEdit.setObjectName("lineEdit")
        self.horizontalLayout.addWidget(self.lineEdit)
        self.verticalLayout.addLayout(self.horizontalLayout)
        self.horizontalLayout_3 = QtWidgets.QHBoxLayout()
        self.horizontalLayout_3.setObjectName("horizontalLayout_3")
        self.label_3 = QtWidgets.QLabel(Form)
        self.label_3.setObjectName("label_3")
        self.horizontalLayout_3.addWidget(self.label_3)
        self.lineEdit_3 = QtWidgets.QLineEdit(Form)
        self.lineEdit_3.setObjectName("lineEdit_3")
        self.horizontalLayout_3.addWidget(self.lineEdit_3)
        self.verticalLayout.addLayout(self.horizontalLayout_3)
        self.horizontalLayout_4 = QtWidgets.QHBoxLayout()
        self.horizontalLayout_4.setObjectName("horizontalLayout_4")
        self.label_4 = QtWidgets.QLabel(Form)
        self.label_4.setObjectName("label_4")
        self.horizontalLayout_4.addWidget(self.label_4)
        self.lineEdit_4 = QtWidgets.QLineEdit(Form)
        self.lineEdit_4.setObjectName("lineEdit_4")
        self.horizontalLayout_4.addWidget(self.lineEdit_4)
        self.verticalLayout.addLayout(self.horizontalLayout_4)
        self.horizontalLayout_5 = QtWidgets.QHBoxLayout()
        self.horizontalLayout_5.setObjectName("horizontalLayout_5")
        self.label_5 = QtWidgets.QLabel(Form)
        self.label_5.setObjectName("label_5")
        self.horizontalLayout_5.addWidget(self.label_5)
        self.lineEdit_5 = QtWidgets.QLineEdit(Form)
        self.lineEdit_5.setObjectName("lineEdit_5")
        self.horizontalLayout_5.addWidget(self.lineEdit_5)
        self.verticalLayout.addLayout(self.horizontalLayout_5)
        self.horizontalLayout_7 = QtWidgets.QHBoxLayout()
        self.horizontalLayout_7.setObjectName("horizontalLayout_7")
        spacerItem = QtWidgets.QSpacerItem(40, 20, QtWidgets.QSizePolicy.Expanding, QtWidgets.QSizePolicy.Minimum)
        self.horizontalLayout_7.addItem(spacerItem)
        self.pushButton = QtWidgets.QPushButton(Form)
        font = QtGui.QFont()
        font.setBold(True)
        font.setWeight(75)
        self.pushButton.setFont(font)
        self.pushButton.setObjectName("pushButton")
        self.horizontalLayout_7.addWidget(self.pushButton)
        spacerItem1 = QtWidgets.QSpacerItem(40, 20, QtWidgets.QSizePolicy.Expanding, QtWidgets.QSizePolicy.Minimum)
        self.horizontalLayout_7.addItem(spacerItem1)
        self.verticalLayout.addLayout(self.horizontalLayout_7)

        self.retranslateUi(Form)
        QtCore.QMetaObject.connectSlotsByName(Form)

    def retranslateUi(self, Form):
        _translate = QtCore.QCoreApplication.translate
        Form.setWindowTitle(_translate("Form", "管理员增加用户"))
        self.label.setText(_translate("Form", "用户名"))
        self.label_3.setText(_translate("Form", "密码  "))
        self.label_4.setText(_translate("Form", "手机号"))
        self.label_5.setText(_translate("Form", "权限  "))
        self.pushButton.setText(_translate("Form", "确定"))

class AddPeopleWindow(QWidget):
    def __init__(self):
        super().__init__()
        self.ui = Ui_Form()
        self.ui.setupUi(self)
        self.ui.pushButton.clicked.connect(self.queding)

    def queding(self):
        username0 = self.ui.lineEdit.text()
        passwd0 = self.ui.lineEdit_3.text()
        phone0 = self.ui.lineEdit_4.text()
        role0 = self.ui.lineEdit_5.text()
        now = datetime.now()
        formatted_now = now.strftime("%Y-%m-%d %H:%M:%S")

        if all([username0, passwd0, phone0]):

            connection = pymysql.connect(host='localhost', port=3306, user='root', passwd='root',db='music_project', charset='utf8')
            cursor = connection.cursor()
            sql0 = "SELECT * FROM users WHERE username = %s AND passwd = %s AND phone_number = %s"
            cursor.execute(sql0, (username0, passwd0, phone0))
            records = cursor.fetchall()

            if not records:
                sql1 = "INSERT INTO users(username, passwd, phone_number, created_time, role) VALUES(%s, %s, %s, %s, %s)"
                cursor.execute(sql1, (username0, passwd0, phone0, formatted_now, '1'))
                connection.commit()
                QMessageBox.information(None, '注册成功', f'用户 {username0} 注册成功', QMessageBox.Ok)
                cursor.close()
                connection.close()
                return
            else:
                text1 = '用户已存在'
        else:
            text1 = '有空选项'

        QMessageBox.information(self, '提示', text1, QMessageBox.Ok)

class MyWindow(QStackedWidget):
    def __init__(self):
        super().__init__()

        # 创建页面
        self.denglu_page = Ui_denglu()
        self.zhuce_page = Ui_zhuce()
        self.musiclibrary_page=Ui_MusicLibrary()
        self.managerment_page=Ui_management()

        # 设置页面的 UI
        self.denglu_widget = QWidget()
        self.denglu_page.setupUi(self.denglu_widget)

        self.zhuce_widget = QWidget()
        self.zhuce_page.setupUi(self.zhuce_widget)

        self.musiclibrary_widget = QWidget()
        self.musiclibrary_page.setupUi(self.musiclibrary_widget)

        self.managerment_widget = QWidget()
        self.managerment_page.setupUi(self.managerment_widget)

        # 添加页面到 QStackedWidget
        self.addWidget(self.denglu_widget)
        self.addWidget(self.zhuce_widget)
        self.addWidget(self.musiclibrary_widget)
        self.addWidget(self.managerment_widget)

        # 绑定按钮的点击事件
        self.denglu_page.pushButton_2.clicked.connect(self.show_registration_page)
        self.zhuce_page.pushButton.clicked.connect(self.show_login_page)
        self.zhuce_page.pushButton_2.clicked.connect(self.zhuce_cancel)
        self.denglu_page.pushButton.clicked.connect(self.login)
        self.managerment_page.pushButton.clicked.connect(self.add_people)
        self.managerment_page.pushButton_2.clicked.connect(self.rm_people)
        self.managerment_page.pushButton_4.clicked.connect(self.add_music)
        self.managerment_page.pushButton_5.clicked.connect(self.rm_music)
        self.managerment_page.pushButton_6.clicked.connect(self.set_music)
        self.musiclibrary_page.pushButton_3.clicked.connect(self.play_music)
        self.musiclibrary_page.pushButton.clicked.connect(self.add_music_to_people)
        self.musiclibrary_page.pushButton_2.clicked.connect(self.rm_music_from_peole)
        self.musiclibrary_page.pushButton_4.clicked.connect(self.flush_list)

        # 初始页面
        self.setCurrentIndex(0)
        self.data={'username8':'','passwd8':'','id':''}

    def flush_list1(self):
        connection = pymysql.connect(host='localhost', port=3306, user='root', passwd='root', db='music_project',
                                     charset='utf8')
        cursor = connection.cursor()
        sql = f"select * from user_favorites where uid = '{self.data['id']}' order by mid"
        cursor.execute(sql)
        list1 = cursor.fetchall()
        self.musiclibrary_page.listWidget_2.clear()
        row_str = '歌曲\t歌手\tid'
        self.musiclibrary_page.listWidget_2.addItem(row_str)
        for row in list1:
            sql1 = f"select * from music_library where id = '{row[1]}'"
            cursor.execute(sql1)
            list0 = cursor.fetchall()
            for row0 in list0:
                row_str = f'{row0[1]}\t{row0[2]}\t{row0[0]}'
                self.musiclibrary_page.listWidget_2.addItem(row_str)
        cursor.close()
        connection.close()

    def flush_list(self):
        self.flush_list1()
        connection = pymysql.connect(host='localhost', port=3306, user='root', passwd='root', db='music_project',charset='utf8')
        cursor = connection.cursor()
        sql = 'select * from music_library order by id'
        cursor.execute(sql)
        list1 = cursor.fetchall()
        self.musiclibrary_page.listWidget.clear()
        row_str = '歌曲\t歌手\tid'
        self.musiclibrary_page.listWidget.addItem(row_str)
        for row in list1:
            row_str = f'{row[1]}\t{row[2]}\t{row[0]}'
            self.musiclibrary_page.listWidget.addItem(row_str)
        cursor.close()
        connection.close()

    def play_music(self):
        self.music_item0 = self.musiclibrary_page.listWidget_2.selectedItems()
        self.music_item1 = self.musiclibrary_page.listWidget.selectedItems()
        if not self.music_item0 and not self.music_item1:
            return
        self.music_item = self.music_item0 if self.music_item0 else self.music_item1
        connection = pymysql.connect(host='localhost', port=3306, user='root', passwd='root', db='music_project',charset='utf8')
        cursor = connection.cursor()
        for item in self.music_item:
            text = item.text()
            parts = text.split()
            mid = parts[-1] if parts else None
            sql = f"select * from music_library where id = '{mid}'"
            cursor.execute(sql)
            connection.commit()
            result_list = cursor.fetchall()
            print(result_list[0][-1])
            path = result_list[0][-1]
            print(path)
            music_thread = threading.Thread(target=self.music_start, args=(path,))
            music_thread.daemon = True
            music_thread.start()

        cursor.close()
        connection.close()

    def music_start(self,path):
        pygame.mixer.init()
        pygame.mixer.music.load(path)
        pygame.mixer.music.play()
        while pygame.mixer.music.get_busy():  # 等待音频播放完成
            pass

    def add_music_to_people(self):
        self.music_item = self.musiclibrary_page.listWidget.selectedItems()
        if not self.music_item:
            return

        connection = pymysql.connect(host='localhost', port=3306, user='root', passwd='root', db='music_project',charset='utf8')
        cursor = connection.cursor()
        for item in self.music_item:
            text = item.text()
            parts = text.split()
            mid = parts[-1]
            sql1 = f"select * from user_favorites where mid = '{mid}' and uid = '{self.data['id']}'"
            cursor.execute(sql1)
            result_list = cursor.fetchall()
            if not result_list:
                sql = f"insert into user_favorites(mid,uid) values('{mid}','{self.data['id']}')"
                cursor.execute(sql)
                connection.commit()

        cursor.close()
        connection.close()

    def rm_music_from_peole(self):
        self.music_item = self.musiclibrary_page.listWidget_2.selectedItems()
        if not self.music_item:
            return

        connection = pymysql.connect(host='localhost', port=3306, user='root', passwd='root', db='music_project',charset='utf8')
        cursor = connection.cursor()
        for item in self.music_item:
            text = item.text()
            parts = text.split()
            mid = parts[-1]
            print(id)
            sql = f"DELETE FROM user_favorites WHERE mid = '{mid}' and uid = '{self.data['id']}'"
            cursor.execute(sql)
            connection.commit()

        cursor.close()
        connection.close()

    def add_people(self):
        self.second_window = AddPeopleWindow()
        self.second_window.show()

    def rm_people(self):
        self.music_item = self.managerment_page.listWidget.selectedItems()
        for item in self.music_item:
            template1 = item.text()
            parts = template1.split()
            id = parts[2]
            connection = pymysql.connect(host='localhost', port=3306, user='root', passwd='root', db='music_project',charset='utf8')
            cursor = connection.cursor()
            sql = f"delete from users where id='{id}'"
            cursor.execute(sql)
            connection.commit()
            cursor.close()
            connection.close()

    def add_music(self):
        options = QFileDialog.Options()
        file_path, _ = QFileDialog.getOpenFileName(self, "选择音乐文件", "", "音乐 (*.mp3)", options=options)

        if file_path:
            match = re.search(r'([^\\/]+)-([^\\/]+)-\d+\.mp3$', file_path)
            if match:
                singer = match.group(1)
                song_name = match.group(2)
                connection = pymysql.connect(host='localhost', port=3306, user='root', passwd='root',db='music_project', charset='utf8')
                cursor = connection.cursor()
                sql0 = f"SELECT * FROM music_library WHERE artist='{singer}' AND title='{song_name}' AND file_path='{file_path}'"
                cursor.execute(sql0)
                result0 = cursor.fetchall()
                if not result0:
                    sql = f"INSERT INTO music_library(artist, title, file_path) VALUES('{singer}', '{song_name}', '{file_path}')"
                    cursor.execute(sql)
                    connection.commit()
                    cursor.close()
                    connection.close()
                    return
                else:
                    text3='歌曲已存在'
            else:
                text3 = "没有匹配到歌手和歌名"
        else:
            text3 = '没有选中歌曲'
        QMessageBox.information(self, '提示', text3, QMessageBox.Ok)

    def rm_music(self):
        self.music_item = self.managerment_page.listWidget_2.selectedItems()
        if not self.music_item:
            return

        connection = pymysql.connect(host='localhost', port=3306, user='root', passwd='root', db='music_project',charset='utf8')
        cursor = connection.cursor()

        for item in self.music_item:
            text = item.text()
            parts = text.split()
            id = parts[-1]
            print(id)
            sql = "DELETE FROM music_library WHERE id = %s"
            cursor.execute(sql, (id,))
            connection.commit()

        cursor.close()
        connection.close()

    def set_list1(self):
        connection = pymysql.connect(host='localhost', port=3306, user='root', passwd='root', db='music_project',
                                     charset='utf8')
        cursor = connection.cursor()
        sql = 'select * from users order by id'
        cursor.execute(sql)
        list1 = cursor.fetchall()
        self.managerment_page.listWidget.clear()
        row_str = '用户名\t密码\tid\t手机号\t创建时间\t\t\t最近登录\t\t\t权限'
        self.managerment_page.listWidget.addItem(row_str)
        for row in list1:
            row_str = f'{row[0]}\t{row[1]}\t{row[2]}\t{row[3]}\t{row[4]}\t{row[5]}\t{row[6]}'
            self.managerment_page.listWidget.addItem(row_str)
        cursor.close()
        connection.close()

    def set_music(self):
        self.set_list1()
        connection = pymysql.connect(host='localhost', port=3306, user='root', passwd='root', db='music_project',
                                     charset='utf8')
        cursor = connection.cursor()
        sql = 'select * from music_library order by id'
        cursor.execute(sql)
        list1 = cursor.fetchall()
        self.managerment_page.listWidget_2.clear()
        row_str = '歌曲\t歌手\tid'
        self.managerment_page.listWidget_2.addItem(row_str)
        for row in list1:
            row_str = f'{row[1]}\t{row[2]}\t{row[0]}'
            self.managerment_page.listWidget_2.addItem(row_str)
        cursor.close()
        connection.close()

    # 登录后跳转用户界面/管理员界面
    def login(self):
        username2 = self.denglu_page.lineEdit.text()
        password2 = self.denglu_page.lineEdit_2.text()
        if username2:
            if password2:
                connection = pymysql.connect(host='localhost', port=3306, user='root', passwd='root',db='music_project', charset='utf8')
                cursor = connection.cursor()
                sql = f"select * from users where username='{username2}' and passwd='{password2}'"
                cursor.execute(sql)
                count = cursor.fetchall()
                self.data['username8'] = username2
                self.data['passwd8'] = password2
                self.data['id'] = count[0][2]
                if count:
                    if count[0][6] == 1:
                        self.setCurrentIndex(2)
                        self.resize(666, 555)
                    else:
                        self.setCurrentIndex(3)
                        self.resize(555, 444)
                    return
                else:
                    text2='账户或密码错误'
            else:
                text2 = '密码为空'
        else:
            text2 = '用户名为空'
        QMessageBox.information(None, '登录失败', text2, QMessageBox.Ok)
        return
    # 注册取消,返回登录页面
    def zhuce_cancel(self):
        self.setCurrentIndex(0)
        self.resize(500, 300)
    # 登录跳转注册页面
    def show_registration_page(self):
        self.setCurrentIndex(1)
        self.resize(600, 400)
    # 注册完,跳转登录页面
    def show_login_page(self):
        # 获取注册信息
        usernames=self.zhuce_page.lineEdit.text()
        passwds=self.zhuce_page.lineEdit_2.text()
        passwds2=self.zhuce_page.lineEdit_4.text()
        sign=self.zhuce_page.lineEdit_3.text()
        now = datetime.now()
        formatted_now = now.strftime("%Y-%m-%d %H:%M:%S")
        # 验证注册信息
        if usernames:
            if passwds:
                if passwds2:
                    if passwds2==passwds:
                        if sign:
                            # 数据库信息验证
                            connection = pymysql.connect(host='localhost', port=3306, user='root', passwd='root',db='music_project', charset='utf8')
                            cursor = connection.cursor()
                            sql = f"select * from users where phone_number='{sign}'"
                            cursor.execute(sql)
                            count = cursor.fetchall()
                            if not count:
                                sql1 = f"INSERT into users(username,passwd,phone_number,created_time,role) VALUES('{usernames}','{passwds}','{sign}','{formatted_now}','1')"
                                cursor.execute(sql1)
                                list=cursor.fetchall()
                                print(list)
                                connection.commit()
                                QMessageBox.information(None, '注册成功', '注册成功,请返回登录', QMessageBox.Ok)
                                self.setCurrentIndex(0)
                                self.resize(500, 300)
                                return
                            else:
                                text1='用户已存在,不可重复创建,注册失败'
                        else:
                            text1 ='手机号不能为空'
                    else:
                        text1 ='两次密码不一致,请重新输入'
                else:
                    text1 ='确认密码不能为空'
            else:
                text1 ='密码不能为空'
        else:
            text1 ='用户名为空'
        QMessageBox.information(None, '注册失败', text1, QMessageBox.Ok)
        self.setCurrentIndex(1)
        self.resize(600, 400)

if __name__ == '__main__':
    app = QApplication(sys.argv)
    window = MyWindow()
    window.setWindowIcon(QIcon('E:\杂物\音乐图标.ico'))
    window.setWindowTitle("音乐播放器")
    window.resize(500, 300)
    window.show()
    sys.exit(app.exec_())

解读代码

首先要掌握MySQL数据库的操作方式,最基本的增删查改

这是存储数据的地方,需要使用SQL语言与数据库交互

然后是pyqt5的知识,知道如何设计界面,还有信号与槽函数

本身并不是很复杂,大部分的逻辑代码都在处理信息方面,比如,注册用户的时候,用户名不能为空,密码不能为空,还需要手机号不同。我设计的是通过手机号排除用户,这样就避免了用户取名的烦恼。在当今这个时代,一个身份证下面手机号的数量是有限的,通过手机号作为唯一标识就跟大数据时代使用身份证作为人的唯一依据。

一开始还想给用户也设计增删查改操作,后来发现改操作,需要再设计一个页面,还不能直接使用注册的页面。

页面的数量又增加一个。

目前的页面有:登录界面,注册界面,用户听歌界面,管理员界面,管理员添加用户界面。

我知道,有很多可以优化的地方,但是目前没必要优化,我只是联系MySQL+pyqt5仅此而已。

如果,你们想要让我优化一下,可以在评论区评论,或者私信,我会尝试一下。

难点

思路很简单,但是遇到的问题一个比一个难解决。

  • qt界面跳转功能

一开始想的是一次只展示一个界面,但是还想跳转一下界面,比如在登录界面,我们可以点击注册按钮,跳转到注册界面。注册结束之后,我们点击确定,后台进行数据库比对,返回对应的信息。然后再去登录界面。我不知道如何展示不同的界面,之前我设计的软件都是一个界面用到结束。比如,音乐下载器,打开就是一个音乐下载界面,输入信息,找歌。然后双击下载,还有翻页什么的。都是在一个界面进行的。

用的比较高级的操作就是页面刷新,就是多线程操作。还有动态控件设置。基本上都是基于一个界面实现的。

第一次写多界面的软件。才知道要使用QStackedWidget方法,把页面缓存到堆栈,通过setCurrentIndex(1)这个方法可以跳转到对应的界面。比如这里的5个界面,前四个都是使用页面缓存的方式实现的。下标从0开始,对应不同的页面。

在创建多界面的时候,还有很多要注意的地方:

  1. 主窗口需要继承类QStackedWidget
  2. 子界面类需要继承类QWidget
  3. 跳转界面的时候,要使用setCurrentIndex
  4. 界面需要先实例化界面,再给每一个界面创建一个QWidget对象,界面类里面还要接收这个QWidget对象。
  5. 然后添加页面到 QStackedWidget,把刚刚的QWidget对象在添加到QStackedWidget,使用addwidget方法。就像多行文本框一样。也就是后续使用下标跳转页面的缘由。

如果你本来就会界面跳转,那就没什么难点了

写到后面,又发现可以使用多窗口,也就是创建多个主窗口的方式,创建多个界面,也就是后来的管理员添加用户界面

  • 界面设计

在设计界面的时候,一开始就想设计三个界面,一个登录,一个注册,一个音乐器本体。

后来没想起来如何动态添加组件。之前使用uniapp的时候,有一个动态更新组件的方法,记混了,设计了好久,没实现。。。

其实是能实现,但是特别麻烦。我们根据用户的权限role,判断是管理员还是普通用户。然后在设计界面的逻辑部分,判断权限。权限不同就显示不同界面。

可能是我设计的界面不够华丽,我感觉与其进行逻辑判断动态更新界面,不如就直接设计两个界面,在登录的时候,从数据库验证身份后,直接就打开对应的界面。也不用逻辑判断了。

也许,如果管理员和用户的界面功能设计的很复杂,又很接近的情况,我会重新考虑这个部分。

  • SQL语言设计

因为涉及大量的SQL语言,也是第一次实操,避免不了遗漏或语句错误

为了逻辑代码的严谨,每一个可能出错的地方都要预先模拟一下。其实完全没必要,目前的需求不在界面设计,我听的课也就设计了一个播放界面,只有三个按钮,对应,播放,添加,删除功能,甚至用户登录都是在python的控制台输入的。我是实在看不下去,但也浪费了好多时间。

废话不多说,MySQL板块,到此为止。

下一章NoSQL数据库—Redis数据库。


http://www.kler.cn/a/586334.html

相关文章:

  • MySQL:MySQL库和表的基本操作
  • SpringBoot实现一个Redis限流注解
  • Springboot项目修改端口
  • 深入理解Spring Boot:快速构建现代化的Java应用
  • 【调研】模型输出内容的json形式content怎样处理可以转换为json?
  • kafka生成者发送消息失败报错:RecordTooLargeException
  • MCU的工作原理:嵌入式系统的控制核心
  • Elasticsearch:语义文本 - 更简单、更好、更精炼、更强大 8.18
  • Hot100算法刷题:双指针
  • c# 利用mv-cs200-10gc工业相机,识别液注的高度
  • ubuntu-学习笔记-nextjs部署相关
  • QT:文件读取
  • Webpack优化前端性能
  • SQL--算术运算符
  • MATLAB风光柴储微网粒子群算法
  • Unity透视相机下屏幕坐标转世界坐标
  • 目前人工智能的发展,判断10年、20年后的人工智能发展的主要方向,或者带动的主要产业
  • PyTorch多机训练Loss不一致问题排查指南:基于算子级一致性验证
  • TGARS2024 | LGP | 面向目标检测的通用且可控攻击
  • Deepseek-R1 VS QwQ-32B 评测对比:文本理解与生成(2)