第六章 SQL编程系列-Gbase8a从入门到进阶

1 引言

天津南大通用数据技术股份有限公司(简称:GBASE)成立于2004年,注册资金1.88亿元。公司自成立以来始终坚持自主创新,产品的核心技术及底层代码自主可控,构建了覆盖数据管理全生命周期,包括分析型、事务型、分布式事务型、云原生数据仓库等全技术栈的数据产品体系及服务解决方案。

南大通用自主研发的GBase系列数据库产品及服务范围覆盖全国33个省级行政区域。为金融、电信、政务、能源交通、国防军工等百余个行业上万家用户提供产品和服务,建立节点超过10万个,管理数据总量超过500PB,并远销美国、巴西、墨西哥等五十余个国家及地区。

GBase 8a是南大通用自主研发的分布式数据库产品, 包含GBase 8a(OLAP)、GBase 8a(OLTP)、GBase 8a(GCDW)、GBase 8a(GCVD)、GBase 8a(GDOM)、GBase 8a(HD)和 GBase 8a (UP) 产品组件, 各个产品组件可以独立部署,也可以组合部署,通过积木式的组合方式满足不同应用场景的数据库需求,广泛应用于金融、电信、政务、能源等关键行业。

1.1 Gbase8a数据库的基本概念

Gbase8a是一款面向海量数据存储与分析的分布式列式数据库管理系统,其核心设计围绕高性能、高扩展性、高可用性展开,适用于大数据时代的复杂分析场景。以下从核心概念、技术原理、关键组件等维度进行全面解析:

1.1.1 核心概念

  1. 列式存储(Columnar Storage)
    • 定义:数据按列而非行存储,同一列的数据连续存放。
    • 优势
      • 分析效率高:查询仅需读取相关列,减少I/O开销(例如:统计销售额时只需读取"金额"列)。
      • 压缩率高:同列数据类型一致,压缩比可达1:5至1:20(例如:日期列可压缩为整数存储)。
    • 对比行式存储:传统关系型数据库(如MySQL)采用行式存储,适合事务处理,但分析场景下效率较低。
  2. 分布式架构(Distributed Architecture)
    • Shared Nothing模式:计算与存储节点独立,无共享资源,避免单点瓶颈。
    • 扩展性:支持千节点级集群,通过横向扩展(Scale-Out)应对PB级数据规模。
    • 容错性:节点故障时,数据自动重分布,保障服务连续性。
  3. 大规模并行处理(MPP, Massively Parallel Processing)
    • 原理:将查询任务拆分为多个子任务,并行分发到集群节点执行,结果汇总返回。
    • 效果:查询性能随节点数线性提升(例如:10节点集群查询速度是单节点的10倍)。
  4. 高可用性(High Availability)
    • 数据冗余:支持副本(Replica)或纠删码(Erasure Coding)机制,确保数据不丢失。
    • 故障切换:主节点故障时,备用节点自动接管,业务无感知。

1.1.2 关键组件

  1. 协调节点(Coordinator Node)
    • 角色:接收用户SQL请求,解析并生成执行计划,分发任务到计算节点。
    • 类比:类似餐厅的"服务员",负责接收订单并协调后厨(计算节点)工作。
  2. 计算节点(Compute Node)
    • 角色:执行查询任务,处理本地数据,返回中间结果。
    • 类比:后厨的"厨师",负责烹饪(计算)分配的菜品(任务)。
  3. 存储节点(Storage Node)
    • 角色:存储数据文件,支持列式存储与压缩。
    • 类比:餐厅的"仓库",存放食材(数据)。
  4. 元数据管理(Metadata Management)
    • 内容:记录表结构、分区信息、节点分布等元数据。
    • 作用:确保协调节点能快速定位数据位置。

1.1.3 核心特性与原理

  1. 智能压缩与编码
    • 算法:支持字典压缩、游程编码(RLE)、位图压缩等。
    • 示例
      • 字典压缩:将"北京、上海、广州"编码为1、2、3,节省存储空间。
      • 游程编码:连续重复值(如"0,0,0,1,1")压缩为"3个0,2个1"。
  2. 分区与分片(Partition & Sharding)
    • 分区:按时间、范围等逻辑划分数据(例如:按日期分区,每天一个分区)。
    • 分片:将分区数据分散到不同节点(例如:分区1存于节点A,分区2存于节点B)。
    • 效果:加速查询(仅扫描相关分区)并提升并行度。
  3. 向量化执行(Vectorized Execution)
    • 原理:批量处理数据(如一次处理1000行),减少CPU缓存未命中。
    • 对比:传统行式处理一次处理一行,效率较低。
  4. 混合负载支持
    • OLAP与轻量级OLTP:支持复杂分析查询(如聚合、关联)及高并发事务(如实时写入)。
    • 应用场景:金融交易监控(实时写入+实时分析)。

1.1.4 与关系型数据库的对比

特性 Gbase8a(列式) 传统关系型数据库(行式)
存储方式 按列存储 按行存储
适用场景 分析型查询(如报表、数据挖掘) 事务处理(如订单、用户信息)
压缩率 高(1:5~1:20) 低(通常1:1~1:3)
扩展性 横向扩展(Scale-Out) 纵向扩展(Scale-Up,加CPU/内存)
查询性能 复杂查询快,简单查询可能较慢 简单查询快,复杂查询效率低

1.1.5 典型应用场景

  1. 数据仓库与集市
    • 替代Oracle、Teradata等传统数据仓库,支持PB级数据存储与分析。
  2. 实时风控与反欺诈
    • 在金融行业,实时分析交易数据,识别异常行为(如高频交易、异地登录)。
  3. 物联网(IoT)数据存储
    • 处理海量设备数据(如传感器日志),支持实时趋势分析。
  4. 日志分析与用户行为分析
    • 存储并分析用户点击流、操作日志,优化产品体验。

1.2 Gbase8a技术架构演进

Gbase8a的技术架构演进经历了列存数据库阶段、MPP+列存数据库阶段和逻辑数仓(LDW)阶段,每个阶段都在前一个阶段的基础上进行功能增强和架构优化,以适应不断增长的数据处理需求。以下是具体演进过程:

  1. 列存数据库阶段:这是Gbase8a MPP Cluster产品核心功能特性的新生阶段,包括列存储特性、数据压缩存储特性、智能索引特性和并行执行特性等。南大通用公司自2008年开始研发面向大数据分析领域的列存储分析型数据库,启动Gbase8a MPP Cluster产品基础版本的研发工作,并在2009年6月正式发布Gbase8a单机版。这一阶段的架构与传统的关系型数据库架构相近,但采用了列存储方式,显著提升了分析类查询语句的性能。
  2. MPP+列存数据库阶段:这是Gbase8a MPP Cluster产品成长壮大和成熟阶段,历经了多个大版本的迭代演进。在V8.3版本中,以Gbase8a单机版本为基础,研发大规模分布式集群数据库,实现了分布式SQL、高速分布式加载、高可用、负载均衡、在线扩容、备份/恢复功能特性,并在2011年5月正式发布Gbase8a MPP Cluster的第一个版本。随后的功能演进,分别完成了V8.5和V8.6两个大的产品版本迭代,支持了更大规模的节点集群,并提供了异构数据集成能力。这一阶段,Gbase8a MPP Cluster产品逐渐成为一款成熟的国产OLAP数据库,服务于电信、金融和政企等行业客户。
  3. 逻辑数仓(LDW)阶段:这是Gbase8a MPP Cluster产品全面超越国外同类竞品阶段。2018年研发Gbase8a v9大规模虚拟集群版本,支持1000节点以上集群规模,支持虚拟集群、支持分区表等。2019年面向国产服务器进行深度优化,研发V9.5版本,针对国产生态进行深度适配和优化,支持多种国产芯片和操作系统。此外,Gbase8a V9.5版本在性能、功能、安全等方面做了大量改进,完善了CBO优化器,在小文件批量加载、高并发查询、OLAP分析函数等性能均有成倍的提升。这一阶段,Gbase8a的技术架构更加灵活和高效,能够更好地满足大规模数据分析的需求。

1.3 Gbase8a的核心技术优势

Gbase8a作为一款分布式分析型数据库,其核心技术优势围绕高性能、高扩展性、高可用性、国产化适配四大核心展开,以下从技术维度进行结构化总结:

1.3.1 列式存储与智能压缩

  • 核心原理
    • 数据按列存储,而非传统行式存储,同一列数据连续存放,减少I/O开销。
    • 支持多种压缩算法(如字典编码、游程编码、位图压缩),压缩比可达1:5~1:20
  • 优势体现
    • 查询性能提升 :分析类查询(如聚合、统计)仅需扫描相关列,速度提升10倍以上
    • 存储成本降低 :同等数据量下,存储空间节省50%~90%
  • 案例类比
    • 传统行式存储如"按行翻书",需逐页查找;列式存储如"按章节翻书",直接定位目标内容。

1.3.2 分布式架构与MPP并行计算

  • 核心原理
    • 采用Shared Nothing架构,计算与存储节点独立,无共享资源,避免单点瓶颈。
    • 支持千节点级集群扩展,通过**MPP(大规模并行处理)**将查询任务拆分为子任务,并行执行。
  • 优势体现
    • 线性扩展能力:节点数增加,查询性能随之线性提升(如10节点集群速度是单节点的10倍)。
    • 高吞吐量:支持PB级数据规模下的复杂分析,满足大数据场景需求。
  • 技术对比
    • 传统数据库(如Oracle RAC)依赖共享存储,扩展性受限;Gbase8a通过分布式架构突破瓶颈。

1.3.3 高可用性与容灾能力

  • 核心原理
    • 数据多副本存储(默认3副本),支持跨节点、跨数据中心容灾
    • 提供故障自动检测与切换机制,节点故障时业务无感知。
  • 优势体现
    • 业务连续性保障:RTO(恢复时间目标)<30秒,RPO(恢复点目标)=0,确保数据零丢失。
    • 满足合规要求:支持金融、政务等行业的等保2.0、信创等安全标准。
  • 场景示例
    • 金融交易监控系统需7×24小时运行,Gbase8a通过多副本和自动切换保障服务不中断。

1.3.4 混合负载与实时分析能力

  • 核心原理
    • 兼顾OLAP(分析型) 与**轻量级OLTP(事务型)**需求,支持高并发写入与实时查询。
    • 通过向量化执行引擎智能缓存优化,提升混合负载下的性能。
  • 优势体现
    • 实时风控:在金融场景中,支持每秒万级交易写入,同时实时分析风险行为。
    • 用户行为分析:在互联网场景中,支持日志实时写入与用户画像实时更新。
  • 技术对比
    • 传统OLAP数据库(如Teradata)难以支持实时写入;Gbase8a通过混合负载能力填补空白。

