Skip to content

数据库

数据库

6.1 数据库概述

学习目标

知道什么是数据库

知道关系型数据库和非关系型数据库的区别

一、 什么是数据库?

简单来说,数据库就是一个有组织、可高效访问和管理的电子化数据集合

我们可以用一个形象的比喻来理解:

  • 文件柜 vs. 数据库:一个装满杂乱无章文件的文件柜,找一份文件非常困难。而数据库就像一个智能文件柜,它不仅把所有文件(数据)分门别类地放好,还有一个专业的图书管理员(数据库管理系统)帮你快速找到、添加、修改或删除任何你需要的文件。

官方定义:数据库是长期存储在计算机内、有组织的、可共享的、统一管理的海量数据的集合。

二、 为什么需要数据库?

在没有数据库的时代,数据通常存储在文件(如txt、excel、csv)中,但这会带来诸多问题:

  • 数据冗余与不一致性:相同的数据可能在多个文件中重复存储,且更新时容易产生不一致。

  • 数据访问困难:需要编写复杂的程序来查找特定数据,效率低下。

  • 数据孤立性:数据分散在不同格式的文件中,难以关联和整合。

  • 并发访问异常:多个用户同时读写同一文件时,数据容易损坏。

  • 安全问题:难以精细化控制不同用户对数据的访问权限。

数据库的出现,正是为了解决上述所有问题。

三、 数据库的核心组件

我们通常所说的“数据库”其实包含两个核心部分:

数据库本身

  • 即实际存储数据的“仓库”,通常是一个或多个文件。

数据库管理系统

  • DBMS 是一种操纵和管理数据库的大型软件,用于建立、使用和维护数据库。它是用户(或应用程序)与数据库之间的接口。

  • 常见的DBMSMySQL, Oracle, PostgreSQL, SQL Server, SQLite,Redis, MongoDB, Hbase, Neo4j 等。

用户和应用程序通过 DBMS 来访问和操作数据库,而不是直接操作数据文件。

**四、 数据库分类 **

数据库根据其组织数据的方式可以分为不同类型,其中最主要的两类是:

数据库根据其组织数据的方式(数据模型)可以分为多种类型,其中最主要的两类是:

关系型数据库

  • 概念:将数据组织成一系列二维表(由行和列组成),表与表之间存在关系。

  • 核心术语

  • :存储特定类型数据的结构。

  • 行/记录:表中的一条具体数据。

  • 列/字段:数据的某个属性(如姓名、年龄)。

  • 主键:唯一标识一条记录的字段。

  • 外键:建立表与表之间关系的字段。

  • 查询语言SQL,这是一种声明式语言,你只需要告诉数据库“想要什么”,而不需要关心“如何获取”。

  • 优点:ACID特性(原子性、一致性、隔离性、持久性)保证数据强一致性、成熟稳定。

  • 代表MySQL, Oracle, PostgreSQL, SQL Server, SQLite

非关系型数据库

概念:为了应对互联网时代海量、多样、高速的数据处理需求而诞生。它没有固定的表结构,数据模型灵活。

主要类型

键值型:简单的键值对存储(如Redis)。

  • 文档型:数据存储为类似JSON的文档(如MongoDB)。

  • 列族型:按列存储数据,适合大数据分析(如HBase)。

图数据库:存储实体(节点)和它们之间的关系(边),适合社交网络、推荐系统(如Neo4j)。

优点:灵活、可扩展性强、读写性能高。

代表Redis, MongoDB, Hbase, Neo4j

五、 数据库的应用

选择 SQL 还是 NoSQL,不是一个谁更好的问题,而是一个 “更适合” 的问题。

  • 当你需要构建一个关键业务系统(如银行、电商、ERP),其中数据的准确性和一致性是生命线,并且业务逻辑复杂、涉及大量关联查询时,SQL 数据库是毋庸置疑的首选

  • 当你需要处理海量非结构化数据、要求极高的写入速度无限的水平扩展能力(如日志记录、社交媒体的动态流、物联网传感器数据),并且可以接受最终一致性时,NoSQL 数据库是更好的选择

数据库是现代信息系统的核心,无处不在:

  • 电子商务:存储商品、用户、订单信息。

  • 银行系统:管理账户、交易记录。

  • 社交网络:存储用户资料、好友关系、动态信息。

  • 物联网:收集和处理海量传感器数据。

  • 企业ERP系统:整合企业的人力、财务、供应链等数据。


简单来说,数据库是现代数字世界的基石,它以一种可靠、高效和安全的方式,帮助我们存储和管理日益增长的数据资产。 理解数据库的基本概念是进入软件开发、数据分析等领域的必备第一步。


6.2 关系型数据库 MySQL

一、SQL语言

1 SQL简介

`SQL: 结构化查询语言, 是所有关系型数据库都要遵循的规范

大白话解释: 可以理解成sql是普通话,mysql,oracle等是方言
`

2 SQL分类

`DDL: 数据定义语言:简称DDL(Data Definition Language)
        作用: 用来定义数据库对象:数据库,表,列/字段等。
        关键字: create,drop,alter等

DML: 数据操作语言:简称DML(Data Manipulation Language)
        作用:用来对数据库中表的记录进行更新。
        关键字: insert,delete,update等

DQL: 数据查询语言:简称DQL(Data Query Language)
        作用:用来查询数据库中表的记录。
        关键字: select,from,where等

DCL: 数据控制语言:简称DCL(Data Control Language)
        用来定义数据库的访问权限和安全级别,及创建用户。
`

3 SQL通用语法

`1、SQL语句可以单行或多行书写,以分号结尾。
        举例: select *  from 表名 where 条件;

2、可使用空格和缩进来增强语句的可读性
        select *
        from 表名
        where 条件;

3、MySQL数据库的SQL语句不区分大小写,关键字建议使用大写
        例如:select *  from 表名 where 条件; SELECT * FROM 表名 WHERE 条件;
        大小写切换快捷键: ctrl+shift+u

4、可以使用 /**/,--,# 的方式完成注释
        /**/:多行注释,在注释区域内可以随意换行
        -- 和 # :单行注释,写在语句开头,换行后注释截止。注意: --的后面必须有一个空格
        单行注释快捷键: ctrl+/
        多行注释快捷键: ctrl+shift+/
`

二、MySQL数据库连接

MySQL是最好的 RDMS (Relational Database Management System,关系数据库管理系统) 应用软件,目前属于 Oracle 旗下产品,MySQL 是最流行的关系型数据库管理系统中的一个

本篇使用MySQL8.0版本, MySQL Community Server进行学习

1 命令连接

`登录:
    方式1: mysql -u用户名 -p密码
    方式2: mysql -u用户名 -p 回车后再输入密码
    方式3: mysql -h主机地址 -p 回车后再输入密码
    注意: localhost默认代表本地主机,或者127.0.0.1也代表本机

登出:
    方式1: exit
    方式2: quit
    方式3: \q
    注意: 在mac/linux中使用ctrl+d/ctrl+c也能退出
`

