数据库安全和事务以及sql

1.数据库安全

  • 静态转储:即冷备份,指在转储期间不允许对数据库进行任何存取、修改操作:
    • 优点是非常快速的备份方法、容易归档(直接物理复制操作);
    • 缺点是只能提供到某一时间点上的恢复,不能做其他工作,不能按表或按用户恢
      复。
  • 动态转储:即热备份,在转储期间允许对数据库进行存取、修改操作,因此,转储和用户事务可并发执行;
    • 优点是可在表空间或数据库文件级备份,数据库扔可使用,可达到秒级恢复:
    • 缺点是不能出错,否则后果严重,若热备份不成功,所得结果几乎全部无效。
  • 完全备份:备份所有数据。
  • 差量备份:仅备份上一次完全备份之后变化的数据。
  • 增量备份:备份上一次备份之后变化的数据。
  • 日志文件:在事务处理过程中,DBMS把事务开始、事务结束以及对数据库的插
    入、删除和修改的每一次操作写入日志文件。一旦发生故障,DBMS的恢复子系统
    利用日志文件撤销事务对数据库的改变,回退到事务的初始状态。(在数据库中引入了日志文件,记录系统进行事务处理时的一切操作,但只记录操作内容,数据是存放在数据文件中,恢复现场时,依据日志文件可知有哪些操作,依据数据文件可知操作的数据是哪些。)

2.分布式数据库

  • 局部数据库位于不同的物理位置,使用一个全局DBMS将所有局部数据库联网管理,这就是分布式数据库。
    • 分片模式
      • 水平分片: 将表中水平的记录分别存放在不同的地方
      • 垂直分片:将表中的垂直的列值分别存放在不同的地方。
    • 分布透明性
      • 分片透明性: 用户或应用程序不需要知道逻辑上访问的表具体是如何分块存储的。
      • 位置透明性:应用程序不关心数据存储物理位置的改变
      • 逻辑透明性:用户或应用程序无需知道局部使用的是哪种数据模型
      • 复制透明性:用户或应用程序不关心复制的数据从何而来

"分布式数据库"(Distributed Database, DDB)是指数据在物理上分布在多个计算机节点(站点)上,但在逻辑上被视为一个统一整体的数据库系统 。它结合了数据库技术与计算机网络技术,旨在提高系统的可扩展性、可用性、性能和容错能力

一、核心特点

1.数据分布(Data Distribution)

  • 数据被拆分并存储在多个节点上,通常通过分片(Sharding)实现,包括:
    • 水平分片(Horizontal Sharding):按行拆分(如用户ID取模)。
    • 垂直分片(Vertical Sharding):按列拆分(如将用户基本信息与行为日志分开)。
  1. 数据复制(Replication)
  • 在多个节点保存相同数据副本,用于提高读性能和容错能力。常见模式:
    • 主从复制(Master-Slave)
    • 多主复制(Multi-Master)
    • 无主复制(Leaderless,如 Dynamo 风格)
  1. 一致性模型(Consistency Model)
  • 强一致性:所有节点看到的数据状态完全一致(如 Spanner)。
  • 最终一致性:系统保证在无新更新时,所有副本最终会一致(如 Cassandra)。
  • 受 CAP 定理约束:在分区发生时,必须在一致性(C)与可用性(A)之间权衡。
  1. 透明性(Transparency)
  • 对应用程序隐藏底层分布细节,包括:
    • 位置透明(Location Transparency)
    • 复制透明(Replication Transparency)
    • 故障透明(Failure Transparency)
    • 并发透明(Concurrency Transparency)
  1. 分布式事务(Distributed Transaction)
  • 跨多个节点的 ACID 操作,需协调多个参与者完成提交或回滚。

二、体系结构(基于扩展的三级模式)

在分布式数据库中,为了兼顾逻辑统一性物理分布性 ,通常将传统的三级模式(外模式、概念模式、内模式)扩展为五层模式结构 ,分为全局(Global)局部(Local) 两个层次:

1. 全局 DBMS(Global DBMS)

面向用户和应用程序,提供统一的逻辑视图。

模式 说明
全局外模式(Global External Schema) 用户或应用看到的数据视图,屏蔽了数据分布、分片、复制等细节。例如:SELECT * FROM Employees,用户不知道员工表被分片到不同节点。
全局概念模式(Global Conceptual Schema) 描述整个分布式数据库的完整逻辑结构 ,包括所有实体、关系、完整性约束等。它是全局数据的"单一逻辑模型",不涉及物理存储或位置。例如:定义 Employees(emp_id, name, dept, salary) 表结构。
分片模式(Fragmentation Schema) 描述全局概念模式中的数据如何被逻辑分片 (水平或垂直)。例如:Employees_North = σ_{region='North'}(Employees)
分布模式(Allocation Schema / Distribution Schema) 描述每个分片(fragment)物理存储在哪个节点上 ,以及是否被复制。例如:Employees_North 存储在 Site A;Employees_South 存储在 Site B 和 C(副本)

注意 :有些文献将"分片模式 + 分布模式"合称为分布描述(Distribution Description),作为全局概念模式到局部数据之间的桥梁。
分片模式,分布模式 。相当于全局内模式,管理如何存储物理的数据。

2. 局部 DBMS(Local DBMS,每个节点一个)
  • 管理本地数据存储与处理(每个站点(节点)维护自己的本地数据库。)
模式 说明
局部概念模式(Local Conceptual Schema) 描述该站点上实际存储的数据逻辑结构 ,即该站点拥有的分片(可能包含多个全局表的分片)。例如:Site A 上有 Employees_North(emp_id, name, dept, salary)Departments_HR(dept_id, name)
局部内模式(Local Internal Schema) 描述该站点上数据的物理存储方式,如文件组织、索引结构、存储路径等,与传统数据库的内模式一致。

✅ 这种结构实现了从"逻辑统一"到"物理分布"的映射。


3.映射关系(扩展的"映射"机制)

在分布式数据库中,除了传统的两级映射,还增加了分布相关的映射

映射 功能 实现的透明性
全局外模式 → 全局概念模式 支持不同用户视图 逻辑数据独立性
全局概念模式 → 分片模式 定义如何将全局表拆分为片段 分片透明性(Sharding Transparency)
分片模式 → 分布模式 定义片段存放位置 位置透明性(Location Transparency)
分布模式 → 局部概念模式 将全局分片映射到各站点的实际表 ------
局部概念模式 → 局部内模式 定义本地物理存储 物理数据独立性

🔍 这些映射共同实现了分布式数据库的分布透明性(Distribution Transparency),包括:

  • 分片透明性:用户无需知道数据被分片;
  • 位置透明性:用户无需知道分片存于哪个节点;
  • 复制透明性:若存在副本,用户无需知道副本数量和位置。

4.结构示意图(文字版)
复制代码
[用户/应用]
     ↓
全局外模式(View)
     ↓