1.3.5 国产化适配与生态兼容

  • 核心原理
    • 深度适配国产芯片(如鲲鹏、飞腾、海光)、操作系统(如麒麟、统信)及中间件。
    • 提供全栈信创解决方案,支持从底层硬件到上层应用的自主可控。
  • 优势体现
    • 政策合规:满足政府、金融等行业的国产化替代要求。
    • 生态开放:兼容主流BI工具(如Tableau、Power BI)和大数据平台(如Hadoop、Spark)。
  • 行业案例
    • 某国有银行通过Gbase8a替代Oracle,实现核心系统国产化,TCO(总拥有成本)降低60%。

1.3.6 智能优化与易用性

  • 核心原理
    • 内置基于成本的优化器(CBO),自动选择最优执行计划。
    • 提供图形化管理工具(如GCluster Manager),简化集群部署、监控与运维。
  • 优势体现
    • 开发效率提升:兼容标准SQL语法,降低学习成本。
    • 运维成本降低:支持自动化扩容、故障自愈,减少人工干预。
  • 用户反馈
    • 某电信运营商通过Gbase8a的智能优化器,将复杂查询性能提升3倍以上

1.3.7 总结

维度 优势
性能 列式存储+MPP并行计算,查询性能提升10倍以上
扩展性 支持千节点级集群,PB级数据规模下线性扩展
高可用 多副本+故障自动切换,保障业务连续性
混合负载 兼顾OLAP与轻量级OLTP,支持实时分析与高并发写入
国产化 深度适配信创生态,满足自主可控要求
易用性 兼容标准SQL,提供图形化管理工具,降低开发与运维成本

2 Gbase8a SQL编程入门

2.1 规范介绍

2.1.1 标识符语法规则

数据库、表、列和别名等对象的名称都称为标识符,这部分描述GBase8aMPP Cluster中标识符允许的语法规则。

  • 注意
  1. 除了表内注明的限制,标识符不可以包含ASCII(0)或ASCII(255)。数据库、表和列名不应以空格结尾。
  2. 如果标识符是一个限制词或包含特殊字符,当用户使用它时,必须总是用``引用它,比如:SELECT*FROM`select`.id>100。
  3. 如果标识符长度超过最大长度限制,数据库、表、列、视图、存储过程的命令将报错,而别名将会截断至256个字符进行显示。
  4. 实际应用系统中,标识符不得使用GBase8aMPPCluster的保留字,也不能包含特殊字符。

2.1.2 标识符限定词

GBase 8a MPPCluster 允许名称由一个或多个标识符组成。组合名称的各个组成成分应该用英文句号字符"."分隔开。组合名称的开始部分做为限定词来使用,它影响了上下文中后面的标识符的解释。

在GBase8a MPPCluster 中,用户可以使用下列表格中的任一种方式引用一个列:

组合标识符如果需要引用,则标识符的各部分都要各自引用,而不是把组合标识符作为一个整体来引用。例如:`gs-table`.`gs-column`合法, `gs-table.gs-column`不合法。

在 一条语句的列引用中,不需要明确指定一个 table_name、database_name.table_name 或 vc_name.database_name.table_name 前缀,除非这个引用存在二义性。例如:

1)假设表t1和t2均包含一个字段c,当用一个使用了t1和t2的SELECT检索c时,在这种情况下,字段c存在二义性,因为它在这个语句所使用的表不是唯一的,因而必须通过写出t1.c或t2.c来指明用户所需的是哪个表。

2)如果从数据库db1的表t和数据库db2的表t中检索,用户必须用db1.t.col_name和db2.t.col_name 来指定引用哪个库表的列。

3)如果从vc1中数据库db1的表t和vc2中数据库db2的表t中检索,用户必须用vc1.db1.t.col_name 和 vc2.db2.t.col_name 来指定引用哪个库表的列

2.1.3 注释语法

GBase 8a MPPCluster 支持三种注释风格:

  • "#":单行注释;
  • "--":单行注释,以"--"开头到该行结束为注释内容。注意"--"(引导号)注释要求第二个引导号后至少跟着一个空格;
  • "/*注释内容*/":这种注释支持注释内容为一行或者连续的多行,还支持注释内容在行中间。/**/这个封闭的序列不一定在同一行表示,因此该语法允许多行注释。

注意

  • "--"(引导号)注释风格要求第二个引导号后至少跟着一个空格。这个语法和标准的SQL注释风格略有不同。

2.1.4 遵循的SQL标

默认支持SQL-92的主要特性。

2.1.5 数据类型

GBase 8a MPP Cluster 支持 SQL-92 中定义的绝大多数数据类型,同时也支持

SQL99 和SQL2003中定义的大部分数据类型。

GBase 8a MPPCluster 支持的数据类型,如下表所示:

|--------|-----------|
| GBase 8a MPPCluster 的数据类型 ||
| 数值型 | TINYINT |
| 数值型 | SMALLINT |
| 数值型 | INT |
| 数值型 | BIGINT |
| 数值型 | FLOAT |
| 数值型 | DOUBLE |
| 数值型 | DECIMAL |
| 数值型 | NUMERIC |
| 字符型 | CHAR |
| 字符型 | VARCHAR |
| 字符型 | TEXT |
| 二进制类型 | BLOB |
| 二进制类型 | LONGBLOB |
| 日期和时间型 | DATE |
| 日期和时间型 | DATETIME |
| 日期和时间型 | TIME |
| 日期和时间型 | TIMESTAMP |

注意:以下数据类型在gcluster层与gnode层范围有差异,gcluster层支持范围较gnode 支持范围大,建议应用开发中以较小的支持范围为准,便于应用在gcluster和gnode 层的统一处理。本章节数据类型的范围描述统一为gcluster和gnode共同支持的范围,即以较小的gnode范围为准

2.1.6 运算符与常用函数

2.1.6.1 运算符

Gbase8a支持标准SQL运算符,包括算术、比较、逻辑和位运算符。

  • 算术运算符 +, -, *, /, %(取模)
sql 复制代码
SELECT salary, salary * 0.1 AS 奖金 FROM employees;
  • 比较运算符 =, <>/!=, >, <, >=, <=, BETWEEN, IN, LIKE
sql 复制代码
SELECT * FROM employees 
WHERE salary BETWEEN 15000 AND 25000;
  • 逻辑运算符 AND, OR, NOT
sql 复制代码
SELECT * FROM employees 
WHERE dept = 'Technology' AND salary > 20000;
  • 位运算符 &, |, ^, ~(按位与、或、异或、非)
sql 复制代码
SELECT 4 & 2;  -- 结果:0

优先级 :算术运算符 > 比较运算符 > 逻辑运算符,可通过()调整优先级。

2.1.6.2 常用函数

Gbase8a支持丰富的内置函数,覆盖字符串、数值、日期等类型。

  • 字符串函数

CONCAT(str1, str2): 拼接字符串

sql 复制代码
SELECT CONCAT(name, '_', dept) FROM employees;

SUBSTRING(str, start, length): 截取子串

sql 复制代码
SUBSTRING(str, start, length): 截取子串

UPPER(str), LOWER(str): 转换大小写

sql 复制代码
SELECT UPPER(name) FROM employees;
  • 数值函数

ROUND(num, decimals): 四舍五入

sql 复制代码
SELECT ROUND(salary, -3) FROM employees;  -- 保留到千位

ABS(num): 绝对值

sql 复制代码
SELECT ABS(-100) FROM employees;
  • 日期函数

CURRENT_DATE: 当前日期

sql 复制代码
SELECT CURRENT_DATE;

DATE_ADD(date, INTERVAL expr unit): 日期加减

sql 复制代码
SELECT DATE_ADD(hire_date, INTERVAL '3 MONTH') FROM employees;
  • 聚合函数

COUNT(), SUM(), AVG(), MAX(), MIN()

sql 复制代码
SELECT dept, COUNT(*) AS 人数, AVG(salary) AS 平均薪资
FROM employees
GROUP BY dept;
  • 窗口函数

ROW_NUMBER(), RANK(), NTILE()

sql 复制代码
SELECT 
    emp_id,
    salary,
    RANK() OVER (ORDER BY salary DESC) AS 全局排名
FROM employees;

2.2 DDL语法

此处不再赘述,请参考:第五章 SQL编程系列-Oracle数据库从入门到进阶 2.1 SQL基础

2.3 数据库管理工具

2.3.1 官方管理工具

2.3.1.1 GCluster Manager(集群管理工具)
  • 核心功能
    • 集群监控:实时展示节点状态(CPU、内存、磁盘I/O)、查询性能(QPS、TPS)、任务进度等。
    • 故障诊断:自动检测节点故障、网络延迟、数据倾斜等问题,提供日志分析与报警功能。
    • 扩容与缩容:支持在线添加/删除节点,自动数据重平衡,保障业务连续性。
  • 适用场景
    • 金融级高可用集群的日常运维。
    • 电商大促前的资源弹性扩展。
2.3.1.2 GBase Migration Toolkit(数据迁移工具)
  • 核心功能
    • 异构数据迁移:支持从Oracle、MySQL、SQL Server等数据库迁移到Gbase8a,自动转换数据类型与SQL语法。
    • 全量/增量迁移:提供全量数据导入与基于日志的增量同步,最小化业务停机时间。
    • 数据校验:迁移后自动比对源端与目标端数据一致性,确保零丢失。
  • 适用场景
    • 传统数据库向Gbase8a的国产化替代。
    • 多数据源合并分析(如合并MySQL业务数据与Hadoop日志数据)。
2.3.1.3 GBaseDataStudio(全链路数据库管理)

1 功能说明

  • 开发功能
    • SQL编辑器:支持语法高亮、自动补全、多标签页查询,内置Gbase8a函数模板库。
    • 调试器:支持逐行执行、断点调试复杂存储过程,实时查看变量状态。
    • 版本控制:集成Git,实现SQL脚本的版本管理与团队协作。
  • 运维功能
    • 集群监控:可视化展示节点状态、查询性能、资源使用率,支持自定义报警阈值。
    • 备份恢复:一键执行全量/增量备份,支持备份文件加密与压缩,可验证备份集完整性。
    • 权限管理:基于角色的访问控制(RBAC),支持批量授权与审计日志追踪。
  • 优化功能
    • 执行计划分析:图形化展示查询计划,标记全表扫描、数据倾斜等性能瓶颈。
    • 索引推荐:根据查询模式自动生成索引优化建议,支持模拟执行验证效果。
    • 慢查询治理:捕获历史慢查询,提供重写建议(如避免子查询、减少JOIN操作)。

2 使用简介

  • 用户数据库 - 虚拟集群 - 新建数据库
  • 新建表
  • 设置表属性