2 工具连接

2.1 连接mysql服务

2.2 关联驱动

3 工具设置

3.1 字体

3.2 主题

3.3 mysql相关

3.4 插件

3.5 方言

三、MySQL操作

1 数据库核心操作

数据库英文单词: database

知识点:

`创建数据库: create database [if not exists] 数据库名;

使用/切换数据库: use 数据库名;

查看所有的数据库名: show databases;
`

示例:

`# 一.数据库的增删改查
# 1.创建数据库:
create database test;
create database test; # 报错,因为test已经存在
# if not exists: 如果库不存在就创建,存在就忽略
create database test1;
create database IF NOT EXISTS test1; # 存在忽略
create database IF NOT EXISTS test2; # 不存在就创建
# character set utf8: 设置编码为utf8,注意: mysql大多数版本已经都默认utf8
create database test3 CHARACTER SET utf8;
create database test4 CHARSET utf8;

# 2.切换数据库:
use test;

# 3.查看所有库:
show databases;
`

2 数据类型和约束

数据类型

`字符串类型: varchar(字符长度)

整数类型: int    注意: 默认长度是11,如果int不够用就用bigint

浮点类型: float(python默认) 或者 double(java默认)   decimal(默认是有效位数是10,小数后位数是0)

日期时间: date  datetime  year
`

数据约束

`主键约束: primary key    特点: 修饰列对应的值非空唯一

主键自增: AUTO_INCREMENT 特点: 修饰主键对应的值不指定主键字段或者用0和null占位代表自动使用自增

非空约束: not null       特点: 修饰列对应的值不能为空

唯一约束: unique         特点: 修饰列对应的值不能重复

默认约束: default        特点: 修饰列对应的值提前设置默认值

外键约束: FOREIGN KEY    特点: 保证数据的完整性:限制从表插入数据,同时也限制主表删除数据
`

3 库中表核心操作

表的英文单词: table

知识点:

`创建表: create table  表名(字段名 字段类型 [字段约束] , 字段名 字段类型 [字段约束] ...);

查看所有表: show tables;
`

示例:

`# 创建my_db数据库
create database my_db;
# 使用my_db库
use my_db;
# 操作表的前提: 先创建库,并使用它
# 需求:创建学生表,用于存储学生的姓名,年龄,身高,生日信息
create table student(
    id int,
    name VARCHAR(100),
    age int,
    height float,
    birthday date
);
`

4 表中记录增删改

知识点:

`插入数据记录: insert into 表名 (字段名...) values (具体值...) , (具体值...);
            注意1: 具体值要和前面的字段名以及顺序一一对应上
            注意2: 如果要插入的是所有字段,那么字段名可以省略(默认代表所有列都要插入数据)
            注意3: 如果要插入多条记录,values后多条数据使用 逗号 分隔

修改数据记录: update 表名 set 字段名=值 [where 条件];
            注意: 如果没有加条件就是修改对应字段的所有数据

删除数据记录: delete from 表名 [where 条件];
            注意: 如果没有加条件就是删除所有数据

清空所有数据:
    方式1: delete from 表名;   注意:delete删除所有数据,自增顺序保留,下次再插入的时候继续自增
    方式2: truncate [table] 表名;  注意:truncate删除所有数据,自增顺序重置,下次再插入数据的时候从1重新开始自增
`

示例:

`/*
操作库的前提: 先启动mysql服务,并连接它
操作表的前提: 先有库,并使用它
操作数据的前提: 先有表,并有对应字段
*/
# 创建库
create database my_db;
# 使用库
use my_db;
# 创建表
create table student(
    id int,
    name VARCHAR(100),
    age int
);
# 插入数据
# 指定字段插入一条数据
insert into student(name) values ('张三');
# 指定字段插入多条数据
insert into student(name) values ('李四'),('王五'),('赵六');
# 注意: 不指定字段本质代表指定所有字段
# 不指定字段插入一条数据
insert into student values (1,'张三',18);
# 不指定字段插入多条数据
insert into student values (1,'张三',18),(2,'李四',18),(3,'王五',18);

# 修改数据
# 修改李四的年龄为28
update student set age = 28 where name = '李四';
# 修改王五和赵六的年龄为39
update student set age = 39 where name = '王五' or name = '赵六';
# 修改赵六的id为4,年龄为40
update student set id = 4,age = 40 where name = '赵六';
# 注意: 如果没有加条件,修改的是所有数据(慎用!!!)
update student set age = 12; # 报黄警告!然后弹窗警告,如果非要执行,选择execute

# 删除数据
# 删除id为1的记录
delete from student where id = 1;
# 删除id为1,以及id为3的记录
delete from student where id = 2 or id = 3;
# 注意: 如果不加条件删除的是所有数据(慎用!!!)
delete from student; # 报黄警告!然后弹窗警告,如果非要执行,选择execute

# 为了演示truncate删除数据,重新插入多条
insert into student values (1,'张三',18),(2,'李四',18),(3,'王五',18);
# truncate也能删除所有数据
truncate table student;
`

四、单表查询

1 准备数据

`# 创建数据库: create database 库名;
CREATE DATABASE IF NOT EXISTS my_db CHARSET=utf8;

# 使用数据库: use 库名;
USE my_db;

# 创建表: create table 表名(字段名 字段类型 [约束],...);
# 建测试表
drop table if EXISTS products;
CREATE TABLE IF NOT EXISTS products
(
    id          INT PRIMARY KEY AUTO_INCREMENT, -- 商品ID
    name        VARCHAR(24)    NOT NULL,        -- 商品名称
    price       DECIMAL(10, 2) NOT NULL,        -- 商品价格
    score       DECIMAL(5, 2),                  -- 商品评分,可以为空
    is_self     VARCHAR(8),                     -- 是否自营
    category_id INT                             -- 商品类别ID
);

drop table if EXISTS category;
CREATE TABLE IF NOT EXISTS category
(
    id   INT PRIMARY KEY AUTO_INCREMENT, -- 商品类别ID
    name VARCHAR(24) NOT NULL            -- 类别名称
);

# 插入数据: insert into 表名 (字段名,字段名) values(字段值,字段值),(字段值,字段值);
# 添加测试数据
INSERT INTO category
VALUES (1, '手机'),
       (2, '电脑'),
       (3, '美妆'),
       (4, '家居');

INSERT INTO products
VALUES (1, '华为Mate50', 5499.00, 9.70, '自营', 1),
       (2, '荣耀80', 2399.00, 9.50, '自营', 1),
       (3, '荣耀80', 2199.00, 9.30, '非自营', 1),
       (4, '红米note 11', 999.00, 9.00, '非自营', 1),
       (5, '联想小新14', 4199.00, 9.20, '自营', 2),
       (6, '惠普战66', 4499.90, 9.30, '自营', 2),
       (7, '苹果Air13', 6198.00, 9.10, '非自营', 2),
       (8, '华为MateBook14', 5599.00, 9.30, '非自营', 2),
       (9, '兰蔻小黑瓶', 1100.00, 9.60, '自营', 3),
       (10, '雅诗兰黛粉底液', 920.00, 9.40, '自营', 3),
       (11, '阿玛尼红管405', 350.00, NULL, '非自营', 3),
       (12, '迪奥996', 330.00, 9.70, '非自营', 3);
`