全局概念模式(Global Logical Schema)
     ↓
分片模式(How data is fragmented?)
     ↓
分布模式(Where fragments are stored?)
     ↓
┌─────────────┐    ┌─────────────┐
│ 局部概念模式 │    │ 局部概念模式 │  ← 各站点
│ (Site A)    │    │ (Site B)    │
└─────┬───────┘    └─────┬───────┘
      ↓                  ↓
局部内模式           局部内模式
(物理存储)         (物理存储)

5.与传统三级模式的区别总结
特性 传统数据库(集中式) 分布式数据库
模式层数 3 层(外/概/内) 扩展为 5+ 层(全局外/全局概/分片/分布/局部概/局部内)
映射数量 2 级映射 多级映射(含分片、分布映射)
核心目标 数据抽象与独立性 数据抽象 + 分布透明性 + 并发与一致性
物理存储 单一位置 多节点、可能复制
管理组件 单一 DBMS 全局 DBMS + 多个局部 DBMS
传统三级模式
1. 三级模式
  • 外模式(External Schema):面向用户或应用程序的视图,描述用户看到的数据结构。
  • 概念模式(Conceptual Schema):全局逻辑结构,描述整个数据库的实体、关系、约束等(如 ER 图或关系模式)。
  • 内模式(Internal Schema):物理存储结构,描述数据如何在磁盘上存储(索引、文件组织等)。
2. 两级映射
  • 外模式/概念模式映射 :实现逻辑数据独立性。当概念模式改变时,只需调整映射,外模式可不变。
  • 概念模式/内模式映射 :实现物理数据独立性。当存储结构改变时,概念模式不受影响。
两者的核心区别
维度 分布式数据库结构 三级模式两级映射
目标 解决数据在多节点上的分布、一致性、可用性等问题 实现数据抽象与独立性,屏蔽底层细节
适用范围 分布式系统(多机、跨地域) 单机或集中式数据库系统
核心关注点 数据分布、通信、事务协调、容错 数据抽象层次、逻辑/物理独立性
是否包含映射机制 有"分布透明性"机制,但不同于两级映射 明确定义了两级映射机制
结构层级 物理分布 + 逻辑统一(无严格三层) 严格的三层抽象(外/概/内)

💡 关键理解

  • "三级模式两级映射"是一种逻辑架构模型 ,强调数据抽象与独立性
  • "分布式数据库结构"是一种物理部署模型 ,强调数据分布与协同
  • 在实际的分布式数据库系统中,可能同时采用三级模式思想(例如每个节点有自己的三级模式),但还需额外处理分布带来的复杂性。

6.实际例子(简化)

假设有一个全球公司员工表 Employees

  • 全局概念模式Employees(eid, name, country, salary)
  • 分片模式 (水平分片):
    • Emp_US = σ_{country='US'}(Employees)
    • Emp_CN = σ_{country='CN'}(Employees)
  • 分布模式
    • Emp_US → Site_NewYork
    • Emp_CN → Site_Beijing
  • 局部概念模式(Site_NewYork) :只有 Emp_US
  • 局部内模式(Site_NewYork)Emp_US 以 B+树索引存储在 SSD 上

用户查询 SELECT * FROM Employees WHERE country='US'

  • 全局 DBMS 通过分布模式知道数据在 NewYork;
  • 自动路由查询到该站点;
  • 用户完全感知不到分布过程。

三、关键技术机制

方式 说明 优点 缺点
分片(Fragmentation) 将表拆分为子集 减少单点负载,提升查询效率 跨分片查询复杂
- 水平分片 按行划分(如按地区) 适合范围查询 需维护分片键
- 垂直分片 按列划分(如分离敏感字段) 提高安全性 多表关联开销大
复制(Replication) 同一份数据存多份 高可用、读性能提升 一致性维护复杂
混合方式 分片 + 复制 平衡性能与容灾 系统复杂度高
  1. 数据分片策略

    -- 水平分片:按行切分
    -- 例如:用户表按地区分片
    北京分片: SELECT * FROM users WHERE region='beijing'
    上海分片: SELECT * FROM users WHERE region='shanghai'

    -- 垂直分片:按列切分
    -- 分片1: 用户基本信息 (id, name, email)
    -- 分片2: 用户敏感信息 (id, password, phone)

    -- 混合分片:两者结合

  2. 数据分布策略

策略 描述 优点 缺点
集中式 所有数据在一个主节点 管理简单,一致性容易 单点瓶颈,扩展性差
分割式 数据分片,每个分片在不同节点 负载均衡,并行查询 跨节点查询复杂
全复制式 每个节点都有完整数据副本 高可用,本地访问快 更新同步开销大
混合式 常用数据复制,其他数据分割 兼顾性能和管理 实现复杂

3.数据复制技术

复制代码
主节点 ──── 异步复制 ────> 从节点1
   │                          │
同步复制                异步复制
   │                          │
   └─── 同步复制 ────> 从节点2

复制类型:
1. 主从复制:写主节点,读从节点(默认读操作走从节点,特殊场景读操作走主节点,所有写操作必须走主节点)
2. 多主复制:多个节点都可写
3. 无主复制:任意节点都可读写(如Dynamo)
  • 同步复制 :延迟最高(等待所有副本节点)
  • 半同步复制:延迟中等(等待至少一个)
  • 异步复制:延迟最低(立即返回)

四、核心挑战与解决方案

1. 分布式事务管理
sql 复制代码
-- 经典的两阶段提交协议(2PC)
-- 阶段1: 准备阶段
协调者 -> 参与者: "Can you commit?"
参与者 -> 协调者: "Yes, I'm prepared" or "No, I abort"

-- 阶段2: 提交/回滚阶段
协调者 -> 参与者: "Everyone agreed, commit!" or "Someone disagreed, abort!"

分布式事务协议演进

  • 2PC:强一致,但阻塞问题
  • 3PC:解决阻塞,但复杂
  • Paxos/Raft:基于共识的协议
  • TCC/SAGA:最终一致性方案
2. 数据一致性模型
复制代码
强一致性 (Strong) ── 线性一致性 (Linearizable)
    │
顺序一致性 (Sequential)
    │
因果一致性 (Causal)
    │
最终一致性 (Eventual) ── 弱一致性 (Weak)

CAP定理的应用

  • CP系统:ZooKeeper, etcd, HBase
  • AP系统:Cassandra, DynamoDB, CouchDB
  • CA系统:传统单机数据库(分布式下不存在)
3. 查询处理与优化
sql 复制代码
-- 分布式查询示例:跨节点JOIN
SELECT o.order_id, c.customer_name
FROM orders o JOIN customers c ON o.customer_id = c.customer_id
WHERE o.region = 'north' AND c.region = 'south'