注意:创建为NOCOPIES表,慎用!无副本、无分片备份信息,有一定的风险。

  • 设置列属性
  • 查看表属性

2.3.2 第三方工具兼容性

Gbase8a通过标准协议与接口,兼容主流第三方数据库管理工具,降低用户学习成本:

1. 通用客户端工具
  • Navicat:支持通过JDBC/ODBC连接Gbase8a,提供可视化表设计、数据导入导出、会话管理等功能。
  • DBeaver:开源多数据库工具,支持Gbase8a的元数据浏览、SQL执行、ER图生成等。

3 Gbase8a SQL编程进阶

3.1 函数

3.1.1 控制流函数

3.1.1.1 CASE函数
  • 语法1 -简单 CASE 表达式

简单 CASE 表达式将一个表达式与一组简单表达式进行比较,语法如下:

sql 复制代码
CASE expression
    WHEN value1 THEN result1
    WHEN value2 THEN result2
    ...
    [ELSE else_result]
END

函数说明

逐一匹配,当满足value=compare-value 时,返回对应的result,如果未找到匹配项,则返回ELSE后的result。如果没有ELSE子句,默认返回NULL。

如果条件中compare-value 有重叠,即value值满足多个compare-value条件时,只返回第一个满足的值。

示例 1:根据员工部门编号返回部门名称

sql 复制代码
SELECT 
    employee_id,
    employee_name,
    department_id,
    CASE department_id
        WHEN 10 THEN '财务部'
        WHEN 20 THEN '人力资源部'
        WHEN 30 THEN '销售部'
        WHEN 40 THEN '技术部'
        ELSE '其他部门'
    END AS department_name
FROM employees;

示例 2:根据订单状态代码返回状态描述

sql 复制代码
SELECT 
    order_id,
    order_date,
    status_code,
    CASE status_code
        WHEN 'P' THEN '待支付'
        WHEN 'S' THEN '已发货'
        WHEN 'D' THEN '配送中'
        WHEN 'C' THEN '已完成'
        ELSE '未知状态'
    END AS status_description
FROM orders;
  • 语法2 -搜索 CASE 表达式

搜索 CASE 表达式允许在 WHEN 子句中编写复杂的条件表达式,语法如下:

sql 复制代码
CASE
    WHEN condition1 THEN result1
    WHEN condition2 THEN result2
    ...
    [ELSE else_result]
END

函数说明

逐一判断,当condition为TRUE时,返回对应的result,如果condition全为FALSE,则返回ELSE后的result。如果没有ELSE子句,默认返回NULL。

一个CASE表达式的默认返回值类型是所有返回值的相容集合类型,具体情况视其所在语境而定:

如用在字符串语境中,则返回结果为字符串;

如用在数字语境中,则返回结果为十进制值的实数值或整数值。

示例 1:根据销售额分级

sql 复制代码
SELECT 
    salesperson_id,
    salesperson_name,
    total_sales,
    CASE
        WHEN total_sales > 1000000 THEN '钻石销售'
        WHEN total_sales > 500000 THEN '黄金销售'
        WHEN total_sales > 200000 THEN '白银销售'
        WHEN total_sales > 0 THEN '普通销售'
        ELSE '无业绩'
    END AS sales_level
FROM sales_performance;

示例 2:多条件组合判断

sql 复制代码
SELECT 
    student_id,
    student_name,
    score,
    CASE
        WHEN score >= 90 THEN 'A'
        WHEN score >= 80 AND score < 90 THEN 'B'
        WHEN score >= 70 AND score < 80 THEN 'C'
        WHEN score >= 60 AND score < 70 THEN 'D'
        WHEN score < 60 AND score >= 0 THEN 'F'
        ELSE '无效分数'
    END AS grade
FROM student_scores;

示例 3:在 UPDATE 语句中使用 CASE WHEN

sql 复制代码
UPDATE products
SET price = CASE
                WHEN stock_quantity > 100 THEN price * 0.9  -- 库存多打9折
                WHEN stock_quantity < 10 THEN price * 1.1  -- 库存少涨价10%
                ELSE price
            END,
    discount_flag = CASE
                      WHEN stock_quantity > 100 THEN 1
                      ELSE 0
                   END;

示例 4:在 ORDER BY 中使用 CASE WHEN

sql 复制代码
SELECT 
    product_id,
    product_name,
    category,
    price
FROM products
ORDER BY 
    CASE category
        WHEN '电子产品' THEN 1
        WHEN '家居用品' THEN 2
        WHEN '服装' THEN 3
        ELSE 4
    END,
    price DESC;
  • 注意事项
  1. CASE 表达式会按顺序评估条件,一旦满足某个 WHEN 子句,就会返回对应的结果,不再评估后续条件
  2. 如果没有匹配的条件且没有 ELSE 子句,CASE 表达式将返回 NULL
  3. 所有 THEN 子句返回的结果数据类型应该兼容
  4. GBase 8a 的 CASE WHEN 语法与标准 SQL 一致,可以在 SELECT、WHERE、ORDER BY、GROUP BY、HAVING 等子句中使用
3.1.1.2 DECODE函数

DECODE 函数是 GBase 8a 数据库中的一个条件表达式函数,它类似于 CASE 表达式,但语法更简洁。DECODE 函数通过比较表达式的值与一组搜索值,返回对应的结果。

  • DECODE 函数语法
sql 复制代码
DECODE(expression, search1, result1, 
               [search2, result2, ...], 
               [default_result])

参数说明:

  • expression:要比较的表达式或列名
  • searchN:与 expression 比较的值
  • resultN:当 expression 等于 searchN 时返回的结果
  • default_result:可选参数,当没有匹配项时返回的默认值

示例 1:基本用法 - 员工部门转换

sql 复制代码
SELECT 
    employee_id,
    employee_name,
    department_id,
    DECODE(department_id, 
           10, '财务部', 
           20, '人力资源部', 
           30, '销售部', 
           40, '技术部', 
           '其他部门') AS department_name
FROM employees;

示例 2:性别代码转换

sql 复制代码
SELECT 
    student_id,
    student_name,
    DECODE(gender, 
           'M', '男', 
           'F', '女', 
           '未知') AS gender_name
FROM students;

示例 3:多级评分转换

sql 复制代码
SELECT 
    exam_id,
    student_id,
    score,
    DECODE(SIGN(score-90), 1, 'A',
           DECODE(SIGN(score-80), 1, 'B',
           DECODE(SIGN(score-70), 1, 'C',
           DECODE(SIGN(score-60), 1, 'D', 'F')))) AS grade
FROM exam_results;

示例 4:在 UPDATE 语句中使用 DECODE

sql 复制代码
UPDATE products
SET discount_rate = DECODE(category_id, 
                          1, 0.1,  -- 类别1打9折
                          2, 0.15, -- 类别2打85折
                          3, 0.2,  -- 类别3打8折
                          0),      -- 其他类别不打折
    stock_status = DECODE(SIGN(stock_quantity-10), 
                         1, '充足', 
                         -1, '短缺', 
                         '临界');

示例 5:在 ORDER BY 中使用 DECODE

sql 复制代码
SELECT 
    product_id,
    product_name,
    category
FROM products
ORDER BY DECODE(category, 
               '电子产品', 1, 
               '家居用品', 2, 
               '服装', 3, 
               '食品', 4, 
               5),
         product_name;

示例 6:复杂条件判断(DECODE 嵌套)

sql 复制代码
SELECT 
    employee_id,
    employee_name,
    salary,
    DECODE(SIGN(salary-10000), 
           1, DECODE(SIGN(salary-20000), 
                    1, '高级', 
                    '中级'), 
           '初级') AS salary_level
FROM employees;
  • DECODE 与 CASE WHEN 的对比
  1. 简洁性:DECODE 语法更简洁,特别是对于简单的值映射
  2. 功能:CASE WHEN 更灵活,可以处理复杂条件(如 >, <, BETWEEN 等)
  3. 可读性:对于简单映射,DECODE 更易读;对于复杂逻辑,CASE WHEN 更清晰
  • 等价转换示例

以下 DECODE 语句:

sql 复制代码
SELECT DECODE(department_id, 10, '财务', 20, '人事', '其他') FROM employees;

等价于 CASE 表达式:

sql 复制代码
SELECT 
    CASE department_id 
        WHEN 10 THEN '财务' 
        WHEN 20 THEN '人事' 
        ELSE '其他' 
    END 
FROM employees;
  • 注意事项
  1. DECODE 在 GBase 8a 中是大小写敏感的
  2. 如果没有匹配项且未指定默认值,DECODE 返回 NULL
  3. DECODE 只能进行等值比较,不能用于范围判断
  4. 对于复杂的条件逻辑,建议使用 CASE WHEN 表达式

3.1.2 字符串函数

3.1.2.1 REPLACE 函数

REPLACE 函数是 GBase 8a 数据库中用于字符串替换的函数,它可以将字符串中指定的子串替换为新的子串。

  • REPLACE 函数语法
sql 复制代码
REPLACE(original_string, search_string, [replacement_string])

参数说明:

  • original_string:原始字符串或列名
  • search_string:要查找的子字符串
  • replacement_string:可选参数,替换后的字符串。如果省略,则删除所有找到的 search_string

示例 1:基本字符串替换

sql 复制代码
-- 将字符串中的 'abc' 替换为 'XYZ'
SELECT REPLACE('abcdefgabc', 'abc', 'XYZ') AS replaced_string;
-- 结果: 'XYZdefgXYZ'

示例 2:替换表中的列值

sql 复制代码
-- 将产品描述中的 '旧型号' 替换为 '新款'
SELECT 
    product_id,
    product_name,
    description,
    REPLACE(description, '旧型号', '新款') AS new_description
FROM products;

示例 3:删除字符串中的特定字符(省略替换字符串)

sql 复制代码
-- 删除电话号码中的连字符
SELECT 
    customer_id,
    phone,
    REPLACE(phone, '-', '') AS clean_phone
FROM customers;
-- 如果 phone 是 '138-1234-5678',结果为 '13812345678'

示例 4:在 UPDATE 语句中使用 REPLACE

sql 复制代码
-- 更新产品描述,将 '免费送货' 替换为 '包邮'
UPDATE products
SET description = REPLACE(description, '免费送货', '包邮')
WHERE description LIKE '%免费送货%';

示例 5:替换 JSON 字符串中的内容

sql 复制代码
-- 替换 JSON 字符串中的键名
SELECT 
    config_id,
    config_data,
    REPLACE(config_data, '"old_key":', '"new_key":') AS updated_config
FROM system_configs;

示例 6:替换特殊字符

sql 复制代码
-- 替换产品名称中的特殊字符
SELECT 
    product_id,
    product_name,
    REPLACE(REPLACE(product_name, '&', '和'), '#', '号') AS clean_name