2 基础查询

知识点:

`基础查询关键字: select:查什么    from: 从哪儿查

基础查询格式:  select [distinct] 字段名 | * from 表名;
             []:可以省略
              |: 或者
              *: 对应表的所有字段名
              distinct: 去除重复内容
              as  : 可以给表或者字段起别名
`

示例:

`-- 需求1: 查看所有商品
select * from products;
-- 需求2: 查看所有商品的名称和价格
select name,price from products;
-- 需求3: 查看所有商品的名称和价格,要求给字段名起别名展示
select name as 姓名,price as 价格 from products;
select name  姓名,price  价格 from products;
-- 需求4: 查看所有商品的名称和价格,要求给表名起别名并使用
select products.name,products.price from products;
-- 如果给表起了表名,必须用别名调用字段
select p.name,p.price from products as p;
select name,price from products as p;
-- 需求5: 查看所有的分类编号,要求去重展示
select DISTINCT category_id from products;
`

3 条件查询

知识点:

`条件查询关键字:  where

条件查询基础格式: select 字段名 from 表名 where 条件;

              比较运算符: >  <  >=  <=  !=  <>

              逻辑运算符: and  or  not

              范围 查询: 连续范围:between x and y       非连续范围: in(x,y)

              模糊 查询: 关键字:like   %:0个或者多个字符   _:一个字符

              非空 判断: 为空: is null    不为空:is not null
`

比较查询

`# 1.比较运算符: >  <  >=  <=  !=  <>

-- 需求1: 查询所有'自营'的商品
select * from products where is_self = '自营';
-- 需求2: 查询评分在'9.50'(不含)以上的商品
select * from products where score > 9.50;
-- 需求3: 查询评分在'9.50'(含)以上的商品
select * from products where score >= 9.50;
-- 需求4: 查询价格在999(不含)以下的商品
select * from products where price < 999;
-- 需求5: 查询价格在999(含)以下的商品
select * from products where price <= 999;
-- 需求6: 查询评分不等于9.30的商品
select * from products where score != 9.3;
select * from products where score <> 9.3;
`

逻辑查询

`-- and: 并且  or:或者  not:取反

-- 需求1: 查询自营商品中所有价格大于2000的商品信息
select * from products where is_self = '自营' and price > 2000;
-- 需求2: 查询商品评分在9.0(含)-9.5(含)之间的商品信息
select * from products where score >= 9 and score <= 9.5;
-- 需求3: 查询商品价格在1000(含)到3000(含)之间的商品信息
select * from products where price >= 1000 and price <= 3000;
-- 需求4: 查询价格是999或者2199或者2399的商品
select * from products where price = 999 or price = 2199 or price = 2399;
-- 需求5: 查询商品是'华为Mate50'或者'荣耀80'的商品
select * from products where name = '华为Mate50' or name = '荣耀80';
-- 需求6: 查询商品不是自营的商品
select * from products where not is_self = '自营';
select * from products where  is_self = '非自营';
-- 需求7: 查询商品不在1000到3000之间的商品
select * from products where not (price >= 1000 and price <= 3000);
select * from products where  price < 1000 or price > 3000;
`

范围查询

`-- 需求1: 查询商品价格在1000(含)到3000(含)之间的商品信息
select * from products where  price BETWEEN 1000 and 3000;
-- 注意: 以下语法是错误的
select * from products where 1000 <= price <= 3000;
-- 需求2: 查询商品不在1000到3000之间的商品
select * from products where  price not BETWEEN 1000 and 3000;
-- 需求3: 查询价格是999或者2199或者2399的商品
select * from products where price in(999,2199,2399);
-- 需求4: 查询商品是'华为Mate50'或者'荣耀80'的商品
select * from products where name in('华为Mate50','荣耀80');
`

模糊查询

`-- 关键字: like   符号 %:任意多个字符  _:任意1个字符

-- 需求1: 查询商品名称以'华'开头的商品信息
select * from products where name like '华%';
-- 需求2: 查询商品名称以'华'开头并且8个字符的商品信息
select * from products where name like '华_______';
-- 需求3: 查询商品名称以'66'结尾商品信息
select * from products where name like '%66';
-- 需求4: 查询商品名称中包含'兰'字的商品信息
select * from products where name like '%兰%';
-- 需求5: 查询商品名称中第3个字是'兰'字的商品信息
select * from products where name like '__兰%';
`

非空判断

`/*
null在sql中代表空的,没有任何意义的意思
如果数据中有空字符串'',字符串'null',一定要注意,他们和sql中的null不是一回事!!!
*/
-- 需求1:查询未评分的商品信息
select * from products where score is null;
-- 注意: 以下方式是错误的
select * from products where score = null;
select * from products where score = '';
select * from products where score = 'null';
select * from products where score is 'null';

-- 为了方便演示null和'','null'的区别,可以插入部分测试数据
insert into products(name,price) values('拯救者Y9000','9999');
insert into products(name,price) values('null','99');
insert into products(name,price) values('',0);

-- 需求2:查询商品名称是'null'的商品信息
select * from products where name = 'null';
-- 需求3:查询商品名称是''的商品信息
select * from products where name = '';
`

4 排序查询

知识点:

`排序查询关键字: order by

排序查询基础格式: select 字段名 from 表名 order by 排序字段名 asc|desc;
            asc : 升序(默认)
            desc: 降序

排序查询进阶格式: select 字段名 from 表名 order by 排序字段1名 asc|desc , 排序字段2名 asc|desc;
            注意: 如果order by后跟多个排序字段,先按照前面的字段排序,如果有相同值的情况再按照后面的排序规则排序
`

示例:

`-- 示例1:查询所有商品,并按照评分从高到低进行排序
SELECT * FROM products ORDER BY score DESC;

-- 示例2:查询所有商品,先按照评分从高到低进行排序,评分相同的再按照价格从低到高排序
SELECT * FROM products ORDER BY score DESC, price;
`

5 聚合函数

知识点:

`聚合函数: 又叫统计函数,也叫分组函数

常用聚合函数:  sum()  count()  avg()   max()  min()

聚合查询基础格式: select 聚合函数(字段名) from 表名;    注意: 此处没有分组默认整个表就是一个大的分组

注意: 聚合函数(字段名)会自动忽略null值,以后统计个数一般用count(*)统计因为它不会忽略null值
`

示例:

`# 注意: 别名不建议用中文,以下仅仅为了演示
-- 示例1:统计当前商品一共有多少件
SELECT count(id) FROM products;
SELECT count(*) FROM products;

-- 示例2:对商品评分列进行计数、求最大、求最小、求和、求平均
SELECT
    COUNT(score) AS cnt,
    MAX(score) AS max_score,
    MIN(score) AS min_score,
    SUM(score) AS total_score,
    AVG(score) AS avg_score
FROM products;

-- 示例3:统计所有非自营商品评分的平均值
SELECT
    is_self,
    AVG(score)
FROM
    products
WHERE
    is_self = '非自营';
`