-- 可能的执行策略:
1. 数据重分布:将相关数据移动到同一节点
2. 半连接优化:只传输连接键和必要列
3. Bloom Filter:快速过滤不匹配的记录
4. 故障恢复机制
复制代码
故障类型:
1. 节点故障:心跳检测,自动故障转移
2. 网络分区:多数派原则,脑裂保护
3. 数据损坏:校验和,副本修复

恢复策略:
- 日志重做 (Redo)
- 日志撤销 (Undo)
- 检查点 (Checkpoint)
- 消息重传

五、适用场景

  • 海量数据存储(如用户行为日志)
  • 高并发读写(如电商、社交平台)
  • 多地部署与低延迟访问(如全球 SaaS 应用)
  • 高可用与容灾需求(金融、电信核心系统)

六、与集中式数据库对比

维度 集中式数据库 分布式数据库
数据位置 单一节点 多节点分布
扩展性 垂直扩展(升级硬件) 水平扩展(加机器)
可用性 单点故障风险高 多副本容错
事务处理 简单高效 复杂,有协调开销
开发复杂度 高(需考虑分布问题)

七 常见的分布式数据库

MySQL 本身是单机数据库,但通过主从复制 + 分库分表中间件可构建分布式系统。

类型 代表系统 是否全局单主 一致性 自动故障转移 适合场景
传统主从复制 MySQL + MHA, MongoDB 副本集 ✅ 是 强(同步)或最终(异步) ✅(需组件支持) 中小规模、读多写少
分片级主从(Raft/Paxos) TiDB, CockroachDB, Spanner ❌ 否(每分片有主) 强一致 ✅ 自动 大规模、强一致、高可用
无主复制 Cassandra, ScyllaDB ❌ 否 最终一致 ✅(无主故无切换) 高写入、容忍延迟一致
  • 选型建议:
    • 若你需要简单、成熟、读写分离 → 选 MySQL 主从 + 中间件 或 MongoDB 副本集。
    • 若你需要强一致性 + 自动扩缩容 + 分布式事务 → 选 TiDB / CockroachDB。
    • 若你需要极致写入性能 + 容忍最终一致 → 选 Cassandra / ScyllaDB。
    • 若你在公有云上,可直接使用托管服务:
      • AWS Aurora(主从+共享存储)
      • Azure Cosmos DB(多模型,可调一致性)
      • Google Cloud Spanner(全球强一致)

3.数据仓库

数据仓库是一个面向主题的集成的非易失的且随时间变化的数据集合,用于支

持管理决策。

  • 面向主题:按照一定的主题域进行组织的。
  • 集成的:数据仓库中的数据是在对原有分散的数据库数据抽取、清理的基础上经过系统加工、汇总和整理得到的,必须消除源数据中的不一致性,以保证数据仓库内的信息是关于整个企业的一致的全局信息。
  • 相对稳定的:数据仓库的数据主要供企业决策分析之用,所涉及的数据操作主要是数据查询,一旦某个数据进入数据仓库以后,一般情况下将被长期保留,也就是数据仓库中一般有大量的查询操作,但修改和删除操作很少,通常只需要定期的加载、刷新。
  • 反映历史变化:数据仓库中的数据通常包含历史信息,系统记录了企业从过去某一时点(如开始应用数据仓库的时点)到目前的各个阶段的信息,通过这些信息,可以对企业的发展历程和未来趋势做出定量分析和预测。

3.1 数据仓库的结构

数据仓库的结构通常包含四个层次

  • 1.数据源:是数据仓库系统的基础,是整个系统的数据源泉
  • 2.数据的存储与管理:是整个数据仓库系统的核心。
  • 3.OLAP(联机分析处理) 服务器:对分析需要的数据进行有效集成,按多维模型组织,以便进行多角度、多层次的分析,并发现趋势。
  • 4.前端工具:主要包括各种报表工具、查询工具、数据分析工具、数据挖掘工具以及各种基于数据仓库或数据集市的应用开发工具

3.2 BI系统

BI系统主要包括数据预处理建立数据仓库数据分析数据展现四个主要阶段。

  • 数据预处理是整合企业原始数据的第一步,它包括数据的抽取(Extraction)、转换 (Transformation)和加载(Load) 三个过程 (ETL过程);
  • 建立数据仓库则是处理海量数据的基础:
  • 数据分析是体现系统智能的关键,一般采用联机分析处理 (OLAP) 和数据挖
    掘两大技术。联机分析处理不仅进行数据汇总/聚集,同时还提供切片、切块
    下钻、上卷和旋转等数据分析功能,用户可以方便地对海量数据进行多维分
    析。数据挖掘的目标则是挖掘数据背后隐藏的知识,通过关联分析、聚类和
    分类等方法建立分析模型,预测企业未来发展趋势和将要面临的问题
  • 在海量数据和分析手段增多的情况下,数据展现则主要保障系统分析结果的
    可视化。

您总结的数据仓库四层结构(数据源 → 存储与管理 → OLAP服务器 → 前端工具)非常准确,这清晰地展现了数据仓库系统的逻辑架构。

3.3 数据仓库和数据库的区别:


1. 目的不同
  • 数据库 (通常指事务处理数据库,OLTP)

    主要用于支持日常的业务操作,如订单录入、库存更新、用户注册等。

    特点是高并发、小事务、实时性要求高,保证数据的一致性和完整性(ACID 属性)。

  • 数据仓库 (OLAP 系统)

    用于数据分析、决策支持,处理历史数据,回答"为什么发生""趋势如何"等问题。

    特点是大范围数据汇总、复杂查询、批量加载、读多写少


2. 数据模型不同
  • 数据库

    一般采用规范化模型(第三范式等),减少冗余,优化事务更新效率。

  • 数据仓库

    一般采用维度模型(星型模型、雪花模型),以事实表和维度表组织,便于业务用户理解和提高查询性能。


3. 数据内容与时间范围
  • 数据库

    存储当前、瞬时的数据,反映最新状态,通常只保留短期历史数据。

  • 数据仓库

    存储历史数据,时间跨度大(数年),并按时间维度进行分析。


4. 使用方式与用户
  • 数据库

    使用者:一线操作人员、应用程序。

    操作:增、删、改、查,以随机读写为主。

  • 数据仓库

    使用者:业务分析师、管理者、决策者。

    操作:复杂查询、报表、多维分析、数据挖掘,基本是批量读取。


5. 技术特点
方面 数据库 (OLTP) 数据仓库 (OLAP)
数据更新 实时、频繁更新 定期批量加载(ETL/ELT)
查询复杂度 简单查询,返回少量数据 复杂聚合、多表连接,返回大量汇总数据
索引设计 为快速定位单条记录 为快速扫描和聚合大量数据
存储优化 行存储为主 列存储常见(便于分析查询)
并发 高并发短事务 并发较低,但查询负载重

6. 应用场景举例
  • 数据库

    银行 ATM 取款交易、电商下单、酒店预订系统。

  • 数据仓库

    分析上个月各地区的销售趋势、客户购买行为分析、年度财务报表生成。