FROM products;

示例 7:替换 XML 标签内容

sql 复制代码
-- 替换 XML 数据中的特定标签值
SELECT 
    record_id,
    xml_content,
    REPLACE(xml_content, '<status>old</status>', '<status>new</status>') AS updated_xml
FROM xml_records;

示例 8:批量替换多种内容

sql 复制代码
-- 嵌套使用 REPLACE 函数进行多次替换
SELECT 
    article_id,
    content,
    REPLACE(
        REPLACE(
            REPLACE(content, 'GBase', 'GBase 8a'),
            '数据库', '数据库管理系统'
        ),
        '高级', '企业级'
    ) AS enhanced_content
FROM articles;
  • 注意事项
  1. REPLACE 函数是区分大小写的,除非数据库或列使用不区分大小写的排序规则
  2. 如果要替换的字符串不存在,则返回原始字符串不变
  3. REPLACE 函数会替换所有出现的搜索字符串,而不仅仅是第一个
  4. 对于大量数据的替换操作,考虑性能影响,特别是在 WHERE 子句中使用时
  • 性能提示

对于大数据量的替换操作,特别是在 UPDATE 语句中:

  • 先测试 SELECT 语句确认替换效果
  • 考虑分批更新以减少锁表时间
  • 在非高峰期执行大规模数据更新
3.1.2.2 正则表达式函数

REGEXP_REPLACE 是 GBase 8a 数据库中用于正则表达式替换的强大函数,它允许您使用正则表达式模式进行复杂的字符串替换操作。

  • REGEXP_REPLACE 函数语法
sql 复制代码
REGEXP_REPLACE(source_string, pattern, [replacement_string], [position], [occurrence], [match_parameter])

参数说明:

  • source_string:要搜索的原始字符串或列名
  • pattern:正则表达式模式
  • replacement_string:可选,替换后的字符串(默认为空字符串)
  • position:可选,开始搜索的位置(默认为1)
  • occurrence:可选,要替换的匹配项出现次数(0表示全部替换,默认为0)
  • match_parameter:可选,修改匹配行为的参数(如'i'表示不区分大小写)

示例1:基本正则替换

sql 复制代码
-- 将所有数字替换为#
SELECT REGEXP_REPLACE('abc123def456', '[0-9]', '#') AS result;
-- 结果: 'abc###def###'

示例2:替换手机号中间四位

sql 复制代码
-- 将手机号中间4位替换为****
SELECT 
    user_id,
    phone,
    REGEXP_REPLACE(phone, '(\\d{3})\\d{4}(\\d{4})', '\\1****\\2') AS masked_phone
FROM user_profiles;
-- 输入: '13812345678' → 输出: '138****5678'

示例3:格式化日期字符串

sql 复制代码
-- 将YYYYMMDD格式日期转换为YYYY-MM-DD
SELECT 
    event_id,
    event_date,
    REGEXP_REPLACE(event_date, '(\\d{4})(\\d{2})(\\d{2})', '\\1-\\2-\\3') AS formatted_date
FROM events;
-- 输入: '20231015' → 输出: '2023-10-15'

示例4:删除所有非数字字符

sql 复制代码
-- 从字符串中提取纯数字
SELECT 
    order_id,
    order_code,
    REGEXP_REPLACE(order_code, '[^0-9]', '') AS pure_numbers
FROM orders;
-- 输入: 'ORD-2023-ABC-456' → 输出: '2023456'

示例5:替换特定模式的单词

sql 复制代码
-- 将所有以"test"开头的单词替换为"example"
SELECT 
    doc_id,
    content,
    REGEXP_REPLACE(content, '\\btest\\w*\\b', 'example') AS modified_content
FROM documents;
-- 输入: 'this is a test and testing' → 输出: 'this is a example and example'

示例6:不区分大小写替换

sql 复制代码
-- 不区分大小写替换
SELECT 
    product_id,
    description,
    REGEXP_REPLACE(description, 'color', 'colour', 1, 0, 'i') AS uk_spelling
FROM products;
-- 会替换'Color'、'COLOR'等变体

示例7:格式化金额显示

sql 复制代码
-- 将数字格式化为带千位分隔符的金额
SELECT 
    transaction_id,
    amount,
    REGEXP_REPLACE(TO_CHAR(amount, '999999999.99'), '([0-9])([0-9]{3})([0-9]{3},)?(\\.[0-9]+)?$', '\\1,\\2\\3\\4') AS formatted_amount
FROM transactions;
-- 输入: 1234567.89 → 输出: '1,234,567.89'

示例8:在UPDATE语句中使用

sql 复制代码
-- 更新电子邮件地址,将旧域名替换为新域名
UPDATE users
SET email = REGEXP_REPLACE(email, '@old\\.com$', '@new.com')
WHERE email REGEXP '@old\\.com$';

示例9:使用反向引用重组字符串

sql 复制代码
-- 交换名字和姓氏的位置
SELECT 
    user_id,
    full_name,
    REGEXP_REPLACE(full_name, '([A-Za-z]+) ([A-Za-z]+)', '\\2, \\1') AS name_reversed
FROM users;
-- 输入: 'John Smith' → 输出: 'Smith, John'

示例10:条件性替换

sql 复制代码
-- 只在特定模式后替换
SELECT 
    log_id,
    log_message,
    REGEXP_REPLACE(log_message, '(ERROR: ).*', '\\1[详细信息已隐藏]') AS sanitized_log
FROM system_logs
WHERE log_message REGEXP 'ERROR: ';

示例11:处理多行文本

sql 复制代码
-- 替换多行文本中的特定模式(GBase 8a可能需要设置特定模式)
SELECT 
    doc_id,
    REGEXP_REPLACE(document_text, '^--.*$', '', 1, 0, 'm') AS without_comments
FROM documents;
-- 删除以--开头的注释行
  • 注意事项
  1. GBase 8a 的正则表达式实现基于 POSIX 标准,某些高级特性可能不可用
  2. 复杂的正则表达式可能影响查询性能
  3. 反斜杠()在字符串中需要转义,通常需要使用双反斜杠(\)
  4. 不同版本的 GBase 8a 可能对正则表达式的支持程度不同
  • 性能优化建议
  1. 对于大数据量表,先在小样本上测试正则表达式
  2. 尽可能使用更简单的模式
  3. 考虑在应用层处理特别复杂的正则替换
  4. 为正则表达式查询创建适当的函数索引(如果支持)

3.1.3 日期函数

3.1.3.1 日期提取函数
  1. YEAR() / MONTH() / DAY()
  • 示例
sql 复制代码
-- 从日期中提取年、月、日
SELECT 
    order_date,
    YEAR(order_date) AS order_year,
    MONTH(order_date) AS order_month,
    DAY(order_date) AS order_day
FROM orders;
  1. HOUR() / MINUTE() / SECOND()
  • 示例
sql 复制代码
-- 从时间中提取时、分、秒
SELECT 
    event_time,
    HOUR(event_time) AS event_hour,
    MINUTE(event_time) AS event_minute,
    SECOND(event_time) AS event_second
FROM event_logs;
  1. DAYOFWEEK() / DAYOFMONTH() / DAYOFYEAR()
sql 复制代码
-- 获取星期几、月中的第几天、年中的第几天
SELECT 
    appointment_date,
    DAYOFWEEK(appointment_date) AS day_of_week,  -- 1=周日, 2=周一,...,7=周六
    DAYOFMONTH(appointment_date) AS day_of_month,
    DAYOFYEAR(appointment_date) AS day_of_year
FROM appointments;

4.WEEK() / WEEKOFYEAR()

sql 复制代码
-- 获取一年中的第几周
SELECT 
    sale_date,
    WEEK(sale_date) AS sale_week,
    WEEKOFYEAR(sale_date) AS week_of_year
FROM sales;

5.QUARTER() / MONTHNAME() / DAYNAME()

sql 复制代码
-- 获取季度、月份名称、星期名称
SELECT 
    report_date,
    QUARTER(report_date) AS quarter,
    MONTHNAME(report_date) AS month_name,
    DAYNAME(report_date) AS day_name
FROM reports;
3.1.3.2 日期计算函数
  1. DATE_ADD() / ADDDATE()
sql 复制代码
-- 日期加法运算
SELECT 
    order_date,
    DATE_ADD(order_date, INTERVAL 7 DAY) AS expected_delivery,
    DATE_ADD(order_date, INTERVAL 1 MONTH) AS next_month_date
FROM orders;
  1. DATE_SUB() / SUBDATE()
sql 复制代码
-- 日期减法运算
SELECT 
    event_date,
    DATE_SUB(event_date, INTERVAL 1 DAY) AS previous_day,
    DATE_SUB(event_date, INTERVAL 1 WEEK) AS last_week_date
FROM events;
  1. DATEDIFF()
sql 复制代码
-- 计算两个日期之间的天数差
SELECT 
    DATEDIFF('2023-12-31', '2023-01-01') AS days_in_year;
-- 结果: 364 (闰年365)

SELECT 
    order_date,
    ship_date,
    DATEDIFF(ship_date, order_date) AS days_to_ship
FROM orders;
  1. TIMESTAMPDIFF()
sql 复制代码
-- 计算两个时间戳之间的差值(可指定单位)
SELECT 
    TIMESTAMPDIFF(HOUR, '2023-01-01 08:00:00', '2023-01-02 10:30:00') AS hours_diff;
-- 结果: 26 (小时差)

SELECT 
    start_time,
    end_time,
    TIMESTAMPDIFF(MINUTE, start_time, end_time) AS duration_minutes
FROM sessions;
  1. PERIOD_ADD() / PERIOD_DIFF()
sql 复制代码
-- 添加月份到年月周期 / 计算两个年月周期之间的月份差
SELECT PERIOD_ADD(202311, 3);  -- 结果: 202402 (2023年11月加3个月)
SELECT PERIOD_DIFF(202312, 202301);  -- 结果: 11 (月份差)
3.1.3.3 日期格式化函数
  1. DATE_FORMAT()
sql 复制代码
-- 格式化日期输出
SELECT 
    NOW() AS current_time,
    DATE_FORMAT(NOW(), '%Y-%m-%d') AS formatted_date,
    DATE_FORMAT(NOW(), '%W, %M %d, %Y') AS long_date,
    DATE_FORMAT(NOW(), '%H:%i:%s') AS time_only,
    DATE_FORMAT(NOW(), '%Y%m%d%H%i%s') AS timestamp_str
FROM dual;
  1. TIME_FORMAT()
sql 复制代码
-- 格式化时间输出
SELECT 
    CURTIME() AS current_time,
    TIME_FORMAT(CURTIME(), '%h:%i %p') AS formatted_time