6 分组查询

知识点:

`分组查询关键字: group by

分组查询基础格式: select 分组字段名,聚合函数(字段名) from 表名 group by 分组字段名;

注意: select后的字段名要么在group by后面出现过,要么写到聚合函数中,否则报错...sql_mode=only_full_group_by

分组查询进阶格式: select 分组字段名,聚合函数(字段名) from 表名 [where 非聚合条件] group by 分组字段名 [having 聚合条件];

where和having的区别?
    书写顺序: where在group by 前,having在group by后
    执行顺序: where在分组前,having在分组后
    分组函数: where后不能跟聚合条件,只能跟非聚合条件,having后可以使用聚合条件,也可以使用非聚合条件(不建议)
    应用场景: 建议大多数过滤数据都采用where,只有当遇到聚合条件的时候再使用having
    使用别名: where后不能使用别名,having后可以使用别名
`

示例:

`-- 示例1:统计每个分类的商品数量
SELECT
    category_id,
    -- ② 再聚合:对每一组的id进行count计数
    COUNT(id) AS cnt
FROM products
-- ① 先分组:按照category_id进行分组
GROUP BY category_id;

-- 示例2:统计每个分类中自营和非自营商品的数量
SELECT
    category_id,
    is_self,
    COUNT(id) AS cnt
FROM products
GROUP BY category_id, is_self;

-- 示例3:统计每个分类商品的平均价格,并筛选出平均价格低于1000的分类
SELECT category_id,
       -- 再聚合
       AVG(price)
FROM products
-- 先分组
GROUP BY category_id
-- 对分组聚合的结果进行筛选
HAVING AVG(price) < 1000;
`

7 limit查询

知识点:

`分页查询关键字: limit

分页查询基础格式: select 字段名 from 表名 limit x,y;
            x: 起始索引,默认从0开始     x = (页数-1)*y
            y: 本次查询的条数

注意: limit能完成topN需求,但是不能考虑到并列情况,此问题可以使用后期学习的开窗函数解决
`

示例:

`- 示例1:获取所有商品中,价格最高的商品信息
SELECT
    *
FROM products
ORDER BY price DESC
LIMIT 1; -- LIMIT 0, 1;

-- 示例2:将商品数据按照价格从低到高排序,然后获取第2页内容(每页3条)
SELECT
    *
FROM products
ORDER BY price
LIMIT 3, 3;

-- 示例3:当分页展示的数据不存在时,不报错,只不过查询不到任何数据
SELECT * FROM products LIMIT 20, 10;
`

8 SQL顺序

`书写顺序: SELECT -> DISTINCT -> 聚合函数 -> FROM -> WHERE -> GROUP BY -> HAVING -> ORDER BY -> LIMIT
执行顺序: FROM -> WHERE -> GROUP BY -> 聚合函数 -> HAVING -> SELECT -> DISTINCT -> ORDER BY -> LIMIT
`

五、多表查询

本质: 把多个表通过主外键关联关系连接(join)合并成一个大表,再去查询

1 多表关系

一对一 : 一个人一个身份证号

一对多: 一个分类下有多个商品

多对多: 课程和学生 一般要借助中间表,把多对多变成一对多

2 外键

`外键概念: 在从表(多方)创建一个字段,引用主表(一方)的主键,对应的这个字段就是外键。

外键特点:
    1:从表外键的值是对主表主键的引用。
    2:从表外键类型,必须与主表主键类型一致。
`

3 连接查询

3.1 数据准备

`# 使用库
use my_db;
# 创建商品表
CREATE TABLE products
(
    id          INT PRIMARY KEY AUTO_INCREMENT, -- 商品ID
    name        VARCHAR(24)    NOT NULL,        -- 商品名称
    price       DECIMAL(10, 2) NOT NULL,        -- 商品价格
    score       DECIMAL(5, 2),                  -- 商品评分,可以为空
    is_self     VARCHAR(8),                     -- 是否自营
    category_id INT                             -- 商品类别ID
);
# 创建分类表
CREATE TABLE category
(
    id   INT PRIMARY KEY AUTO_INCREMENT, -- 商品类别ID
    name VARCHAR(24) NOT NULL            -- 类别名称
);

# 添加分类数据
INSERT INTO category
VALUES (1, '手机'),
       (2, '电脑'),
       (3, '美妆'),
       (4, '家居');
# 添加商品数据
INSERT INTO products
VALUES (1, '华为Mate50', 5499.00, 9.70, '自营', 1),
       (2, '荣耀80', 2399.00, 9.50, '自营', 1),
       (3, '荣耀80', 2199.00, 9.30, '非自营', 1),
       (4, '红米note 11', 999.00, 9.00, '非自营', 1),
       (5, '联想小新14', 4199.00, 9.20, '自营', 2),
       (6, '惠普战66', 4499.90, 9.30, '自营', 2),
       (7, '苹果Air13', 6198.00, 9.10, '非自营', 2),
       (8, '华为MateBook14', 5599.00, 9.30, '非自营', 2),
       (9, '兰蔻小黑瓶', 1100.00, 9.60, '自营', 3),
       (10, '雅诗兰黛粉底液', 920.00, 9.40, '自营', 3),
       (11, '阿玛尼红管405', 350.00, NULL, '非自营', 3),
       (12, '迪奥996', 330.00, 9.70, '非自营', 3),
       (13, '百草味紫皮腰果',9,NULL,NULL,NULL);
`

3.2 交叉连接[慎用]

知识点:
`交叉连接关键字: cross join

显式交叉连接格式: select * from 左表 cross join 右表;

隐式交叉连接格式: select * from 左表,右表;

注意: 交叉连接了解即可,因为它本质就是一个错误,又叫笛卡尔积(两个表记录数的乘积)

注意: 左表和右表没有特殊含义,只是在前面是左表,在后面的是右表
`
示例:
`-- 1.交叉连接(笛卡尔积): 是一个错误!!!工作中慎用!!!
# 关键字: cross join
# 隐式交叉连接格式: select 字段名 from 左表,右表;
SELECT *
FROM
    products,
    category;
# 显式交叉连接格式: select 字段名 from 左表 cross join右表;
SELECT *
FROM
    products
        CROSS JOIN category;
`

3.3 内连接(常用)

知识点:
`内连接关键字: inner join ... on

显式内连接格式: select * from 左表 inner join 右表 on 关联条件;

隐式内连接格式:  select * from 左表 , 右表 where 关联条件;

注意: 左表和右表没有特殊含义,只是在前面是左表,在后面的是右表
`
示例:
`-- 2.内连接: 两个表的交集
# 关键字: inner join on
# 隐式内连接格式: select 字段名 from 左表,右表 where 条件;
SELECT
    c.id   cid,
    c.name cname,
    p.id   pid,
    p.name pname
FROM
    products p,
    category c
WHERE
    p.category_id = c.id;
# 显式内连接格式: select 字段名 from 左表 cross join右表;
SELECT
    c.id   cid,
    c.name cname,
    p.id   pid,
    p.name pname
FROM
    products p
        INNER JOIN category c ON p.category_id = c.id;
`

