概述
关系型数据库:像“Excel表格联合作战”的管家
关系型数据库就像一个超级智能的表格管理系统,专门处理数据之间有复杂关联的情况。比如学生和成绩、订单和商品、用户和评论——这些数据像蜘蛛网一样相互连接,用键值数据库的“独立抽屉”会手忙脚乱,而关系型数据库能像拼积木一样把它们严丝合缝地组装起来。
🌰 举个接地气的例子
场景1:班级学生管理系统
假设你是班主任,需要管理:
- 学生表(学号、姓名、性别)
- 课程表(课程编号、课程名称、任课老师)
- 成绩表(学号、课程编号、分数)
如果用键值数据库:
- 存储像这样👇(数据冗余且难关联):
键: "学生_1001" → 值: {姓名:"张三", 性别:"男", 数学成绩:90, 英语老师:"王老师"} 键: "学生_1002" → 值: {姓名:"李四", 性别:"女", 数学成绩:85, 英语老师:"王老师"}
→ 问题:王老师改名时,需要遍历所有学生修改,效率极低。
用关系型数据库:
- 三张表互联,用“学号”“课程编号”作为桥梁:
学生表 课程表 成绩表 学号:1001 张三 男 课程:C001 数学 张老师 学号:1001 C001 90 学号:1002 李四 女 课程:C002 英语 王老师 学号:1002 C001 85 - 需要查“张三的数学成绩”时,只需一句SQL:
SELECT 分数 FROM 成绩表 WHERE 学号=(SELECT 学号 FROM 学生表 WHERE 姓名='张三') AND 课程编号=(SELECT 课程编号 FROM 课程表 WHERE 课程名称='数学');
→ 优势:改老师名字只需动课程表一处,所有关联数据自动生效。
场景2:电商订单系统
一个订单可能涉及:用户信息、商品详情、收货地址、支付记录。
- 键值数据库的困局:
如果把整个订单存成一个Value:{ "订单号": "202310011234", "用户": {"用户ID": 789, "姓名": "王五", "手机": "13800138000"}, "商品": [ {"商品ID": "G1001", "名称": "手机", "价格": 2999}, {"商品ID": "G1002", "名称": "耳机", "价格": 199} ] }
→ 痛点:想统计“所有买过手机的用户的手机号”,需要扫描所有订单,解析JSON,效率极低。
- 关系型数据库的解法:
用户表 商品表 订单表 订单详情表 用户ID 姓名 手机 商品ID 名称 价格 订单ID 用户ID 总金额 订单ID 商品ID 数量 - 查“买过手机的用户的手机号”:
SELECT 用户表.手机 FROM 用户表 JOIN 订单表 ON 用户表.用户ID = 订单表.用户ID JOIN 订单详情表 ON 订单表.订单ID = 订单详情表.订单ID JOIN 商品表 ON 订单详情表.商品ID = 商品表.商品ID WHERE 商品表.名称 = '手机';
→ 优势:像拼乐高一样关联多表,直接精准定位数据。
🤔 什么时候该用关系型数据库?
- 数据像家族族谱:需要理清“谁是谁的谁”(如用户→订单→商品)
- 经常要“跨界”查询:比如“统计每个班级的平均分,并显示班主任名字”
- 需要强一致性:比如转账操作(A账户减钱,B账户加钱必须同时成功)
⚠️ 什么时候别用它?
- 数据像散装沙子:比如独立缓存项(今日天气、临时验证码)
- 每秒要处理10万+请求:比如微博热搜实时计数(适合用Redis)
- 数据结构天天变:比如快速迭代中的实验性功能配置(适合用文档数据库如MongoDB)
现实中的工具
- SQLite:手机APP本地存储(如微信聊天记录)、小型软件
- MySQL:淘宝早期用户系统、知乎问答数据
- PostgreSQL:苹果的iCloud部分服务、地理信息系统
// 导入关系型数据库模块和业务错误模块
import { relationalStore } from '@kit.ArkData';
import { BusinessError } from '@kit.BasicServicesKit';// 定义数据库配置
const STORE_CONFIG: relationalStore.StoreConfig = {name: 'RdbTest.db', // 数据库文件名securityLevel: relationalStore.SecurityLevel.S3, // 数据库安全级别encrypt: false, // 是否加密数据库,默认为不加密customDir: 'customDir/subCustomDir', // 自定义数据库路径(可选)isReadOnly: false // 是否以只读方式打开数据库,默认为可读写
};// 定义创建表的 SQL 语句
const SQL_CREATE_TABLE ='CREATE TABLE IF NOT EXISTS EMPLOYEE (ID INTEGER PRIMARY KEY AUTOINCREMENT, NAME TEXT NOT NULL, AGE INTEGER, SALARY REAL, CODES BLOB, IDENTITY UNLIMITED INT)'; // 创建 EMPLOYEE 表// 定义插入数据的值
let value1 = 'Lisa';
let value2 = 18;
let value3 = 100.5;
let value4 = new Uint8Array([1, 2, 3, 4, 5]);
let value5 = BigInt('15822401018187971961171');let value6 = 'Rose';
let value7 = 22;
let value8 = 200.5;
let value9 = new Uint8Array([1, 2, 3, 4, 5]);
let value10 = BigInt('15822401018187971967863');// 定义插入的数据对象
const valueBucket1: relationalStore.ValuesBucket = {'NAME': value1,'AGE': value2,'SALARY': value3,'CODES': value4,'IDENTITY': value5,
};const valueBucket5: relationalStore.ValuesBucket = {NAME: value6,AGE: value7,SALARY: value8,CODES: value9,IDENTITY: value10,
};// 定义组件结构
@Entry
@Component
struct RdbStore {context = getContext(this); // 获取上下文store: relationalStore.RdbStore | undefined = undefined; // 数据库实例/*** 插入数据到数据库*/async rdbInsert() {if (this.store !== undefined) {await (this.store as relationalStore.RdbStore).insert('EMPLOYEE', valueBucket1, (err: BusinessError, rowId: number) => {if (err) {console.error(`Failed to insert data. Code:${err.code}, message:${err.message}`); // 插入失败时的日志return;}console.log(`Succeeded in inserting data. rowId:${rowId}`); // 插入成功时的日志});console.log('rdbInsert'); // 调试日志}}/*** 更新数据库中的数据*/async rdbUpdate() {let predicates1 = new relationalStore.RdbPredicates('EMPLOYEE'); // 创建查询条件predicates1.equalTo('NAME', 'Lisa'); // 匹配 NAME 为 'Lisa' 的记录if (this.store !== undefined) {await (this.store as relationalStore.RdbStore).update(valueBucket5, predicates1, (err: BusinessError, rows: number) => {if (err) {console.error(`Failed to update data. Code:${err.code}, message:${err.message}`); // 更新失败时的日志return;}console.log(`Succeeded in updating data. row count: ${rows}`); // 更新成功时的日志});console.log('rdbUpdate'); // 调试日志}}/*** 删除数据库中的数据*/async rdbDelete() {let predicates1 = new relationalStore.RdbPredicates('EMPLOYEE'); // 创建查询条件predicates1.equalTo('NAME', 'Lisa'); // 匹配 NAME 为 'Lisa' 的记录if (this.store !== undefined) {await (this.store as relationalStore.RdbStore).delete(predicates1, (err: BusinessError, rows: number) => {if (err) {console.error(`Failed to delete data. Code:${err.code}, message:${err.message}`); // 删除失败时的日志return;}console.log(`Delete rows: ${rows}`); // 删除成功时的日志});console.log('rdbDelete'); // 调试日志}}/*** 查询数据库中的数据*/async rdbQuery() {let predicates2 = new relationalStore.RdbPredicates('EMPLOYEE'); // 创建查询条件predicates2.equalTo('NAME', 'Lisa'); // 匹配 NAME 为 'Lisa' 的记录if (this.store !== undefined) {await (this.store as relationalStore.RdbStore).query(predicates2, ['ID', 'NAME', 'AGE', 'SALARY', 'IDENTITY'], (err: BusinessError, resultSet) => {if (err) {console.error(`Failed to query data. Code:${err.code}, message:${err.message}`); // 查询失败时的日志return;}console.info(`ResultSet column names: ${resultSet.columnNames}, column count: ${resultSet.columnCount}`); // 查询结果信息while (resultSet.goToNextRow()) { // 遍历查询结果const id = resultSet.getLong(resultSet.getColumnIndex('ID'));const name = resultSet.getString(resultSet.getColumnIndex('NAME'));const age = resultSet.getLong(resultSet.getColumnIndex('AGE'));const salary = resultSet.getDouble(resultSet.getColumnIndex('SALARY'));const identity = resultSet.getValue(resultSet.getColumnIndex('IDENTITY'));console.log(`id=${id}, name=${name}, age=${age}, salary=${salary}, identity=${identity}`); // 输出每条记录的信息}resultSet.close(); // 关闭结果集});console.log('rdbQuery'); // 调试日志}}/*** 获取数据库实例*/async getRdbStore() {console.log('getRdbStore'); // 调试日志await relationalStore.getRdbStore(this.context, STORE_CONFIG, (err, store) => {if (err) {console.error(`Failed to get RdbStore. Code:${err.code}, message:${err.message}`); // 获取数据库失败时的日志return;}console.info('Succeeded in getting RdbStore.'); // 获取数据库成功时的日志store.executeSql(SQL_CREATE_TABLE); // 执行建表语句this.store = store; // 将数据库实例赋值给成员变量});}/*** 页面即将消失时关闭数据库*/async aboutToDisappear() {if (this.store !== undefined) {await this.store.close(); // 关闭数据库}}/*** 页面即将出现时初始化数据库*/async aboutToAppear() {await this.getRdbStore(); // 初始化数据库}/*** 构建页面布局*/build() {Column() {Button("添加数据") // 添加数据按钮.onClick(() => {this.rdbInsert(); // 调用插入方法}).width('100%').margin({ top: 10 });Button("查询数据") // 查询数据按钮.onClick(async () => {this.rdbQuery(); // 调用查询方法}).width('100%').margin({ top: 10 });Button("修改数据") // 修改数据按钮.onClick(async () => {this.rdbUpdate(); // 调用更新方法}).width('100%').margin({ top: 10 });Button("删除数据") // 删除数据按钮.onClick(async () => {this.rdbDelete(); // 调用删除方法}).width('100%').margin({ top: 10 });}.width('100%'); // 设置列宽为 100%}
}