FROM dual;
-- 结果示例: '02:30 PM'
3.1.3.4 日期转换函数
  1. STR_TO_DATE()
sql 复制代码
-- 字符串转日期
SELECT STR_TO_DATE('15,11,2023', '%d,%m,%Y') AS converted_date;
-- 结果: '2023-11-15'

SELECT STR_TO_DATE('Nov 15 2023 2:30PM', '%b %d %Y %h:%i%p') AS converted_datetime;
-- 结果: '2023-11-15 14:30:00'
  1. UNIX_TIMESTAMP() / FROM_UNIXTIME()
sql 复制代码
-- Unix时间戳转换
SELECT UNIX_TIMESTAMP('2023-11-15 14:30:00') AS timestamp;
-- 结果: 1700058600

SELECT FROM_UNIXTIME(1700058600) AS datetime;
-- 结果: '2023-11-15 14:30:00'

SELECT FROM_UNIXTIME(1700058600, '%Y年%m月%d日 %H时%i分%s秒') AS formatted_datetime;
-- 结果: '2023年11月15日 14时30分00秒'
3.1.3.5 实用查询示例

示例1:计算年龄

sql 复制代码
SELECT 
    name,
    birth_date,
    TIMESTAMPDIFF(YEAR, birth_date, CURDATE()) AS age
FROM employees;

示例2:获取本季度第一天

sql 复制代码
SELECT 
    DATE_ADD(
        MAKEDATE(YEAR(CURDATE()), 1), 
        INTERVAL QUARTER(CURDATE())*3-3 MONTH
    ) AS first_day_of_quarter;

示例3:工作日计算(排除周末)

sql 复制代码
SELECT 
    order_date,
    ship_date,
    (DATEDIFF(ship_date, order_date) + 1) - 
    (FLOOR((DATEDIFF(ship_date, order_date) + 1 + 
    DAYOFWEEK(order_date) + 5) / 7) * 2 - 
    IF(DAYOFWEEK(order_date) = 1, 1, 0) - 
    IF(DAYOFWEEK(ship_date) = 7, 1, 0)) AS working_days
FROM orders;

示例4:按周分组统计

sql 复制代码
SELECT 
    YEAR(order_date) AS order_year,
    WEEK(order_date) AS order_week,
    COUNT(*) AS order_count,
    SUM(amount) AS total_amount
FROM orders
GROUP BY YEAR(order_date), WEEK(order_date)
ORDER BY order_year, order_week;
  • 注意事项
  1. GBase 8a 的日期函数可能与 MySQL 有差异,建议测试确认
  2. 日期格式字符串在不同版本中可能有所不同
  3. 对于大量历史数据的日期计算,考虑性能影响
  4. 时区处理可能需要额外配置

3.1.4 加密函数

GBase 8a 提供了一系列加密函数用于数据安全保护,主要包括哈希加密、对称加密和随机数生成等功能。以下是详细的用法示例:

3.1.4.1 AES 对称加密函数
  1. AES_ENCRYPT() - 数据加密
sql 复制代码
-- 基本用法:使用 AES 加密数据(返回二进制数据)
SELECT AES_ENCRYPT('敏感数据', '加密密钥') AS encrypted_data;

-- 实际示例:加密存储用户身份证号
CREATE TABLE user_info (
    user_id INT,
    id_card VARBINARY(255),  -- 存储加密后的二进制数据
    name VARCHAR(50)
);

-- 插入加密数据(建议对密钥进行适当处理)
INSERT INTO user_info 
VALUES (1, AES_ENCRYPT('123456199001011234', 'secret_key_2023'), '张三');

-- 查询时解密数据
SELECT 
    user_id, 
    name, 
    CAST(AES_DECRYPT(id_card, 'secret_key_2023') AS CHAR) AS id_card
FROM user_info;
  1. AES_DECRYPT() - 数据解密
sql 复制代码
-- 基本用法:解密 AES 加密的数据
SELECT AES_DECRYPT(encrypted_column, '加密密钥') FROM encrypted_table;

-- 示例:解密并转换为可读格式
SELECT 
    user_id,
    AES_DECRYPT(id_card, 'secret_key_2023') AS raw_binary_id,  -- 二进制格式
    CAST(AES_DECRYPT(id_card, 'secret_key_2023') AS CHAR) AS readable_id  -- 字符串格式
FROM user_info;
3.1.4.2 哈希函数
  1. MD5() - MD5 哈希
sql 复制代码
-- 计算字符串的 MD5 哈希值(32位十六进制)
SELECT MD5('password123') AS md5_hash;
-- 结果示例: '482c811da5d5b4bc6d497ffa98491e38'

-- 实际应用:存储密码哈希值
UPDATE users SET password = MD5(password) WHERE user_id = 1;
  1. SHA1() / SHA() - SHA-1 哈希
sql 复制代码
-- 计算 SHA-1 哈希值(40位十六进制)
SELECT SHA1('secure_data') AS sha1_hash;
-- 结果示例: 'e5e9fa1ba31ecd1aa8a66e4b6fb197e02f9580f3'

-- SHA() 是 SHA1() 的别名
SELECT SHA('secure_data') AS sha_hash;
  1. SHA2() - SHA-2 系列哈希
sql 复制代码
-- 计算 SHA-2 系列哈希(支持 224/256/384/512 位)
SELECT 
    SHA2('重要数据', 224) AS sha224,
    SHA2('重要数据', 256) AS sha256,
    SHA2('重要数据', 384) AS sha384,
    SHA2('重要数据', 512) AS sha512;

-- 实际应用:存储高安全性要求的哈希值
INSERT INTO secure_data (data_hash) 
VALUES (SHA2(CONCAT('data', 'salt_value'), 256));
3.1.4.3 Base64 编解码函数
  1. TO_BASE64() - 编码为 Base64
sql 复制代码
-- 将字符串编码为 Base64
SELECT TO_BASE64('Hello GBase') AS base64_encoded;
-- 结果示例: 'SGVsbG8gR0Jhc2U='

-- 加密数据后进行 Base64 编码(便于存储)
SELECT TO_BASE64(AES_ENCRYPT('敏感数据', '密钥')) AS encrypted_base64;
  1. FROM_BASE64() - 解码 Base64
sql 复制代码
-- 解码 Base64 字符串
SELECT FROM_BASE64('SGVsbG8gR0Jhc2U=') AS decoded_string;
-- 结果示例: 'Hello GBase' (二进制格式)

-- 转换为可读格式
SELECT CAST(FROM_BASE64('SGVsbG8gR0Jhc2U=') AS CHAR) AS readable_string;

-- 解码之前加密的数据
SELECT CAST(
    AES_DECRYPT(
        FROM_BASE64('U2FsdGVkX1+3n6Bj5m5QJZ7vJ5J5J5J5J5J5J5J5J5M='), 
        '密钥'
    ) AS CHAR
) AS original_data;
3.1.4.4 综合应用示例

示例1:安全密码存储

sql 复制代码
-- 创建带盐值的密码存储表
CREATE TABLE user_accounts (
    user_id INT PRIMARY KEY,
    username VARCHAR(50) UNIQUE,
    password_hash CHAR(64),  -- 存储 SHA256 哈希
    salt CHAR(8)
);

-- 注册用户(生成随机盐值并存储密码哈希)
SET @salt = SUBSTRING(MD5(RAND()), 1, 8);
INSERT INTO user_accounts 
VALUES (
    1, 
    'testuser', 
    SHA2(CONCAT('mypassword123', @salt), 256), 
    @salt
);

-- 验证登录
SELECT user_id 
FROM user_accounts 
WHERE username = 'testuser' 
AND SHA2(CONCAT('mypassword123', salt), 256) = password_hash;

示例2:加密敏感字段存储

sql 复制代码
-- 创建存储加密数据的表
CREATE TABLE employee_records (
    emp_id INT,
    name VARCHAR(100),
    salary VARBINARY(255),  -- 加密的薪资信息
    contact VARBINARY(512)  -- 加密的联系信息
);

-- 设置加密密钥
SET @crypto_key = 'secure_key_2023_!';

-- 插入加密数据
INSERT INTO employee_records 
VALUES (
    101, 
    '李四', 
    AES_ENCRYPT('85000', @crypto_key),
    AES_ENCRYPT('13800138000|lisi@example.com', @crypto_key)
);

-- 查询解密数据
SELECT 
    emp_id,
    name,
    CAST(AES_DECRYPT(salary, @crypto_key) AS CHAR) AS salary,
    CAST(AES_DECRYPT(contact, @crypto_key) AS CHAR) AS contact_info
FROM employee_records;

示例3:生成安全令牌

sql 复制代码
-- 生成 API 令牌(UUID + 时间戳 + 随机数的 SHA256 哈希)
SELECT SHA2(
    CONCAT(
        UUID(),
        UNIX_TIMESTAMP(),
        RAND()
    ),
    256
) AS api_token;

-- 生成带有效期的令牌(Base64 编码)
SELECT TO_BASE64(
    AES_ENCRYPT(
        CONCAT(
            'user123|',
            DATE_ADD(NOW(), INTERVAL 1 HOUR),  -- 1小时后过期
            '|',
            UUID()
        ),
        'token_secret_key'
    )
) AS expiring_token;
  • 性能与安全建议
  1. 密钥管理
    • 不要将密钥硬编码在 SQL 中
    • 考虑使用环境变量或密钥管理服务
  2. 哈希算法选择
    • 密码存储推荐使用 SHA256 或 SHA512 加盐
    • 避免使用 MD5 和 SHA1 作为密码哈希
  3. 加密字段类型
    • 使用 VARBINARY 或 BLOB 存储加密数据
    • 加密数据不宜建立索引
  4. Base64 使用场景
    • 适合需要将二进制数据转为文本存储的场景
    • 增加了约 33% 的数据体积
  5. AES 加密建议
    • 使用强密钥(至少 16 字节)
    • 考虑使用 CBC 模式(如果 GBase 8a 支持)
  • 注意事项
  1. 不同版本的 GBase 8a 可能支持不同的加密函数
  2. 某些高级功能可能需要特定配置或版本
  3. 对称加密需要安全地管理密钥,丢失密钥将导致数据无法解密
  4. 加密/解密操作会增加 CPU 负载,影响查询性能

3.1.5 窗口函数

3.1.5.1 基础窗口函数
  1. ROW_NUMBER() - 为行分配唯一序号
sql 复制代码
-- 按部门分区,按薪资降序排列,为每行分配序号
SELECT 
    employee_id,
    name,
    department,
    salary,
    ROW_NUMBER() OVER(PARTITION BY department ORDER BY salary DESC) AS dept_rank
FROM employees;
  1. RANK() 和 DENSE_RANK() - 排名函数