3.4 外连接

知识点:
`左外连接关键字: left outer join ... on
左外连接格式: select * from 左表 left outer join 右表 on 关联条件;

右外连接关键字: right outer join ... on
左外连接格式: select * from 左表 right outer join 右表 on 关联条件;

注意: 左表和右表没有特殊含义,只是在前面是左表,在后面的是右表
`
示例:
`-- 3.外连接
-- 需求: 分别使用左右连接查询每个分类下的所有商品,即使没有商品的分类要展示
-- 分析: 必须以分类表为主
-- 左外连接: left outer join
SELECT
    c.id   cid,
    c.name cname,
    p.id   pid,
    p.name pname
FROM
    category c
        LEFT OUTER JOIN products p ON p.category_id = c.id;
-- 右外连接: right outer join
SELECT
    c.id   cid,
    c.name cname,
    p.id   pid,
    p.name pname
FROM
    products p
        RIGHT OUTER JOIN category c ON p.category_id = c.id;
`

3.5 全外连接

知识点:
`注意: mysql中没有full outer join on这个关键字,所以不能用它来完成全外连接!

mysql中实现全外连接方式: 先查询左外连接和右外连接的结果,然后用union或者union all来实现!!!
    union : 默认去重
    union all: 不去重
`
示例:
`-- 全外连接
# union : 默认去重
SELECT *
FROM
    products p
        LEFT JOIN category c ON p.category_id = c.id
UNION
SELECT *
FROM
    products p
        RIGHT JOIN category c ON p.category_id = c.id;
`

4 自连接和子查询

4.1 准备数据

运行脚本数据:

右键数据库->run sql script -> areas.sql

4.2 自连接查询

知识点:
`自连接查询: 两个表进行关联时,如果左表和右边是同一张表,这就是自关联。

注意: 自连接必须起别名!
`
示例:
`-- 自连接查询
-- 查询'河北省'下所有城市
SELECT
    shi.id,
    shi.title,
    sheng.id,
    sheng.title
FROM
    areas sheng
        JOIN areas shi ON shi.pid = sheng.id
WHERE
    sheng.title = '河北省';

-- 查询'邯郸市'下所有的区县
SELECT
    quxian.id,
    quxian.title,
    shi.id,
    shi.title
FROM
    areas shi
        JOIN areas quxian ON quxian.pid = shi.id
WHERE
    shi.title = '邯郸市';

-- 查询'江苏省'下所有的市,以及市下面的区县信息
SELECT
    quxian.id,
    quxian.title,
    shi.id,
    shi.title,
    sheng.id,
    sheng.title
FROM
    areas sheng
        JOIN areas shi ON shi.pid = sheng.id
        JOIN areas quxian ON quxian.pid = shi.id

WHERE
    sheng.title = '河北省';
`

4.3 子查询

知识点:
`子查询: 在一个SELECT语句中嵌入了另外一个SELECT语句那么被嵌入的 SELECT 语句称之为子查询语句

注意: 子查询是辅助主查询的。
`
示例:
`-- 子查询

--  查询'河北省'下所有城市
select * from areas where pid = (select id from areas where title = '河北省');

--  查询'邯郸市'下所有区县
select * from areas where pid = (select id from areas where title = '邯郸市');
`

六、拓展[了解]

1 数据库其他操作:

`删除数据库: drop database [if exists] 数据库名;

查看当前使用的数据库: select database();

查看指定库的建库语句: show create database 数据库名;
`

2 库中表其他操作:

`删除表: drop table [if exists] 表名;

修改表名: rename table 旧表名 to 新表名;

查看指定表的建表语句: show create table 表名;

注意: 操作字段本质就是在修改表
添加字段:  alter table 表名 add [column] 字段名 字段类型 [字段约束];
删除字段:  alter table 表名 drop [column] 字段名;
修改字段名和字段类型:  alter table 表名 change [column] 旧字段名 新字段名 字段类型 [字段约束];
modify只修改字段类型: alter table 表名 modify [column]  字段名 字段类型 [字段约束];
查看字段信息: desc 表名;
`

6.3 Python操作MySQL

一、为什么要学习PyMySQL

如何实现将100000条数据插入到MySQL数据库?

答案:

如果使用之前学习的MySQL客户端来完成这个操作,那么这个工作量无疑是巨大的,我们可以通过使用程序代码的方式去连接MySQL数据库,然后对MySQL数据库进行增删改查的方式,实现100000条数据的插入,像这样使用代码的方式操作数据库就称为数据库编程。

二、准备工作

1 数据准备

`-- 创建 "京东" 数据库
create database jing_dong charset=utf8;

-- 使用 "京东" 数据库
use jing_dong;

-- 创建一个商品goods数据表
create table goods(
    id int unsigned primary key auto_increment not null,
    name varchar(150) not null,
    cate_name varchar(40) not null,
    brand_name varchar(40) not null,
    price decimal(10,3) not null default 0,
    is_show bit not null default 1,
    is_saleoff bit not null default 0
);

-- 向goods表中插入数据

insert into goods values(0,'r510vc 15.6英寸笔记本','笔记本','华硕','3399',default,default);
insert into goods values(0,'y400n 14.0英寸笔记本电脑','笔记本','联想','4999',default,default);
insert into goods values(0,'g150th 15.6英寸游戏本','游戏本','雷神','8499',default,default);
insert into goods values(0,'x550cc 15.6英寸笔记本','笔记本','华硕','2799',default,default);
insert into goods values(0,'x240 超极本','超级本','联想','4880',default,default);
insert into goods values(0,'u330p 13.3英寸超极本','超级本','联想','4299',default,default);
insert into goods values(0,'svp13226scb 触控超极本','超级本','索尼','7999',default,default);
insert into goods values(0,'ipad mini 7.9英寸平板电脑','平板电脑','苹果','1998',default,default);
insert into goods values(0,'ipad air 9.7英寸平板电脑','平板电脑','苹果','3388',default,default);
insert into goods values(0,'ipad mini 配备 retina 显示屏','平板电脑','苹果','2788',default,default);
insert into goods values(0,'ideacentre c340 20英寸一体电脑 ','台式机','联想','3499',default,default);
insert into goods values(0,'vostro 3800-r1206 台式电脑','台式机','戴尔','2899',default,default);
insert into goods values(0,'imac me086ch/a 21.5英寸一体电脑','台式机','苹果','9188',default,default);
insert into goods values(0,'at7-7414lp 台式电脑 linux )','台式机','宏碁','3699',default,default);
insert into goods values(0,'z220sff f4f06pa工作站','服务器/工作站','惠普','4288',default,default);
insert into goods values(0,'poweredge ii服务器','服务器/工作站','戴尔','5388',default,default);
insert into goods values(0,'mac pro专业级台式电脑','服务器/工作站','苹果','28888',default,default);
insert into goods values(0,'hmz-t3w 头戴显示设备','笔记本配件','索尼','6999',default,default);
insert into goods values(0,'商务双肩背包','笔记本配件','索尼','99',default,default);
insert into goods values(0,'x3250 m4机架式服务器','服务器/工作站','ibm','6888',default,default);
insert into goods values(0,'商务双肩背包','笔记本配件','索尼','99',default,default);

# 创建用户表
create TABLE user(
    id int PRIMARY KEY AUTO_INCREMENT,
    user varchar(30),
    pwd varchar(30)
);
insert into user(user,pwd) VALUE ('root','123456');
`