7.常见数据库和数据仓库产品举例
7.1 传统/关系型数据库 (OLTP)

这些是典型的事务处理数据库:

产品 特点 典型用途
MySQL 开源、轻量、广泛应用于Web 网站用户数据、电商订单
PostgreSQL 功能丰富、支持JSON等复杂类型 地理信息系统、复杂业务系统
Oracle Database 企业级、功能全面、昂贵 银行核心系统、大型ERP
Microsoft SQL Server 与Windows生态集成好 企业内部业务系统
SQLite 嵌入式、零配置 移动应用、桌面应用
7.2 数据仓库产品 (OLAP)

这些是专门为分析设计的:

产品 类型 特点
传统数仓
Teradata 专用硬件/软件一体 企业级、稳定、昂贵
Oracle Exadata Oracle的数仓解决方案 软硬件一体机
云数仓
Snowflake 纯云原生、存算分离 易用、按需付费、支持多云
Amazon Redshift AWS生态、列式存储 与AWS服务深度集成
Google BigQuery Serverless、自动扩缩容 无需管理基础设施
Azure Synapse Analytics Microsoft Azure生态 集成Power BI、机器学习
开源/MPP数仓
Apache Hive 基于Hadoop 批处理、适合海量数据
Greenplum 开源MPP数据库 PostgreSQL兼容、性价比高
ClickHouse 列式数据库、极速查询 实时分析、广告日志分析
湖仓一体
Databricks Lakehouse 基于Delta Lake 数据湖+数据仓库能力
Starburst/Trino 联邦查询引擎 可查询多种数据源
总结

简单来说:

  • 数据库 是做业务的,保证事务处理的效率和正确性。
  • 数据仓库 是做分析的,从大量历史数据中提炼信息与洞察

二者常常并存:数据库中的数据通过 ETL 过程进入数据仓库,供分析使用。

4.反规范化

反规范化技术规范化设计后,数据库设计者希望牺牲部分规范化来提高性能。

  • 采用反规范化技术的益处:降低连接操作的需求、降低外码和索引的数目,还可能减少表的数目,能够提高查询效率。
  • 可能带来的问题:数据的重复存储,浪费了磁盘空间;可能出现数据的完整性问题,为了保障数据 的一致性,增加了数据维护的复杂性,会降低修改速度
    具体方法:
  • (1)增加元余列:在多个表中保留相同的列,通过增加数据元余减少或避免查询时的连接操作。
  • (2)增加派生列:在表中增加可以由本表或其它表中数据计算生成的列,减少查询时的连接操作并避免计算或使用集合函数。
  • (3)重新组表:如果许多用户需要查看两个表连接出来的结果数据,则把这两个表重新组成一个表来减少连接而提高性能。
  • (4)水平分割表: 根据一列或多列数据的值,把数据放到多个独立的表中,主要用于表数据规模很大、表中数据相对独立或数据需要存放到多个质上时使用。
  • (5)垂直分割表:对表进行分割,将主键与部分列放到一个表中,主键与其它列放到另一个表中,在查询时减少I/0次数。

定义

反规范化 是在已经完成规范化设计的数据库基础上,有意识地引入数据冗余,以提高特定查询性能的数据库设计技术。

核心理念

  • "以空间换时间":牺牲存储空间换取查询性能
  • "以一致性复杂度换读取速度":增加数据维护复杂度换取更快的读取速度
  • 平衡的艺术:在数据一致性、完整性与查询性能之间寻找最佳平衡点

适用场景

  1. 读密集型系统(如报表系统、数据仓库)
  2. 性能瓶颈明显的查询
  3. 实时性要求高的OLTP系统中的特定查询
  4. 数据仓库/BI系统中的事实表和维度表设计

五种常用反规范化方法

1. 增加冗余列(Adding Redundant Columns)

原理:将经常需要连接查询的字段复制到多个表中

示例

sql 复制代码
-- 规范化设计
订单表(订单ID, 客户ID, 订单日期, ...)
客户表(客户ID, 客户姓名, 地址, ...)

-- 反规范化:在订单表中增加客户姓名
订单表(订单ID, 客户ID, 客户姓名, 订单日期, ...)
客户表(客户ID, 客户姓名, 地址, ...)

适用情况

  • 频繁的连接操作成为性能瓶颈
  • 冗余字段更新频率低
  • 查询需要显示相关表的字段但不需要连接所有信息

优点

  • 消除连接操作
  • 简化查询语句
  • 显著提升查询速度

缺点

  • 数据更新需要同步多处
  • 增加存储空间
  • 可能产生数据不一致

2. 增加派生列(Adding Derived Columns)

原理:存储可计算的数据,避免实时计算

示例

sql 复制代码
-- 不需要派生列
订单明细表(订单ID, 产品ID, 单价, 数量)
-- 查询时需要计算:SELECT 单价 * 数量 AS 小计

-- 增加派生列
订单明细表(订单ID, 产品ID, 单价, 数量, 小计)

常见派生列类型

  • 聚合值:总和、平均值、计数
  • 计算值:金额、折扣价、税率
  • 状态标志:是否超期、是否达标

维护方式

  • 触发器:数据变更时自动计算
  • 应用程序逻辑:在业务代码中维护
  • 物化视图:数据库自动维护
  • 定时作业:定期批量更新

3. 重新组表/表合并(Table Merging)

原理:将关联紧密的表物理合并为一个表

示例

sql 复制代码
-- 规范化设计(一对一关系)
用户表(用户ID, 用户名, 邮箱)
用户详情表(用户ID, 注册时间, 最后登录, 会员等级)

-- 反规范化:合并表
用户完整表(用户ID, 用户名, 邮箱, 注册时间, 最后登录, 会员等级)

适用情况

  • 表之间是一对一关系
  • 总是需要连接查询
  • 连接查询成为性能瓶颈

注意事项

  • 可能违反第二范式(2NF)
  • 会产生大量NULL值(如果关系不是严格一对一)
  • 更新时需要锁定更大的表

4. 水平分割表(Horizontal Partitioning)

原理:按行将大表分割为多个结构相同的小表

分割策略

分割类型 依据 示例 适用场景
范围分割 连续的值范围 按时间:orders_2023, orders_2024 时间序列数据
列表分割 明确的离散值 按地区:sales_east, sales_west 地域分布明确
哈希分割 哈希函数结果 对用户ID取模分表 均匀分布负载
轮询分割 循环分配 轮流分配记录 简单的负载均衡

优点

  • 提高查询性能(扫描更少数据)
  • 便于数据管理(备份、归档、清理)
  • 提高并发性
  • 优化索引效率

缺点

  • 跨分区查询复杂
  • 可能需要应用层路由
  • 事务管理复杂

5. 垂直分割表(Vertical Partitioning)

原理:按列将宽表分割为多个窄表

示例