sql 复制代码
-- RANK(): 相同值有相同排名,但会跳过后续排名
-- DENSE_RANK(): 相同值有相同排名,但不跳过排名
SELECT 
    employee_id,
    name,
    salary,
    RANK() OVER(ORDER BY salary DESC) AS rank_pos,
    DENSE_RANK() OVER(ORDER BY salary DESC) AS dense_rank_pos
FROM employees;
  1. NTILE() - 将结果集分成指定数量的组
sql 复制代码
-- 将员工按薪资分成4个等级
SELECT 
    employee_id,
    name,
    salary,
    NTILE(4) OVER(ORDER BY salary DESC) AS salary_quartile
FROM employees;
3.1.5.2 聚合窗口函数
  1. SUM() OVER() - 累计求和
sql 复制代码
-- 计算每个部门的薪资累计和
SELECT 
    employee_id,
    name,
    department,
    salary,
    SUM(salary) OVER(PARTITION BY department ORDER BY hire_date) AS dept_running_total
FROM employees;
  1. AVG() OVER() - 移动平均
sql 复制代码
-- 计算3个月的销售移动平均
SELECT 
    sale_date,
    amount,
    AVG(amount) OVER(ORDER BY sale_date ROWS BETWEEN 2 PRECEDING AND CURRENT ROW) AS moving_avg
FROM sales;
  1. COUNT() OVER() - 计数窗口
sql 复制代码
-- 计算每个客户最近的5笔订单
SELECT 
    order_id,
    customer_id,
    order_date,
    amount,
    COUNT(order_id) OVER(PARTITION BY customer_id ORDER BY order_date 
                         ROWS BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW) AS total_orders,
    ROW_NUMBER() OVER(PARTITION BY customer_id ORDER BY order_date DESC) AS recent_rank
FROM orders
WHERE ROW_NUMBER() OVER(PARTITION BY customer_id ORDER BY order_date DESC) <= 5;
-- 注意:GBase 8a可能不支持在WHERE子句中直接使用窗口函数
3.1.5.3 分布函数
  1. PERCENT_RANK() - 百分比排名
sql 复制代码
-- 计算员工薪资在部门中的百分比排名
SELECT 
    employee_id,
    name,
    department,
    salary,
    ROUND(PERCENT_RANK() OVER(PARTITION BY department ORDER BY salary) * 100, 2) AS percentile
FROM employees;
  1. CUME_DIST() - 累积分布
sql 复制代码
-- 计算薪资的累积分布
SELECT 
    salary,
    CUME_DIST() OVER(ORDER BY salary) AS cumulative_distribution
FROM employees;
3.1.5.4 前后函数
  1. LAG() 和 LEAD() - 访问前后行数据
sql 复制代码
-- 比较当前行与前一行和后一行的数据
SELECT 
    date,
    temperature,
    LAG(temperature, 1) OVER(ORDER BY date) AS prev_day_temp,
    LEAD(temperature, 1) OVER(ORDER BY date) AS next_day_temp,
    temperature - LAG(temperature, 1) OVER(ORDER BY date) AS temp_change
FROM daily_weather;
  1. FIRST_VALUE() 和 LAST_VALUE() - 获取首尾值
sql 复制代码
-- 获取每个部门薪资最高和最低的员工信息
SELECT 
    employee_id,
    name,
    department,
    salary,
    FIRST_VALUE(name) OVER(PARTITION BY department ORDER BY salary DESC) AS highest_paid_emp,
    LAST_VALUE(name) OVER(PARTITION BY department ORDER BY salary DESC 
                       ROWS BETWEEN UNBOUNDED PRECEDING AND UNBOUNDED FOLLOWING) AS lowest_paid_emp
FROM employees;
3.1.5.5 高级用法示例

示例1:计算移动平均和累计和

sql 复制代码
-- 计算7天移动平均和30天累计和
SELECT 
    report_date,
    daily_sales,
    AVG(daily_sales) OVER(ORDER BY report_date ROWS BETWEEN 6 PRECEDING AND CURRENT ROW) AS weekly_avg,
    SUM(daily_sales) OVER(ORDER BY report_date ROWS BETWEEN 29 PRECEDING AND CURRENT ROW) AS monthly_total
FROM sales_reports;

示例2:顶级客户分析

sql 复制代码
-- 分析顶级客户的购买行为
WITH customer_stats AS (
    SELECT 
        customer_id,
        COUNT(order_id) AS order_count,
        SUM(amount) AS total_spent,
        AVG(amount) AS avg_order_value,
        RANK() OVER(ORDER BY SUM(amount) DESC) AS spending_rank
    FROM orders
    GROUP BY customer_id
)
SELECT 
    customer_id,
    order_count,
    total_spent,
    avg_order_value,
    spending_rank,
    ROUND(100 * total_spent / SUM(total_spent) OVER(), 2) AS pct_of_total
FROM customer_stats
WHERE spending_rank <= 20;

示例3:库存管理分析

sql 复制代码
-- 分析产品库存周转情况
SELECT 
    product_id,
    warehouse_id,
    current_inventory,
    days_since_last_receipt,
    daily_demand,
    current_inventory / daily_demand AS days_inventory_left,
    AVG(current_inventory) OVER(PARTITION BY product_id) AS avg_inventory,
    RANK() OVER(PARTITION BY warehouse_id ORDER BY days_inventory_left) AS priority_rank
FROM inventory
WHERE current_inventory > 0;

示例4:员工绩效分析

sql 复制代码
-- 员工绩效与部门平均对比
SELECT 
    employee_id,
    name,
    department,
    performance_score,
    ROUND(performance_score - AVG(performance_score) OVER(PARTITION BY department), 2) AS dept_diff,
    ROUND(100 * performance_score / AVG(performance_score) OVER(PARTITION BY department), 0) - 100 AS pct_diff,
    CASE 
        WHEN performance_score > AVG(performance_score) OVER(PARTITION BY department) THEN 'Above Avg'
        ELSE 'Below Avg'
    END AS dept_comparison
FROM employee_performance;
  • 性能优化建议
  1. 分区选择:合理使用 PARTITION BY 可以减少计算量
  2. 框架范围:尽量使用小的窗口框架(ROWS BETWEEN ...),避免大范围计算
  3. 索引利用:确保 ORDER BY 列有适当的索引
  4. 避免重复计算:对同一窗口的多次引用,考虑使用子查询或CTE
  • 注意事项
  1. GBase 8a 不同版本对窗口函数的支持程度可能不同
  2. 某些复杂窗口函数可能在较旧版本中不可用
  3. 在 WHERE 或 HAVING 子句中直接使用窗口函数可能会受到限制
  4. 大数据量上的窗口函数计算可能消耗较多资源

3.2 性能优化

3.2.1 性能优化核心步骤

1.诊断阶段

使用 EXPLAIN 分析执行计划

检查系统资源使用情况

识别慢查询日志

  1. 优化阶段

索引优化

SQL 语句重写

表结构优化

配置参数调整

  1. 验证阶段

基准测试

A/B 测试对比

持续监控

3.2.2 具体优化方法及示例

3.2.2.1 索引优化
  1. 创建合适索引
sql 复制代码
-- 为常用查询条件创建复合索引
CREATE INDEX idx_customer_order ON orders(customer_id, order_date);

-- 为排序字段创建索引
CREATE INDEX idx_sales_date ON sales(transaction_date DESC);

-- 为连接字段创建索引
CREATE INDEX idx_product_category ON products(category_id);
  1. 避免索引失效
sql 复制代码
-- 错误示例:索引失效(对索引列使用函数)
SELECT * FROM orders WHERE DATE(order_date) = '2023-01-01';

-- 优化后:
SELECT * FROM orders 
WHERE order_date >= '2023-01-01' AND order_date < '2023-01-02';

-- 错误示例:隐式类型转换导致索引失效
SELECT * FROM products WHERE product_code = 12345; -- 如果product_code是字符串

-- 优化后:
SELECT * FROM products WHERE product_code = '12345';
3.2.2.2 SQL 语句优化
  1. 避免 SELECT *
sql 复制代码
-- 不推荐
SELECT * FROM customers WHERE status = 'active';

-- 推荐:只查询需要的列
SELECT customer_id, name, email FROM customers WHERE status = 'active';
  1. 优化 JOIN 操作
sql 复制代码
-- 确保JOIN字段有索引
SELECT o.order_id, c.name, o.amount
FROM orders o
JOIN customers c ON o.customer_id = c.customer_id
WHERE o.order_date > '2023-01-01';

-- 小表驱动大表
SELECT /*+ LEADING(t2) */ t1.*, t2.*
FROM large_table t1
JOIN small_table t2 ON t1.id = t2.id;
  1. 优化子查询
sql 复制代码
-- 不推荐:相关子查询
SELECT * FROM employees e
WHERE salary > (SELECT AVG(salary) FROM employees WHERE dept_id = e.dept_id);

-- 优化为JOIN
SELECT e1.* 
FROM employees e1
JOIN (SELECT dept_id, AVG(salary) AS avg_sal FROM employees GROUP BY dept_id) e2
ON e1.dept_id = e2.dept_id
WHERE e1.salary > e2.avg_sal;
3.2.2.3 查询重写技巧
  1. 使用 EXISTS 替代 IN
sql 复制代码
-- 不推荐(当子查询结果集大时)
SELECT * FROM customers 
WHERE customer_id IN (SELECT customer_id FROM vip_list);

-- 推荐
SELECT * FROM customers c
WHERE EXISTS (SELECT 1 FROM vip_list v WHERE v.customer_id = c.customer_id);
  1. 合理使用 UNION ALL 替代 UNION
sql 复制代码
-- UNION 会去重,增加开销
SELECT product_id FROM current_products
UNION
SELECT product_id FROM discontinued_products;

-- 如果确定没有重复或不需要去重,使用UNION ALL
SELECT product_id FROM current_products
UNION ALL
SELECT product_id FROM discontinued_products;
  1. 分页查询优化
sql 复制代码
-- 低效分页(随着页码增加越来越慢)
SELECT * FROM large_table ORDER BY create_time LIMIT 100000, 10;

-- 优化方法1:使用索引覆盖+延迟关联
SELECT t1.* FROM large_table t1
JOIN (SELECT id FROM large_table ORDER BY create_time LIMIT 100000, 10) t2
ON t1.id = t2.id;

-- 优化方法2:记住上一页最后一条记录的值
SELECT * FROM large_table 
WHERE create_time > '上一页最后一条记录的时间值'
ORDER BY create_time LIMIT 10;
3.2.2.4 表结构优化
  1. 合理设计分区表