2 安装pymysql库

方式1

`命令: pip  install pymysql
`

方式2

`命令: pip  install pymysql -i https://pypi.tuna.tsinghua.edu.cn/simple/
`

方式3

三、pymysql模块使用

pymysql是一个纯python实现的mysql客户端库,提供了python程序中操作mysql数据库的操作

1 核心步骤:

`pymysql使用步骤:
1.导入模块
2.创建连接
3.创建游标
4.执行sql
5.关闭游标
6.关闭连接
`

2 查询操作

知识点:

`fetchone(): 一次只取一条数据
fetchall(): 一次把剩下的所有数据都取到
`

示例:

`# 1.导入pymysql模块
import pymysql

# 2.创建连接对象
conn = pymysql.connect(
    host='localhost',
    port=3306,
    user='root',
    password='root',
    database='jing_dong'
)
# 3.创建游标对象
cur = conn.cursor()
# 4.执行sql
row = cur.execute('SELECT * FROM goods')
print(f'影响了{row}行')
# fetchone: 一次拿一条数据
# data1 = cur.fetchone()
# print(data1)
# fetchall: 一次拿剩下的所有的数据
data2 = cur.fetchall()
print(data2)
# 5.关闭游标
cur.close()
# 6.关闭连接
conn.close()
`

3 增删改操作

知识点:

`拓展: 事务->要么都成功,都失败  (特性: 原子性 一致性  隔离性  持久性)

存储引擎: innodb支持事务   myisam不支持事务

增删改操作: 如果数据库底层是innodb引擎,必须手动commit提交

数据提交:commit()
数据回滚:rollback()
`

增:

`# 1.导入pymysql模块
from pymysql import connect

# 2.创建连接对象
conn = connect(
    host='127.0.0.1',
    port=3306,
    user='root',
    password='root',
    database='jing_dong'
)
# 3.创建游标对象
cur = conn.cursor()
# 4.执行sql语句
row = cur.execute("insert into goods(name,cate_name,brand_name) value ('测试','测试','测试')")
print(f'影响了{row}行')
# innoDB引擎要求必须提交,myisam引擎不支持事务(不commit也可以)
conn.commit()
# 5.关闭游标
cur.close()
# 6.关闭连接
conn.close()
`

改:

`# 1.导入pymysql模块
from pymysql import connect

# 2.创建连接对象
conn = connect(
    host='127.0.0.1',
    port=3306,
    user='root',
    password='root',
    database='jing_dong'
)
# 3.创建游标对象
cur = conn.cursor()
# 4.执行sql语句
row = cur.execute("update goods set price = 99999 where name='测试'")
print(f'影响了{row}行')
# innoDB引擎要求必须提交,myisam引擎不支持事务(不commit也可以)
conn.commit()
# 5.关闭游标
cur.close()
# 6.关闭连接
conn.close()
`

删:

`# 1.导入pymysql模块
from pymysql import connect

# 2.创建连接对象
conn = connect(
    host='127.0.0.1',
    port=3306,
    user='root',
    password='root',
    database='jing_dong'
)
# 3.创建游标对象
cur = conn.cursor()
# 4.执行sql语句
try:
    row = cur.execute("DELETE FROM goods WHERE name='测试'")
    print(f'影响了{row}行')
    # a = 1 / 0
except Exception as e:
    print(f'老弟啊,出错了呀...详情:{e}')
    conn.rollback()

# innoDB引擎要求必须提交,myisam引擎不支持事务(不commit也可以)
conn.commit()
# 5.关闭游标
cur.close()
# 6.关闭连接
conn.close()
`

四、登录案例

需求: 自己创建一个数据库,里面有user表,用于存储用户名密码(咱们直接写固定 用户名root 密码123456 )

需要完成的功能是用户键盘录入用户名和密码,使用python代码连接数据库判断是否登录成功

分析: 现实生活中如果要登录,必须先注册,用户注册完后,对应的信息就存储到对应网站后台数据库中

用户登录: 用户新输入的用户名密码和数据库中的用户名和密码比较,都相同就登录成功,否则失败

1 建表语句

`use jing_dong;
create TABLE user(
    id int PRIMARY KEY AUTO_INCREMENT,
    user varchar(30),
    pwd varchar(30)
);
insert into user(user,pwd) VALUE ('root','123456');
`

2 演示sql注入问题:

`# 用户登录
username = input('请输入用户名:')
password = input('请输入密码:')
# 后台python代码连接数据库判断
# 1.导入pymysql模块
import pymysql
# 2.创建连接对象
conn = pymysql.connect(
    host='127.0.0.1',
    port=3306,
    user='root',
    password='root',
    database='jing_dong'
)
# 3.创建游标对象
cur = conn.cursor()
# 4.执行sql语句(判断是否登录成功)
# 下面直接传递参数的方式容易出现sql注入问题-> 用户名:随意  密码: ' or 1=1 or '
# select * from user where user='root' and pwd='' or 1=1 or ''
row = cur.execute(f"select * from user where user='{username}' and pwd='{password}'")
if row:
    print('登录成功')
else:
    print('登录失败')
# 5.关闭游标
cur.close()
# 6.关闭连接
conn.close()
`

3 解决sql注入问题

`# 用户登录
username = input('请输入用户名:')
password = input('请输入密码:')
# 后台python代码连接数据库判断
# 1.导入pymysql模块
import pymysql
# 2.创建连接对象
conn = pymysql.connect(
    host='127.0.0.1',
    port=3306,
    user='root',
    password='root',
    database='jing_dong'
)
# 3.创建游标对象
cur = conn.cursor()
# 4.执行sql语句(判断是否登录成功)
# 解决sql注入问题:  sql单独定义,用%s占位,把所有参数放到列表里,把sql和参数列表传给execute
sql = "select * from user where user=%s and pwd=%s"
parm = [username,password]
row = cur.execute(sql,parm)
if row:
    print('登录成功')
else:
    print('登录失败')
# 5.关闭游标
cur.close()
# 6.关闭连接
conn.close()
`

6.4 非关系型数据库 Redis

一、Redis 概述

1 什么是 Redis

Redis(Remote Dictionary Server)是一个开源的、基于内存的键值对存储数据库,它可以用作数据库、缓存和消息中间件。