sql 复制代码
-- 原始宽表
产品表(
  产品ID, 产品名, 价格, 库存, 
  详细描述, 技术参数, 图片URL, 
  创建时间, 更新时间, 审核状态
)

-- 垂直分割为
产品核心表(产品ID, 产品名, 价格, 库存, 创建时间)
产品详情表(产品ID, 详细描述, 技术参数, 图片URL)
产品管理表(产品ID, 更新时间, 审核状态)

分割原则

  • 热数据/冷数据分离:频繁访问的列 vs 很少访问的列
  • 大小分离:常规列 vs 大对象(BLOB/TEXT)
  • 安全分离:公开信息 vs 敏感信息
  • 频率分离:经常更新 vs 很少更新

优点

  • 减少I/O(只读取必要列)
  • 提高缓存命中率
  • 更好的安全控制
  • 独立的备份策略

其他反规范化技术

6. 预连接表(Pre-Joined Tables)

原理:提前执行连接操作并存储结果

sql 复制代码
-- 创建预连接表
CREATE TABLE 订单详情_预连接 AS
SELECT o.*, c.客户名, p.产品名, p.分类
FROM 订单 o
JOIN 客户 c ON o.客户ID = c.客户ID
JOIN 产品 p ON o.产品ID = p.产品ID;

-- 使用时直接查询预连接表
SELECT * FROM 订单详情_预连接 WHERE 客户名 = '张三';
7. 汇总表(Summary Tables)

原理:存储预先计算的汇总数据

sql 复制代码
-- 日销售汇总表
CREATE TABLE 销售日汇总 (
  日期 DATE PRIMARY KEY,
  订单数 INT,
  销售总额 DECIMAL(10,2),
  客户数 INT,
  热门产品 VARCHAR(100)
);
8. 重复表(Duplicate Tables)

原理:复制整个表到不同位置

应用场景

  • 地理分布式系统
  • 读写分离架构
  • 灾难恢复

总结

反规范化是数据库性能优化的重要手段,但需要谨慎使用。它不是规范化设计的替代品,而是补充。正确的做法是:

  1. 首先进行完整的规范化设计
  2. 基于实际性能测试识别瓶颈
  3. 针对性地选择反规范化方法
  4. 实施严格的数据一致性维护
  5. 持续监控和调整

记住黄金法则:"不要为了反规范化而反规范化,只为解决明确的性能问题而反规范化"。在大多数情况下,优化索引、查询语句和数据库配置可能比反规范化更有效且风险更低。

5.大数据

  • 特点:大量化、多样化、价值密度低、快速化。
  • 大数据和传统数据的比较如下:
  • 要处理大数据,一般使用集成平台,称为大数据平台,其特征为:
    • 支持异构环境、较短的分析延迟、易用且高度可扩展性、高性能、高度容错、开放的接口、较低成本、向下兼容性。

6.SQL语言

  • 表相关语法

    SQL语言中的语法关键字
    创建表create table;
    指定主键primary key();
    指定外键foreign key();
    修改表alter table;
    删除表drop table;
    索引index,
    视图view;

创建表语法

···

CREATE TABLE 表名 (

列名 数据类型 CONSTRAINT 约束名 约束类型,

...

CONSTRAINT 约束名 约束类型 (列名),

CONSTRAINT 约束名 约束类型 (列名) REFERENCES 其他表(列名)

);

···

约束类型

CONSTRAINT(约束)是数据库中对数据进行限制的规则,用于保证数据的完整性、一致性和准确性。

CONSTRAINT 的五大类型

约束类型 关键字 作用 类比
主键约束 PRIMARY KEY 唯一标识每行,不能为空 身份证号
外键约束 FOREIGN KEY 确保参照完整性 员工必须属于存在的部门
唯一约束 UNIQUE 确保列值唯一(可为空) 邮箱/用户名不能重复
非空约束 NOT NULL 确保列不能为空 姓名不能为空
检查约束 CHECK 确保列值符合条件 年龄必须大于0
1.主键约束
sql 复制代码
-- 方法1:字段后直接定义(隐式命名)
CREATE TABLE users (
    id INT PRIMARY KEY,                    -- 约束名由系统生成
    username VARCHAR(50)
);

-- 方法2:使用 CONSTRAINT 命名约束(显式命名)
CREATE TABLE users (
    id INT,
    username VARCHAR(50),
    CONSTRAINT pk_users_id PRIMARY KEY (id)  -- 约束名为 pk_users_id
);

-- 方法3:复合主键
CREATE TABLE order_items (
    order_id INT,
    product_id INT,
    quantity INT,
    CONSTRAINT pk_order_items PRIMARY KEY (order_id, product_id)
);
-- 简写(不命名约束)
CREATE TABLE users (
    id INT,
    username VARCHAR(50),
    email VARCHAR(100),
    PRIMARY KEY (id)
);
2. 外键约束 (FOREIGN KEY)
sql 复制代码
-- 主表
CREATE TABLE departments (
    dept_id INT PRIMARY KEY,
    dept_name VARCHAR(100)
);

-- 从表:定义外键约束
CREATE TABLE employees (
    emp_id INT PRIMARY KEY,
    emp_name VARCHAR(100),
    dept_id INT,
    -- 定义外键约束并命名
    CONSTRAINT fk_emp_dept 
    FOREIGN KEY (dept_id) 
    REFERENCES departments(dept_id)
    ON DELETE CASCADE
    ON UPDATE CASCADE
);

-- 简写(不命名约束)
CREATE TABLE employees (
    emp_id INT PRIMARY KEY,
    dept_id INT,
    FOREIGN KEY (dept_id) REFERENCES departments(dept_id)
);
3.外键约束选项
sql 复制代码
-- 级联删除:主表删除记录,从表相关记录也被删除
ON DELETE CASCADE

-- 级联更新:主表更新主键,从表外键同步更新
ON UPDATE CASCADE

-- 设为 NULL:主表删除记录,从表外键设为 NULL
ON DELETE SET NULL

-- 限制操作(默认):如果从表有相关记录,阻止主表删除/更新
ON DELETE RESTRICT
ON UPDATE RESTRICT

-- 无操作:与 RESTRICT 类似,但在事务结束时检查
ON DELETE NO ACTION
ON UPDATE NO ACTION

修改表

sql 复制代码
-- 添加主键
ALTER TABLE users ADD PRIMARY KEY (id);
-- 删除主键
ALTER TABLE users DROP PRIMARY KEY;

索引

一、语法结构分解
复制代码
CREATE INDEX 索引名称 ON 表名(字段名);
└────┬────┘ └─┬─┘ └─┬┘ └───┬──┘
     ↓       ↓    ↓      ↓
   创建索引  在...上 表名  要索引的字段

详细解释一下 CREATE INDEX idx_email ON users(email); 中各个部分的含义:

二、各部分的详细解释
1. CREATE INDEX - 创建索引的命令
  • 告诉 MySQL 要创建一个新的索引