sql 复制代码
-- 按范围分区(例如按时间)
CREATE TABLE sales_history (
    sale_id INT,
    sale_date DATE,
    amount DECIMAL(10,2),
    ...
) PARTITION BY RANGE (YEAR(sale_date)) (
    PARTITION p2020 VALUES LESS THAN (2021),
    PARTITION p2021 VALUES LESS THAN (2022),
    PARTITION p2022 VALUES LESS THAN (2023),
    PARTITION pmax VALUES LESS THAN MAXVALUE
);

-- 按列表分区(例如按地区)
CREATE TABLE customer_data (
    cust_id INT,
    cust_name VARCHAR(100),
    region VARCHAR(20),
    ...
) PARTITION BY LIST (region) (
    PARTITION p_east VALUES IN ('北京','天津','河北'),
    PARTITION p_west VALUES IN ('四川','重庆','云南'),
    PARTITION p_other VALUES IN (DEFAULT)
);
  1. 适当使用列存储
sql 复制代码
-- GBase 8a 支持列式存储,适合分析型查询
CREATE TABLE fact_sales (
    sale_id INT,
    product_id INT,
    customer_id INT,
    sale_date DATE,
    amount DECIMAL(10,2),
    quantity INT
) DISTRIBUTE BY HASH(sale_id)
TABLEGROUP analytics_tg
STORAGE_TYPE CSTORE; -- 指定列存储
3.2.2.5 数据库配置优化
  1. 内存相关参数
sql 复制代码
-- 设置合理的排序缓冲区大小
SET GLOBAL sort_buffer_size = 4*1024*1024; -- 4MB

-- 设置连接缓冲区大小
SET GLOBAL join_buffer_size = 2*1024*1024; -- 2MB

-- 设置读取缓冲区大小
SET GLOBAL read_buffer_size = 1*1024*1024; -- 1MB
  1. 并发相关参数
sql 复制代码
-- 设置最大连接数
SET GLOBAL max_connections = 500;

-- 设置线程缓存大小
SET GLOBAL thread_cache_size = 50;

-- 设置表定义缓存
SET GLOBAL table_definition_cache = 1024;
  1. 查询缓存优化
sql 复制代码
-- 启用查询缓存(根据版本支持情况)
SET GLOBAL query_cache_type = ON;
SET GLOBAL query_cache_size = 64*1024*1024; -- 64MB
3.2.2.6 高级优化技术
  1. 使用物化视图
sql 复制代码
-- 创建物化视图(如果GBase 8a版本支持)
CREATE MATERIALIZED VIEW mv_daily_sales AS
SELECT 
    product_id,
    SUM(amount) AS total_sales,
    COUNT(*) AS transaction_count,
    AVG(amount) AS avg_sale
FROM sales
GROUP BY product_id;

-- 定期刷新物化视图
REFRESH MATERIALIZED VIEW mv_daily_sales;
  1. 使用查询提示
sql 复制代码
-- 强制使用特定索引
SELECT /*+ INDEX(orders idx_customer_order) */ * 
FROM orders 
WHERE customer_id = 1001 AND order_date > '2023-01-01';

-- 控制连接顺序
SELECT /*+ LEADING(employees departments) */ *
FROM employees e
JOIN departments d ON e.dept_id = d.dept_id
JOIN salaries s ON e.emp_id = s.emp_id;

-- 并行查询提示(如果支持)
SELECT /*+ PARALLEL(4) */ * FROM large_table WHERE condition;
  1. 批量操作优化
sql 复制代码
-- 使用批量插入替代单条插入
INSERT INTO order_items (order_id, product_id, quantity)
VALUES 
    (1001, 201, 2),
    (1001, 205, 1),
    (1002, 210, 3),
    (1002, 211, 1);

-- 使用LOAD DATA INFILE批量导入(如果支持)
LOAD DATA INFILE '/path/to/data.csv' 
INTO TABLE sales_data
FIELDS TERMINATED BY ',' 
LINES TERMINATED BY '\n';

3.2.3 性能监控与分析

  1. 使用 EXPLAIN 分析查询
sql 复制代码
EXPLAIN SELECT * FROM customers WHERE status = 'active' ORDER BY create_time LIMIT 100;

-- 详细输出示例:
/*
+----+-------------+-----------+------+---------------+------+---------+------+------+-------------+
| id | select_type | table     | type | possible_keys | key  | key_len | ref  | rows | Extra       |
+----+-------------+-----------+------+---------------+------+---------+------+------+-------------+
|  1 | SIMPLE      | customers | ref  | idx_status    | idx_status | 1    | const |  500 | Using where |
+----+-------------+-----------+------+---------------+------+---------+------+------+-------------+
*/
  1. 慢查询日志分析
sql 复制代码
-- 查看慢查询日志设置
SHOW VARIABLES LIKE '%slow_query%';

-- 启用慢查询日志
SET GLOBAL slow_query_log = ON;
SET GLOBAL long_query_time = 2; -- 记录超过2秒的查询
SET GLOBAL slow_query_log_file = '/var/log/gbase/slow.log';
  1. 性能监控视图
sql 复制代码
-- 查看当前会话执行状态
SHOW STATUS LIKE 'Handler%';

-- 查看全局状态
SHOW GLOBAL STATUS;

-- 查看进程列表
SHOW PROCESSLIST;

3.2.4 实际应用示例

示例1:复杂分析查询优化

sql 复制代码
-- 优化前(全表扫描+文件排序)
SELECT 
    c.category_name,
    YEAR(o.order_date) AS order_year,
    MONTH(o.order_date) AS order_month,
    COUNT(DISTINCT c.customer_id) AS unique_customers,
    SUM(oi.quantity) AS total_items,
    SUM(oi.quantity * oi.unit_price) AS total_sales
FROM orders o
JOIN order_items oi ON o.order_id = oi.order_id
JOIN products p ON oi.product_id = p.product_id
JOIN categories c ON p.category_id = c.category_id
WHERE o.order_date BETWEEN '2022-01-01' AND '2022-12-31'
GROUP BY c.category_name, YEAR(o.order_date), MONTH(o.order_date)
ORDER BY c.category_name, order_year, order_month;

-- 优化后(使用索引+提前过滤)
SELECT 
    c.category_name,
    YEAR(o.order_date) AS order_year,
    MONTH(o.order_date) AS order_month,
    COUNT(DISTINCT c.customer_id) AS unique_customers,
    SUM(oi.quantity) AS total_items,
    SUM(oi.quantity * oi.unit_price) AS total_sales
FROM (SELECT * FROM orders WHERE order_date BETWEEN '2022-01-01' AND '2022-12-31') o
JOIN order_items oi ON o.order_id = oi.order_id
JOIN products p ON oi.product_id = p.product_id
JOIN categories c ON p.category_id = c.category_id
GROUP BY c.category_name, YEAR(o.order_date), MONTH(o.order_date)
ORDER BY c.category_name, order_year, order_month;

示例2:报表查询优化

sql 复制代码
-- 优化前(每日报表查询)
SELECT 
    user_id,
    user_name,
    department,
    (SELECT COUNT(*) FROM orders WHERE user_id = u.user_id AND order_date = CURDATE()) AS today_orders,
    (SELECT SUM(amount) FROM orders WHERE user_id = u.user_id AND order_date = CURDATE()) AS today_amount,
    (SELECT COUNT(*) FROM orders WHERE user_id = u.user_id AND order_date BETWEEN DATE_SUB(CURDATE(), INTERVAL 7 DAY) AND CURDATE()) AS weekly_orders,
    (SELECT SUM(amount) FROM orders WHERE user_id = u.user_id AND order_date BETWEEN DATE_SUB(CURDATE(), INTERVAL 7 DAY) AND CURDATE()) AS weekly_amount
FROM users u
WHERE u.status = 'active';

-- 优化后(使用JOIN和条件聚合)
SELECT 
    u.user_id,
    u.user_name,
    u.department,
    COUNT(CASE WHEN o.order_date = CURDATE() THEN o.order_id END) AS today_orders,
    SUM(CASE WHEN o.order_date = CURDATE() THEN o.amount END) AS today_amount,
    COUNT(CASE WHEN o.order_date BETWEEN DATE_SUB(CURDATE(), INTERVAL 7 DAY) AND CURDATE() THEN o.order_id END) AS weekly_orders,
    SUM(CASE WHEN o.order_date BETWEEN DATE_SUB(CURDATE(), INTERVAL 7 DAY) AND CURDATE() THEN o.amount END) AS weekly_amount
FROM users u
LEFT JOIN orders o ON u.user_id = o.user_id
WHERE u.status = 'active'
GROUP BY u.user_id, u.user_name, u.department;
  • 总结
  1. 诊断优先:使用 EXPLAIN 和慢查询日志找出问题SQL
  2. 索引为王:创建合适的索引并避免索引失效
  3. 语句优化:重写低效SQL,避免全表扫描
  4. 结构优化:合理设计表结构和分区
  5. 配置调优:根据工作负载调整内存和并发参数
  6. 持续监控:建立性能基准并定期检查

3.3 存储过程

GBase 8a 支持存储过程的创建和使用,可以帮助封装复杂业务逻辑、提高性能并增强安全性。以下是存储过程的基本语法和示例:

3.3.1 存储过程基础

3.3.1.1 创建存储过程
sql 复制代码
DELIMITER //

CREATE PROCEDURE procedure_name([IN|OUT|INOUT] parameter_name parameter_type, ...)
BEGIN
    -- 过程体
    -- 可以包含声明、SQL语句、流程控制等
END //

DELIMITER ;
3.3.1.2 调用存储过程
sql 复制代码
CALL procedure_name(parameter_list);
3.3.1.3 删除存储过程
sql 复制代码
DROP PROCEDURE IF EXISTS procedure_name;

3.3.2 完整示例

示例1:简单存储过程(无参数)

sql 复制代码
DELIMITER //

CREATE PROCEDURE sp_get_active_customers()
BEGIN
    SELECT customer_id, name, email 
    FROM customers 
    WHERE status = 'active'
    ORDER BY registration_date DESC
    LIMIT 100;
END //

DELIMITER ;

-- 调用
CALL sp_get_active_customers();

示例2:带输入参数的存储过程

sql 复制代码
DELIMITER //

CREATE PROCEDURE sp_get_customer_orders(IN cust_id INT, IN start_date DATE, IN end_date DATE)
BEGIN
    SELECT 
        o.order_id,
        o.order_date,
        o.total_amount,
        o.status
    FROM 
        orders o
    WHERE 
        o.customer_id = cust_id
        AND o.order_date BETWEEN start_date AND end_date
    ORDER BY 
        o.order_date DESC;
END //

DELIMITER ;

-- 调用
CALL sp_get_customer_orders(1001, '2023-01-01', '2023-12-31');

示例3:带输出参数的存储过程

sql 复制代码
DELIMITER //