2 Redis 的特点

  • 基于内存运行:数据主要存储在内存中,读写性能极高

  • 支持数据持久化:可以将内存中的数据保存到磁盘,重启后可以再次加载使用

  • 丰富的数据类型:支持字符串(strings)、哈希(hashes)、列表(lists)、集合(sets)、有序集合(sorted sets)等

  • 支持事务:操作都是原子性,要么全部执行,要么全部不执行

  • 丰富的特性:支持发布/订阅、键过期等特性

3 Redis 的应用场景

  • 缓存系统:减轻数据库压力,提升系统性能

  • 计数器:如网站访问量、点赞数等

  • 消息队列:利用列表类型实现简单的消息队列

  • 排行榜:利用有序集合实现各种排行榜功能

  • 会话存储:存储用户会话信息

二、Redis 软件安装

1 Windows 系统安装

☆ 方法一:使用小皮工具(推荐)

☆ 方法二:使用官方版本

  • 访问 Redis 官网下载 Windows 版本

  • 解压到指定目录,如 C:\redis

  • 打开命令提示符,进入 Redis 目录

  • 运行命令:redis-server.exe redis.windows.conf

☆ 方法三:使用 WSL(Windows Subsystem for Linux)

`# 在 WSL 中安装 Redis
sudo apt update
sudo apt install redis-server

# 启动 Redis 服务
sudo service redis-server start

# 检查 Redis 状态
sudo service redis-server status
`

2 macOS 系统安装

方式1: Homebrew

Homebrew是MacOS上的一个包管理器,它可以让安装和管理软件变得非常简单。首先,你需要安装Homebrew(如果你还没有安装的话)。

打开终端,然后粘贴以下命令来安装:

`# 安装 Homebrew(如果尚未安装)
/bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh)"

# 安装 Redis
brew install redis

# 启动 Redis(后台运行)
brew services start redis

# 或手动启动
redis-server /usr/local/etc/redis.conf

# 检查是否运行
# 在另一个终端窗口中,你可以使用以下命令来检查Redis服务器是否正在运行:
redis-cli ping  # 应返回 PONG,表示Redis服务器正在正常运行。
`

方式2:源码编译安装

`# 下载最新稳定版
curl -O http://download.redis.io/redis-stable.tar.gz
tar xzf redis-stable.tar.gz
cd redis-stable

# 编译
make

# 测试编译结果
make test

# 安装到 /usr/local/bin
sudo make install

# 创建配置文件目录
sudo mkdir /etc/redis
sudo cp redis.conf /etc/redis/

# 修改配置文件(可选)
vim /etc/redis/redis.conf

# 启动 Redis
redis-server /etc/redis/redis.conf
`

3 Linux 系统安装

☆ Ubuntu/Debian 系统

`# 更新软件包列表
sudo apt update

# 安装 Redis
sudo apt install redis-server

# 启动 Redis 服务
sudo systemctl start redis-server

# 设置开机自启
sudo systemctl enable redis-server

# 检查 Redis 状态
sudo systemctl status redis-server
`

☆ CentOS/RHEL 系统

`# 安装 EPEL 仓库
sudo yum install epel-release

# 安装 Redis
sudo yum install redis

# 启动 Redis 服务
sudo systemctl start redis

# 设置开机自启
sudo systemctl enable redis
`

4 验证安装

安装完成后,可以通过以下命令测试 Redis 是否正常工作:

`# 连接 Redis 客户端
redis-cli

# 在 Redis 客户端中测试
127.0.0.1:6379> ping
PONG
127.0.0.1:6379> set test "Hello Redis"
OK
127.0.0.1:6379> get test
"Hello Redis"
127.0.0.1:6379> exit
`

6.5 Python操作Redis

一、redis-py 模块安装

1 使用 pip 安装

`# 安装 redis-py
pip install redis

# 或者指定版本
pip install redis==4.5.0
`

2 使用 conda 安装

`conda install redis-py
`

3 验证安装

上述两者都指向同一个 Python 库redis-py 库在导入时都是 import redis

安装完成后,可以在 Python 中验证是否安装成功:

`import redis
print(redis.__version__)
`

二、redis-py 模块使用

1 连接 Redis 数据库

☆ 基本连接

`import redis

# 创建 Redis 连接对象
r = redis.Redis(
    host='localhost',  # Redis 服务器地址
    port=6379,         # Redis 服务器端口
    db=0,              # 数据库编号,默认0
    password=None,     # 密码,如果没有设置密码则为None
    decode_responses=True  # 自动解码,返回字符串而不是字节
)

# 测试连接
try:
    response = r.ping()
    print("Redis 连接成功:", response)
except redis.ConnectionError as e:
    print("Redis 连接失败:", e)
`

2 字符串(String)操作

☆ 设置和获取值

`import redis

r = redis.Redis(host='localhost', port=6379, db=0, decode_responses=True)

# 设置单个值
r.set('name', '张三')
r.set('age', '25')

# 获取单个值
name = r.get('name')
age = r.get('age')
print(f"姓名: {name}, 年龄: {age}")

# 设置多个值
r.mset({'city': '北京', 'job': '工程师'})

# 获取多个值
values = r.mget(['name', 'age', 'city', 'job'])
print("多个值:", values)

# 设置值并设置过期时间(单位:秒)
r.setex('temp_data', 60, '临时数据')  # 60秒后自动删除
`

☆ 数值操作

`import redis

r = redis.Redis(host='localhost', port=6379, db=0, decode_responses=True)

# 设置初始值
r.set('counter', '0')

# 自增操作
r.incr('counter')  # +1
print("自增后:", r.get('counter'))

r.incrby('counter', 5)  # +5
print("增加5后:", r.get('counter'))

# 自减操作
r.decr('counter')  # -1
print("自减后:", r.get('counter'))

r.decrby('counter', 3)  # -3
print("减少3后:", r.get('counter'))
`

3 哈希(Hash)操作

`import redis

r = redis.Redis(host='localhost', port=6379, db=0, decode_responses=True)

# 设置哈希字段
r.hset('user:1001', 'name', '李四')
r.hset('user:1001', 'age', '30')
r.hset('user:1001', 'city', '上海')

# 获取单个字段
name = r.hget('user:1001', 'name')
print(f"用户名: {name}")

# 获取所有字段
user_info = r.hgetall('user:1001')
print("用户信息:", user_info)

# 设置多个字段
r.hmset('user:1002', {
    'name': '王五',
    'age': '28',
    'city': '广州'
})

# 获取多个字段
fields = r.hmget('user:1002', ['name', 'age'])
print("指定字段:", fields)

# 获取所有字段名
field_names = r.hkeys('user:1001')
print("字段名:", field_names)

# 获取所有字段值
field_values = r.hvals('user:1001')
print("字段值:", field_values)

# 删除字段
r.hdel('user:1001', 'city')
print("删除后:", r.hgetall('user:1001'))
`

4 列表(List)操作