2. idx_email - 索引名称
  • 用户自定义的索引名

  • 命名建议:

    sql 复制代码
    idx_字段名                  -- 如:idx_email
    idx_表名_字段名            -- 如:idx_users_email
    uk_字段名                  -- 唯一索引:uk_email
3. ON - 关键字
  • 英语中的"在...上"
  • 表示"在哪个表上创建索引"
  • 是 SQL 语法的一部分,必须要有
4. users - 表名
  • 指定要在哪个表上创建索引
  • 必须是数据库中已存在的表
5. (email) - 要创建索引的字段
  • 括号内指定一个或多个要索引的字段

  • emailusers 表中的一个字段名

  • 可以有多个字段(复合索引):

    sql 复制代码
    CREATE INDEX idx_name_age ON users(last_name, first_name, age);
三、类比理解

可以把这个语法类比成"贴标签"或"建目录":

复制代码
在 "用户档案柜(users)" 的 "邮箱(email)" 这一列上
建立一个 "邮箱快速查找标签(idx_email)"
四、实际效果示例
示例表结构
sql 复制代码
-- 假设 users 表结构如下
CREATE TABLE users (
    id INT PRIMARY KEY,
    username VARCHAR(50),
    email VARCHAR(100),      -- ← 我们要在这个字段上建索引
    age INT,
    created_at DATETIME
);
执行索引创建前后对比

执行前:

  • 查询 WHERE email = 'test@example.com' 需要全表扫描
  • 如果有 100 万条记录,就要检查 100 万次

执行后:

sql 复制代码
-- 创建索引
CREATE INDEX idx_email ON users(email);
  • MySQL 会为 email 字段创建一个"快速查找目录"
  • 查询时直接查找"目录",不需要扫描整张表
  • 查询速度可能提高几十倍甚至几百倍
五、可视化理解
索引创建前(无索引):
复制代码
users 表:
id | username | email               | age
---|----------|---------------------|----
1  | John     | john@example.com    | 25
2  | Jane     | jane@example.com    | 30
3  | Bob      | bob@gmail.com       | 28
...
1000000 | Alice | alice@test.com | 26

查找 alice@test.com → 需要逐行检查100万次
索引创建后(有索引):
复制代码
users 表:
id | username | email               | age
---|----------|---------------------|----
1  | John     | john@example.com    | 25
2  | Jane     | jane@example.com    | 30
3  | Bob      | bob@gmail.com       | 28
...
1000000 | Alice | alice@test.com | 26

idx_email 索引(类似电话簿):
alice@test.com    → 指向第1000000行
bob@gmail.com     → 指向第3行
jane@example.com  → 指向第2行
john@example.com  → 指向第1行
...
(按字母顺序排序)

查找 alice@test.com → 在索引中二分查找,几毫秒找到
六、其他常见写法对比
sql 复制代码
-- 写法1:基本写法(你问的这种)
CREATE INDEX idx_email ON users(email);

-- 写法2:创建表时定义索引
CREATE TABLE users (
    id INT PRIMARY KEY,
    email VARCHAR(100),
    INDEX idx_email (email)  -- 这里的效果相同
);

-- 写法3:使用 ALTER TABLE
ALTER TABLE users ADD INDEX idx_email (email);

-- 写法4:唯一索引
CREATE UNIQUE INDEX uk_email ON users(email);

-- 写法5:多列索引(复合索引)
CREATE INDEX idx_name_email ON users(username, email);
七、验证索引是否创建成功
sql 复制代码
-- 查看表的所有索引
SHOW INDEX FROM users;

-- 结果示例:
+-------+------------+-----------+--------------+-------------+
| Table | Non_unique | Key_name  | Seq_in_index | Column_name |
+-------+------------+-----------+--------------+-------------+
| users |          0 | PRIMARY   |            1 | id          |
| users |          1 | idx_email |            1 | email       |
+-------+------------+-----------+--------------+-------------+
八、实际使用场景
sql 复制代码
-- 创建索引前(慢)
SELECT * FROM users WHERE email = 'alice@example.com';
-- 需要扫描全表:100万次比较

-- 创建索引
CREATE INDEX idx_email ON users(email);

-- 创建索引后(快)
SELECT * FROM users WHERE email = 'alice@example.com';
-- 使用索引:大约只需要 log₂(1000000) ≈ 20次比较
九、重要注意事项
  1. 索引会占用磁盘空间
  2. 索引会减慢插入/更新/删除速度(因为要维护索引)
  3. 不要过度创建索引(只为经常查询的字段创建)
  4. 小表不需要索引(数据量少时全表扫描更快)
十、不同类型的索引对重复值的处理
1. 普通索引 (INDEX)
sql 复制代码
-- 创建普通索引:允许重复值
CREATE INDEX idx_email ON users(email);

-- 示例数据:
-- id | email
-- 1  | a@test.com
-- 2  | a@test.com  ← 允许重复
-- 3  | b@test.com
-- 4  | a@test.com  ← 允许重复

影响:

  • 允许重复值
  • ⚠️ 索引中会有多个相同键值的条目
  • 📊 查询 a@test.com 时,索引会指向所有匹配的行

数据结构示意:

复制代码
B+树索引结构:
idx_email 索引:
┌─────────────────────┐
│   a@test.com        │ → 指向行1、行2、行4
│   b@test.com        │ → 指向行3
└─────────────────────┘
2. 唯一索引 (UNIQUE INDEX)
sql 复制代码
-- 创建唯一索引:不允许重复值
CREATE UNIQUE INDEX uk_email ON users(email);

-- 尝试插入重复值会报错:
-- ERROR 1062 (23000): Duplicate entry 'a@test.com' for key 'uk_email'

影响:

  • 不允许重复值
  • 🔒 插入或更新重复值会失败
  • ✅ 确保数据唯一性