CREATE PROCEDURE sp_get_order_stats(
    IN cust_id INT,
    OUT order_count INT,
    OUT total_amount DECIMAL(12,2),
    OUT avg_amount DECIMAL(12,2)
)
BEGIN
    SELECT 
        COUNT(*), 
        SUM(total_amount), 
        AVG(total_amount)
    INTO 
        order_count, 
        total_amount, 
        avg_amount
    FROM 
        orders
    WHERE 
        customer_id = cust_id;
END //

DELIMITER ;

-- 调用(需要会话变量接收输出参数)
SET @cust_id = 1001;
SET @order_count = 0;
SET @total_amount = 0;
SET @avg_amount = 0;

CALL sp_get_order_stats(@cust_id, @order_count, @total_amount, @avg_amount);

-- 查看结果
SELECT @order_count AS total_orders, 
       @total_amount AS total_spent, 
       @avg_amount AS average_order_value;

示例4:带输入输出参数的存储过程

sql 复制代码
DELIMITER //

CREATE PROCEDURE sp_update_product_price(
    INOUT product_id INT, 
    IN price_change DECIMAL(10,2), 
    OUT new_price DECIMAL(10,2)
)
BEGIN
    -- 获取当前价格
    DECLARE current_price DECIMAL(10,2);
    SELECT unit_price INTO current_price FROM products WHERE id = product_id;
    
    -- 更新价格
    UPDATE products 
    SET unit_price = unit_price + price_change 
    WHERE id = product_id;
    
    -- 返回新价格
    SET new_price = current_price + price_change;
END //

DELIMITER ;

-- 调用
SET @prod_id = 101;
SET @price_change = 5.99;
SET @new_price = 0;

CALL sp_update_product_price(@prod_id, @price_change, @new_price);

-- 查看结果
SELECT @prod_id AS product_id, @new_price AS updated_price;

3.3.3 高级特性

  1. 条件控制
sql 复制代码
DELIMITER //

CREATE PROCEDURE sp_process_order(IN order_id INT)
BEGIN
    DECLARE order_status VARCHAR(20);
    DECLARE customer_balance DECIMAL(12,2);
    
    -- 获取订单状态
    SELECT status INTO order_status FROM orders WHERE id = order_id;
    
    -- 条件处理
    IF order_status = 'pending' THEN
        -- 检查客户余额
        SELECT balance INTO customer_balance FROM customers 
        WHERE id = (SELECT customer_id FROM orders WHERE id = order_id);
        
        IF customer_balance >= (SELECT total_amount FROM orders WHERE id = order_id) THEN
            -- 余额充足,处理订单
            UPDATE orders SET status = 'processing' WHERE id = order_id;
            INSERT INTO order_logs (order_id, action, timestamp)
            VALUES (order_id, 'Order processing started', NOW());
        ELSE
            -- 余额不足
            INSERT INTO order_logs (order_id, action, timestamp)
            VALUES (order_id, 'Insufficient funds', NOW());
        END IF;
    ELSEIF order_status = 'processing' THEN
        -- 其他处理逻辑
        INSERT INTO order_logs (order_id, action, timestamp)
        VALUES (order_id, 'Order already processing', NOW());
    ELSE
        -- 默认处理
        INSERT INTO order_logs (order_id, action, timestamp)
        VALUES (order_id, 'No action taken', NOW());
    END IF;
END //

DELIMITER ;
  1. 循环处理
sql 复制代码
DELIMITER //

CREATE PROCEDURE sp_batch_update_status(IN days_past INT)
BEGIN
    DECLARE done INT DEFAULT FALSE;
    DECLARE order_id_val INT;
    DECLARE order_cursor CURSOR FOR 
        SELECT id FROM orders 
        WHERE status = 'shipped' 
        AND DATEDIFF(NOW(), ship_date) > days_past;
    DECLARE CONTINUE HANDLER FOR NOT FOUND SET done = TRUE;
    
    OPEN order_cursor;
    
    read_loop: LOOP
        FETCH order_cursor INTO order_id_val;
        IF done THEN
            LEAVE read_loop;
        END IF;
        
        -- 更新订单状态
        UPDATE orders SET status = 'completed' WHERE id = order_id_val;
        
        -- 记录日志
        INSERT INTO order_logs (order_id, action, timestamp)
        VALUES (order_id_val, 'Order completed automatically', NOW());
    END LOOP;
    
    CLOSE order_cursor;
END //

DELIMITER ;
  1. 错误处理
sql 复制代码
DELIMITER //

CREATE PROCEDURE sp_safe_delete_customer(IN cust_id INT)
BEGIN
    DECLARE EXIT HANDLER FOR SQLEXCEPTION
    BEGIN
        ROLLBACK;
        SELECT 'An error occurred, transaction rolled back' AS message;
    END;
    
    START TRANSACTION;
    
    -- 先删除相关订单
    DELETE FROM orders WHERE customer_id = cust_id;
    
    -- 再删除客户
    DELETE FROM customers WHERE id = cust_id;
    
    COMMIT;
    
    SELECT CONCAT('Customer ', cust_id, ' and related data deleted successfully') AS message;
END //

DELIMITER ;

3.3.4 实际业务场景示例

示例:月度销售报表生成

sql 复制代码
DELIMITER //

CREATE PROCEDURE sp_generate_monthly_report(
    IN report_year INT, 
    IN report_month INT,
    OUT success_flag INT
)
BEGIN
    DECLARE exit handler for sqlexception
    BEGIN
        SET success_flag = 0;
        ROLLBACK;
    END;
    
    SET success_flag = 1;
    START TRANSACTION;
    
    -- 检查是否已存在该月报表
    IF EXISTS (SELECT 1 FROM monthly_reports WHERE year = report_year AND month = report_month) THEN
        -- 更新现有报表
        UPDATE monthly_reports mr
        SET 
            total_sales = (
                SELECT SUM(amount) 
                FROM orders 
                WHERE YEAR(order_date) = report_year 
                AND MONTH(order_date) = report_month
            ),
            total_orders = (
                SELECT COUNT(*) 
                FROM orders 
                WHERE YEAR(order_date) = report_year 
                AND MONTH(order_date) = report_month
            ),
            avg_order_value = (
                SELECT AVG(amount) 
                FROM orders 
                WHERE YEAR(order_date) = report_year 
                AND MONTH(order_date) = report_month
            ),
            last_updated = NOW()
        WHERE mr.year = report_year AND mr.month = report_month;
    ELSE
        -- 插入新报表
        INSERT INTO monthly_reports (year, month, total_sales, total_orders, avg_order_value, created_at, last_updated)
        SELECT 
            report_year,
            report_month,
            SUM(amount),
            COUNT(*),
            AVG(amount),
            NOW(),
            NOW()
        FROM 
            orders
        WHERE 
            YEAR(order_date) = report_year 
            AND MONTH(order_date) = report_month;
    END IF;
    
    -- 生成报表明细
    DELETE FROM monthly_report_details 
    WHERE year = report_year AND month = report_month;
    
    INSERT INTO monthly_report_details (year, month, product_id, product_name, category, total_quantity, total_amount)
    SELECT 
        report_year,
        report_month,
        p.id,
        p.name,
        c.name AS category,
        SUM(oi.quantity),
        SUM(oi.quantity * oi.unit_price)
    FROM 
        orders o
        JOIN order_items oi ON o.id = oi.order_id
        JOIN products p ON oi.product_id = p.id
        JOIN categories c ON p.category_id = c.id
    WHERE 
        YEAR(o.order_date) = report_year 
        AND MONTH(o.order_date) = report_month
    GROUP BY 
        p.id, p.name, c.name;
    
    COMMIT;
END //

DELIMITER ;

-- 调用示例
SET @success = 0;
CALL sp_generate_monthly_report(2023, 11, @success);
SELECT @success AS report_generation_status;

3.3.5 调试技巧

  1. 使用 SELECT 输出中间结果
sql 复制代码
CREATE PROCEDURE sp_debug_example()
BEGIN
    DECLARE var1 INT DEFAULT 0;
    SET var1 = 10;
    
    -- 调试输出
    SELECT CONCAT('Debug - var1 value: ', var1) AS debug_message;
    
    -- 继续处理...
END
  1. 临时表记录执行过程
sql 复制代码
CREATE PROCEDURE sp_with_logging()
BEGIN
    -- 创建临时日志表
    DROP TEMPORARY TABLE IF EXISTS temp_proc_log;
    CREATE TEMPORARY TABLE temp_proc_log (
        step_no INT,
        step_name VARCHAR(100),
        log_time DATETIME,
        details VARCHAR(500)
    );
    
    -- 记录步骤
    INSERT INTO temp_proc_log VALUES (1, 'Start', NOW(), 'Procedure started');
    
    -- 业务逻辑...
    
    INSERT INTO temp_proc_log VALUES (2, 'Query executed', NOW(), 'Main query completed');
    
    -- 查看日志
    SELECT * FROM temp_proc_log ORDER BY step_no;
END
  • 注意事项
  1. 命名规范 :使用 sp_ 前缀标识存储过程
  2. 参数清晰:明确指定 IN/OUT/INOUT 参数类型
  3. 错误处理:始终包含错误处理逻辑
  4. 事务管理:对数据修改操作使用事务
  5. 注释充分:复杂逻辑添加注释说明
  6. 性能考虑:避免在存储过程中执行大量数据操作
  7. 版本控制:将存储过程定义纳入数据库变更管理
相关推荐
萧曵 丶30 分钟前
Spring @TransactionalEventListener
java·数据库·spring·事务·transactional·异步
胡斌附体33 分钟前
mobaxterm终端sqlplus乱码问题解决
数据库·乱码·sqlplus·字符集设置
moon66sun43 分钟前
开源项目XYZ.ESB:数据库到数据库(DB->DB)集成
数据库·esb
TDengine (老段)1 小时前
使用 StatsD 向 TDengine 写入
java·大数据·数据库·时序数据库·iot·tdengine·涛思数据
DarkAthena1 小时前
【GaussDB】深度解析:创建存储过程卡死且无法Kill会话的疑难排查
数据库·gaussdb
Gauss松鼠会1 小时前
GaussDB权限管理:从RBAC到精细化控制的企业级安全实践
大数据·数据库·安全·database·gaussdb
时序数据说1 小时前
时序数据库IoTDB用户自定义函数(UDF)使用指南
大数据·数据库·物联网·开源·时序数据库·iotdb
Java初学者小白2 小时前
秋招Day15 - Redis - 缓存设计
java·数据库·redis·缓存
绅士玖3 小时前
前端数据存储总结:Cookie、localStorage、sessionStorage与IndexedDB的使用与区别
前端·javascript·数据库
RainbowSea3 小时前
15. MySQL 多版本并发控制
java·sql·mysql