`import redis

r = redis.Redis(host='localhost', port=6379, db=0, decode_responses=True)

# 从左侧添加元素
r.lpush('tasks', 'task1', 'task2', 'task3')

# 从右侧添加元素
r.rpush('tasks', 'task4', 'task5')

# 获取列表长度
length = r.llen('tasks')
print(f"列表长度: {length}")

# 获取列表元素
all_tasks = r.lrange('tasks', 0, -1)
print("所有任务:", all_tasks)

# 获取指定范围的元素
first_three = r.lrange('tasks', 0, 2)
print("前三个任务:", first_three)

# 从左侧弹出元素
left_task = r.lpop('tasks')
print(f"左侧弹出: {left_task}")

# 从右侧弹出元素
right_task = r.rpop('tasks')
print(f"右侧弹出: {right_task}")

print("剩余任务:", r.lrange('tasks', 0, -1))
`

5 集合(Set)操作

`import redis

r = redis.Redis(host='localhost', port=6379, db=0, decode_responses=True)

# 添加元素
r.sadd('tags', 'python', 'redis', 'database', 'cache')

# 获取所有元素
all_tags = r.smembers('tags')
print("所有标签:", all_tags)

# 判断元素是否存在
is_member = r.sismember('tags', 'python')
print("python是否存在:", is_member)

# 获取集合元素数量
tag_count = r.scard('tags')
print(f"标签数量: {tag_count}")

# 随机弹出一个元素
random_tag = r.spop('tags')
print(f"随机弹出: {random_tag}")

# 移除指定元素
r.srem('tags', 'database')
print("移除后:", r.smembers('tags'))

# 集合运算
r.sadd('set1', 'a', 'b', 'c')
r.sadd('set2', 'b', 'c', 'd')

# 交集
intersection = r.sinter('set1', 'set2')
print("交集:", intersection)

# 并集
union = r.sunion('set1', 'set2')
print("并集:", union)

# 差集
difference = r.sdiff('set1', 'set2')
print("差集:", difference)
`

6 有序集合(Sorted Set)操作

`import redis

r = redis.Redis(host='localhost', port=6379, db=0, decode_responses=True)

# 添加带分数的元素
r.zadd('leaderboard', {
    'player1': 1000,
    'player2': 1500,
    'player3': 800,
    'player4': 2000
})

# 按分数升序获取
asc_players = r.zrange('leaderboard', 0, -1, withscores=True)
print("升序排名:", asc_players)

# 按分数降序获取
desc_players = r.zrevrange('leaderboard', 0, -1, withscores=True)
print("降序排名:", desc_players)

# 获取元素分数
score = r.zscore('leaderboard', 'player2')
print(f"player2的分数: {score}")

# 增加元素分数
r.zincrby('leaderboard', 500, 'player1')
print("增加后player1分数:", r.zscore('leaderboard', 'player1'))

# 获取排名
rank = r.zrevrank('leaderboard', 'player4')  # 从0开始的排名
print(f"player4的排名: {rank + 1}")  # 转换为从1开始的排名

# 按分数范围获取
high_scores = r.zrangebyscore('leaderboard', 1000, 3000, withscores=True)
print("高分玩家:", high_scores)
`

7 键操作和过期时间

`import redis
import time

r = redis.Redis(host='localhost', port=6379, db=0, decode_responses=True)

# 设置键值对
r.set('session:user123', 'user_data')

# 检查键是否存在
exists = r.exists('session:user123')
print("键是否存在:", exists)

# 设置过期时间(秒)
r.expire('session:user123', 300)  # 5分钟后过期

# 获取剩余生存时间
ttl = r.ttl('session:user123')
print(f"剩余生存时间: {ttl}秒")

# 设置键值对并指定过期时间
r.setex('temp:code', 60, '123456')  # 60秒后过期

# 移除过期时间,使键永久存在
r.persist('session:user123')

# 查找匹配模式的键
all_keys = r.keys('*')
print("所有键:", all_keys)

session_keys = r.keys('session:*')
print("会话键:", session_keys)

# 删除键
r.delete('session:user123')
print("删除后是否存在:", r.exists('session:user123'))
`

三、实战案例:简单的缓存系统

`import redis
import time
import json

class RedisCache:
    def __init__(self, host='localhost', port=6379, db=0):
        self.redis_client = redis.Redis(
            host=host,
            port=port,
            db=db,
            decode_responses=True
        )

    def set_cache(self, key, value, expire_time=3600):
        """设置缓存"""
        try:
            # 将值转换为JSON字符串存储
            value_str = json.dumps(value)
            self.redis_client.setex(key, expire_time, value_str)
            return True
        except Exception as e:
            print(f"设置缓存失败: {e}")
            return False

    def get_cache(self, key):
        """获取缓存"""
        try:
            value_str = self.redis_client.get(key)
            if value_str:
                return json.loads(value_str)
            return None
        except Exception as e:
            print(f"获取缓存失败: {e}")
            return None

    def delete_cache(self, key):
        """删除缓存"""
        try:
            self.redis_client.delete(key)
            return True
        except Exception as e:
            print(f"删除缓存失败: {e}")
            return False

    def clear_all_cache(self):
        """清空所有缓存"""
        try:
            self.redis_client.flushdb()
            return True
        except Exception as e:
            print(f"清空缓存失败: {e}")
            return False

# 使用示例
if __name__ == "__main__":
    cache = RedisCache()

    # 模拟一些需要缓存的数据
    user_data = {
        'id': 1,
        'name': '张三',
        'email': 'zhangsan@example.com',
        'last_login': '2024-01-01 10:00:00'
    }

    # 设置缓存
    cache.set_cache('user:1', user_data, expire_time=300)  # 5分钟过期

    # 获取缓存
    cached_user = cache.get_cache('user:1')
    print("缓存用户数据:", cached_user)

    # 模拟耗时操作的数据缓存
    def expensive_operation(x):
        """模拟耗时操作"""
        time.sleep(2)  # 模拟2秒的计算
        return x * x

    def get_square_with_cache(x):
        """使用缓存的平方计算"""
        cache_key = f"square:{x}"
        result = cache.get_cache(cache_key)

        if result is None:
            print(f"缓存未命中,计算 {x} 的平方...")
            result = expensive_operation(x)
            cache.set_cache(cache_key, result, expire_time=60)  # 1分钟缓存
            print("计算结果已缓存")
        else:
            print(f"缓存命中,直接返回 {x} 的平方结果")

        return result

    # 测试缓存效果
    print("\n第一次计算(会缓存结果):")
    start_time = time.time()
    result1 = get_square_with_cache(5)
    end_time = time.time()
    print(f"结果: {result1}, 耗时: {end_time - start_time:.2f}秒")

    print("\n第二次计算(从缓存获取):")
    start_time = time.time()
    result2 = get_square_with_cache(5)
    end_time = time.time()
    print(f"结果: {result2}, 耗时: {end_time - start_time:.2f}秒")
`

基于 VitePress 构建 · 专注前端与 AI 实战