3. 主键索引 (PRIMARY KEY)
sql 复制代码
-- 主键:既唯一又非空
ALTER TABLE users ADD PRIMARY KEY (email);
-- 或
CREATE TABLE users (
    email VARCHAR(100) PRIMARY KEY  -- 不能重复也不能为NULL
);
  • 记录相关语法

    数据库查询select...from...where;
    分组查询group by,分组时要注意select后的列名要适应分组,having为分组查
    询附加条件: select snoavg(score) from student group by sno having(avg(score)>60
    更名运算as: select sno as"学号"from t1
    字符串匹配like,%匹配多个字符串,匹配任意一个字符串: select * from t1 where sname like 'a '
    数据库插入insert into...values():insert into t1 values('a',66)
    数据库删除delete from...where:delete t1 where sno=4
    数据库修改update...set...where:update t1 set sname='aa' where sno=3
    排序order by,默认为升序,降序要加关键字DESC: select * from t1 order by sno DESC

    DISTINCT:过滤重复的选项,只保留一条记录
    UNION:出现在两个SQL语句之间,将两个SQL语句的查询结果取或运算,即值存在于第一句或第二句都会被选出。
    INTERSECT:对两个SQL语句的查询结果做与运算,即值同时存在于两个语句才被选出。
    MIN、AVG、MAX: 分组查询时的聚合函数

一、GROUP BY 基本概念

GROUP BY 语句用于根据一个或多个列对结果集进行分组,通常与聚合函数一起使用。

基本语法
sql 复制代码
SELECT 列1, 列2, 聚合函数(列3)
FROM 表名
[WHERE 条件]
GROUP BY 列1, 列2
[HAVING 分组条件]
[ORDER BY 列]
二、基础示例
1. 创建测试数据
sql 复制代码
-- 创建订单表
CREATE TABLE orders (
    order_id INT AUTO_INCREMENT PRIMARY KEY,
    customer_id INT,
    product_name VARCHAR(50),
    amount DECIMAL(10,2),
    order_date DATE,
    status VARCHAR(20)
);

-- 插入测试数据
INSERT INTO orders (customer_id, product_name, amount, order_date, status) VALUES
(1, '手机', 2999.00, '2024-01-10', 'completed'),
(1, '耳机', 399.00, '2024-01-12', 'completed'),
(2, '电脑', 6999.00, '2024-01-15', 'completed'),
(2, '鼠标', 199.00, '2024-01-15', 'pending'),
(3, '手机', 2999.00, '2024-01-18', 'completed'),
(1, '平板', 3999.00, '2024-01-20', 'completed'),
(3, '键盘', 299.00, '2024-01-22', 'pending');

三、基本用法示例

示例1:按单个字段分组
sql 复制代码
-- 按客户分组,统计每个客户的订单数量
SELECT customer_id, COUNT(*) as order_count
FROM orders
GROUP BY customer_id;

-- 结果:
-- customer_id | order_count
-- 1           | 3
-- 2           | 2
-- 3           | 2
示例2:使用聚合函数
sql 复制代码
-- 按客户分组,统计总金额、平均金额、最大金额
SELECT 
    customer_id,
    COUNT(*) as order_count,
    SUM(amount) as total_amount,
    AVG(amount) as avg_amount,
    MAX(amount) as max_amount,
    MIN(amount) as min_amount
FROM orders
GROUP BY customer_id;
示例3:按多个字段分组
sql 复制代码
-- 按客户和状态分组
SELECT customer_id, status, COUNT(*) as count
FROM orders
GROUP BY customer_id, status;

-- 结果:
-- customer_id | status    | count
-- 1           | completed | 3
-- 2           | completed | 1
-- 2           | pending   | 1
-- 3           | completed | 1
-- 3           | pending   | 1
示例4:按日期分组
sql 复制代码
-- 按日期分组,统计每日订单数量和金额
SELECT 
    order_date,
    COUNT(*) as daily_orders,
    SUM(amount) as daily_revenue
FROM orders
GROUP BY order_date
ORDER BY order_date;

四、HAVING 子句的使用

HAVING 用于对分组后的结果进行过滤(类似 WHERE,但用于分组后)。

sql 复制代码
-- 找出总消费超过3000的客户
SELECT 
    customer_id,
    COUNT(*) as order_count,
    SUM(amount) as total_amount
FROM orders
WHERE status = 'completed'  -- 先筛选符合条件的行
GROUP BY customer_id
HAVING total_amount > 3000  -- 再筛选分组后的结果
ORDER BY total_amount DESC;

-- 统计每日订单,只显示订单数大于1的日期
SELECT 
    order_date,
    COUNT(*) as order_count
FROM orders
GROUP BY order_date
HAVING order_count > 1;

五、WITH ROLLUP 扩展

生成分组汇总的小计和总计。

sql 复制代码
-- 按客户和状态分组,并生成小计和总计
SELECT 
    COALESCE(customer_id, '总计') as customer,
    COALESCE(status, '小计') as status,
    COUNT(*) as count,
    SUM(amount) as total
FROM orders
GROUP BY customer_id, status WITH ROLLUP;

-- 结果:
-- customer | status    | count | total
-- 1        | completed | 3     | 7397
-- 1        | 小计      | 3     | 7397
-- 2        | completed | 1     | 6999
-- 2        | pending   | 1     | 199
-- 2        | 小计      | 2     | 7198
-- 3        | completed | 1     | 2999
-- 3        | pending   | 1     | 299
-- 3        | 小计      | 2     | 3298
-- 总计     | 小计      | 7     | 17893

六、GROUP_CONCAT 函数

将分组中的值连接成字符串。

sql 复制代码
-- 每个客户购买的所有产品(合并为字符串)
SELECT 
    customer_id,
    GROUP_CONCAT(product_name) as all_products,
    GROUP_CONCAT(amount) as all_amounts,
    GROUP_CONCAT(DISTINCT product_name ORDER BY product_name) as unique_products
FROM orders
GROUP BY customer_id;

-- 使用分隔符
SELECT 
    customer_id,
    GROUP_CONCAT(product_name SEPARATOR ' | ') as products
FROM orders
GROUP BY customer_id;

七、复杂分组场景

场景1:多表连接分组
sql 复制代码
-- 假设有 customers 表
CREATE TABLE customers (
    customer_id INT PRIMARY KEY,
    name VARCHAR(50),
    city VARCHAR(50)
);

-- 插入客户数据
INSERT INTO customers VALUES 
(1, '张三', '北京'),
(2, '李四', '上海'),
(3, '王五', '北京');

-- 按城市分组,统计每个城市的订单情况
SELECT 
    c.city,
    COUNT(o.order_id) as order_count,
    SUM(o.amount) as total_amount,
    AVG(o.amount) as avg_amount
FROM customers c
LEFT JOIN orders o ON c.customer_id = o.customer_id
GROUP BY c.city;
场景2:按时间分组
sql 复制代码
-- 按年月分组
SELECT 
    YEAR(order_date) as order_year,
    MONTH(order_date) as order_month,
    COUNT(*) as order_count,
    SUM(amount) as monthly_revenue
FROM orders
GROUP BY YEAR(order_date), MONTH(order_date)
ORDER BY order_year, order_month;

-- 按周分组(周一为每周第一天)
SELECT 
    YEAR(order_date) as order_year,
    WEEK(order_date, 1) as week_number,
    COUNT(*) as order_count
FROM orders
GROUP BY YEAR(order_date), WEEK(order_date, 1);
八、GROUP BY 的注意事项和常见问题
1. ONLY_FULL_GROUP_BY 模式(MySQL 5.7+ 默认启用)
sql 复制代码
-- 查看当前模式
SELECT @@sql_mode;

-- 错误示例(在严格模式下会报错)
SELECT customer_id, product_name, amount
FROM orders
GROUP BY customer_id;
-- 错误:product_name 和 amount 不在 GROUP BY 子句中,也不是聚合函数

-- 正确写法
SELECT customer_id, 
       GROUP_CONCAT(product_name) as products,
       SUM(amount) as total_amount
FROM orders
GROUP BY customer_id;
2. SELECT 列的限制
sql 复制代码
-- 可以出现在 SELECT 中的列:
-- 1. GROUP BY 中的列
-- 2. 聚合函数中的列
-- 3. 常量值

-- 错误 ❌
SELECT customer_id, product_name  -- product_name 不在 GROUP BY 中
FROM orders
GROUP BY customer_id;

-- 正确 ✅
SELECT customer_id, MAX(product_name)  -- 使用聚合函数
FROM orders
GROUP BY customer_id;
3. 性能优化建议
sql 复制代码
-- 为 GROUP BY 的列创建索引
CREATE INDEX idx_customer_status ON orders(customer_id, status);

-- 使用 EXPLAIN 分析查询
EXPLAIN 
SELECT customer_id, COUNT(*) 
FROM orders 
GROUP BY customer_id;

-- 避免在大表上使用复杂的 GROUP BY
-- 先筛选数据,再分组
SELECT customer_id, COUNT(*)
FROM orders
WHERE order_date >= '2024-01-01'  -- 先缩小数据范围
GROUP BY customer_id;
4. NULL 值处理
sql 复制代码
-- NULL 会被视为一组
INSERT INTO orders (customer_id, product_name, amount) VALUES
(NULL, '测试商品', 100.00);

-- NULL 值会单独成为一组
SELECT customer_id, COUNT(*)
FROM orders
GROUP BY customer_id;
5. 分组顺序的影响
sql 复制代码
-- 不同的分组顺序可能影响性能
-- 如果 (status, customer_id) 有索引,这样可能更快
SELECT status, customer_id, COUNT(*)
FROM orders
GROUP BY status, customer_id;

-- 而不是
SELECT status, customer_id, COUNT(*)
FROM orders
GROUP BY customer_id, status;
6. HAVING vs WHERE
sql 复制代码
-- WHERE:分组前过滤行(效率更高)
-- HAVING:分组后过滤组

-- 高效:先过滤,再分组
SELECT customer_id, SUM(amount)
FROM orders
WHERE amount > 100  -- 先排除小额订单
GROUP BY customer_id;

-- 低效:先分组计算,再过滤
SELECT customer_id, SUM(amount)
FROM orders
GROUP BY customer_id
HAVING SUM(amount) > 1000;  -- 已经计算了所有行的总和
7. 内存使用
sql 复制代码
-- GROUP BY 可能使用临时表和文件排序
-- 监控临时表使用
SHOW STATUS LIKE 'Created_tmp%tables';

-- 优化大表分组:增加排序缓冲区
SET sort_buffer_size = 1024*1024*16;  -- 16MB

九、实际应用案例

案例1:电商销售分析报表
sql 复制代码
-- 生成月度销售报表
SELECT 
    YEAR(order_date) as year,
    MONTH(order_date) as month,
    -- 销售统计
    COUNT(*) as total_orders,
    COUNT(DISTINCT customer_id) as unique_customers,
    SUM(amount) as total_revenue,
    AVG(amount) as avg_order_value,
    -- 状态统计
    SUM(CASE WHEN status = 'completed' THEN 1 ELSE 0 END) as completed_orders,
    SUM(CASE WHEN status = 'pending' THEN 1 ELSE 0 END) as pending_orders,
    -- 热门产品
    GROUP_CONCAT(DISTINCT product_name) as products_sold
FROM orders
GROUP BY YEAR(order_date), MONTH(order_date)
ORDER BY year DESC, month DESC;
案例2:用户行为分析
sql 复制代码
-- 用户购买行为分析
SELECT 
    customer_id,
    -- 购买频次
    COUNT(*) as purchase_count,
    -- 时间跨度
    DATEDIFF(MAX(order_date), MIN(order_date)) as days_active,
    -- 产品多样性
    COUNT(DISTINCT product_name) as unique_products,
    -- 消费能力
    SUM(amount) as total_spent,
    AVG(amount) as avg_spent_per_order,
    -- 最近购买时间
    MAX(order_date) as last_purchase_date
FROM orders
WHERE status = 'completed'
GROUP BY customer_id
HAVING purchase_count >= 2  -- 只分析复购用户
ORDER BY total_spent DESC;
案例3:库存管理
sql 复制代码
-- 产品销量统计(带排名)
SELECT 
    product_name,
    COUNT(*) as sales_count,
    SUM(amount) as revenue,
    RANK() OVER (ORDER BY SUM(amount) DESC) as revenue_rank,
    DENSE_RANK() OVER (ORDER BY COUNT(*) DESC) as sales_rank
FROM orders
WHERE status = 'completed'
GROUP BY product_name
ORDER BY sales_count DESC;

十、最佳实践总结

  1. 索引优化:为 GROUP BY 的列创建索引
  2. 数据过滤:先用 WHERE 减少数据量,再用 GROUP BY
  3. 列选择:只 SELECT 需要的列,避免 SELECT *
  4. 分组顺序:分组列的顺序应与常用查询模式匹配
  5. 避免过度分组:不要对不需要分组的列使用 GROUP BY
  6. 监控性能:使用 EXPLAIN 分析查询计划
  7. 处理 NULL:注意 NULL 值的分组行为
  8. 使用合适的聚合函数:根据需求选择 SUM、COUNT、AVG 等
  9. 考虑使用窗口函数:某些场景下窗口函数比 GROUP BY 更合适
  10. 定期优化:随着数据增长,重新评估分组查询的性能

GROUP BY 是数据分析中非常重要的工具,正确使用可以大大提高查询效率,但需要注意其限制和性能影响。

相关推荐
zzzzzz3108 小时前
9K Star 炸裂开源!这个 C 语言写的代码知识图谱,把 Linux 内核索引压缩到了 3 分钟
linux·服务器·sql
倔强的石头_2 天前
《Kingbase护城河》——数据库存储空间全景探测与精细化瘦身实战
数据库
云技纵横2 天前
唯一索引 INSERT 死锁实战:5 秒复现交叉插入的 S 锁循环等待
sql·mysql
冬奇Lab3 天前
每日一个开源项目(第134篇):Zvec - 阿里开源的嵌入式向量数据库,向量搜索界的 SQLite
数据库·人工智能·llm
ClouGence3 天前
Oracle CDC 架构优化:从主库直连到 DataGuard 备库同步
数据库·后端·oracle
无响应de神3 天前
三、用户与权限管理
数据库·mysql
麦聪聊数据4 天前
数据服务化时代:企业数据能力输出的核心路径
数据库
shushangyun_4 天前
2026年快消品B2B系统推荐:支持终端门店订货、促销政策自动化的工具?
java·运维·网络·数据库·人工智能·spring·自动化
DARLING Zero two♡4 天前
【MySQL数据库】数据类型与表约束
数据库·mysql
曹牧4 天前
Oracle EXPLAIN PLAN
数据库·oracle