|
|
@@ -0,0 +1,886 @@
|
|
|
+# TsDB2 完整操作教程
|
|
|
+
|
|
|
+本教程涵盖 TsDB2 时序数据库的所有核心操作,包括建表、增删改查、条件查询、索引管理、事务控制等。
|
|
|
+
|
|
|
+---
|
|
|
+
|
|
|
+## 📋 目录
|
|
|
+
|
|
|
+1. [基础概念](#1-基础概念)
|
|
|
+2. [建表与索引](#2-建表与索引)
|
|
|
+3. [插入数据 (Create)](#3-插入数据-create)
|
|
|
+4. [查询数据 (Read)](#4-查询数据-read)
|
|
|
+5. [更新数据 (Update)](#5-更新数据-update)
|
|
|
+6. [删除数据 (Delete)](#6-删除数据-delete)
|
|
|
+7. [条件查询详解](#7-条件查询详解)
|
|
|
+8. [排序与分页](#8-排序与分页)
|
|
|
+9. [聚合查询](#9-聚合查询)
|
|
|
+10. [多表关联](#10-多表关联)
|
|
|
+11. [事务与提交](#11-事务与提交)
|
|
|
+12. [高级特性](#12-高级特性)
|
|
|
+
|
|
|
+---
|
|
|
+
|
|
|
+## 1. 基础概念
|
|
|
+
|
|
|
+### 1.1 数据模型定义
|
|
|
+
|
|
|
+TsDB2 使用 Go struct 定义数据模型,通过 `tsdb` tag 指定字段类型:
|
|
|
+
|
|
|
+```go
|
|
|
+package main
|
|
|
+
|
|
|
+import (
|
|
|
+ "TsDB2/DB"
|
|
|
+ "fmt"
|
|
|
+)
|
|
|
+
|
|
|
+// 员工表模型
|
|
|
+type Employee struct {
|
|
|
+ DB.BaseModel // 必须嵌入,提供 ID 字段
|
|
|
+ Name string `tsdb:"32byte"` // 字符串字段,最大32字节
|
|
|
+ Age int64 `tsdb:"8byte"` // 整数字段,8字节
|
|
|
+ Score int64 `tsdb:"8byte"` // 整数字段,8字节
|
|
|
+ Dept string `tsdb:"16byte"` // 字符串字段,最大16字节
|
|
|
+}
|
|
|
+
|
|
|
+// 订单表模型
|
|
|
+type Order struct {
|
|
|
+ DB.BaseModel
|
|
|
+ EmployeeID int64 `tsdb:"8byte"`
|
|
|
+ Amount int64 `tsdb:"8byte"`
|
|
|
+ Status int64 `tsdb:"8byte"` // 0=pending, 1=done, 2=cancel
|
|
|
+}
|
|
|
+```
|
|
|
+
|
|
|
+**支持的字段类型:**
|
|
|
+- `1byte` ~ `256byte`: 变长字符串/二进制
|
|
|
+- `8byte`: int64 / uint64
|
|
|
+- `4byte`: int32 / uint32
|
|
|
+- `2byte`: int16 / uint16
|
|
|
+- `1byte`: int8 / uint8
|
|
|
+
|
|
|
+### 1.2 打开数据库
|
|
|
+
|
|
|
+```go
|
|
|
+// 打开或创建数据库文件
|
|
|
+db, err := DB.Open("mydb.db")
|
|
|
+if err != nil {
|
|
|
+ panic(err)
|
|
|
+}
|
|
|
+defer db.Close() // 程序退出前关闭
|
|
|
+
|
|
|
+// 设置自动提交延迟(防抖)
|
|
|
+db.SetAutoCommitDelay(500 * time.Millisecond) // 500ms 后自动 Commit
|
|
|
+```
|
|
|
+
|
|
|
+---
|
|
|
+
|
|
|
+## 2. 建表与索引
|
|
|
+
|
|
|
+### 2.1 创建表
|
|
|
+
|
|
|
+```go
|
|
|
+// ✅ 推荐方式:自动推导表名(从类型名 Employee -> "employee")
|
|
|
+empTbl, err := DB.UseTable[Employee](db)
|
|
|
+if err != nil {
|
|
|
+ panic(err)
|
|
|
+}
|
|
|
+
|
|
|
+// 可选:手动指定表名
|
|
|
+orderTbl, err := DB.UseTable[Order](db, "orders") // 自定义表名为 "orders"
|
|
|
+if err != nil {
|
|
|
+ panic(err)
|
|
|
+}
|
|
|
+```
|
|
|
+
|
|
|
+**工作原理:**
|
|
|
+- `UseTable[Employee](db)` → 自动推导表名为 `"employee"`(首字母小写)
|
|
|
+- `UseTable[OrderItem](db)` → 自动推导表名为 `"orderItem"`
|
|
|
+- `UseTable[T](db, "custom_name")` → 使用自定义表名
|
|
|
+
|
|
|
+### 2.2 创建索引
|
|
|
+
|
|
|
+TsDB2 支持多种索引类型:
|
|
|
+
|
|
|
+```go
|
|
|
+// 获取表的底层 Core 对象
|
|
|
+empCore, _ := db.CoreDB().FindTable("employee")
|
|
|
+
|
|
|
+// 1️⃣ 主键 B+ 树索引(自动创建,基于 ID 字段)
|
|
|
+// 无需手动创建,UseTable 时自动生成 pk_employee
|
|
|
+
|
|
|
+// 2️⃣ 二级 B+ 树索引(单字段)
|
|
|
+empCore.CreateIndex("idx_age", page.IndexTypeBTree, []byte{2}) // Age 字段索引
|
|
|
+empCore.CreateIndex("idx_dept", page.IndexTypeBTree, []byte{4}) // Dept 字段索引
|
|
|
+
|
|
|
+// 3️⃣ 时序索引(时间范围查询优化)
|
|
|
+// empCore.CreateIndex("idx_time", page.IndexTypeTime, []byte{1})
|
|
|
+
|
|
|
+// 4️⃣ 复合索引(多字段组合)
|
|
|
+// empCore.CreateIndex("idx_age_dept", page.IndexTypeCompose, []byte{2, 4})
|
|
|
+
|
|
|
+orderCore, _ := db.CoreDB().FindTable("order")
|
|
|
+orderCore.CreateIndex("idx_status", page.IndexTypeBTree, []byte{3}) // Status 索引
|
|
|
+```
|
|
|
+
|
|
|
+**索引类型说明:**
|
|
|
+- `IndexTypePrimary` (2): 主键索引,叶子节点存 `[Key → DataPage]`
|
|
|
+- `IndexTypeBTree` (5): 二级 B+ 树索引,叶子节点存 `[Key → PKID]`
|
|
|
+- `IndexTypeTime` (1): 时序索引,优化时间范围查询
|
|
|
+- `IndexTypeCompose` (4): 复合索引,多字段联合索引
|
|
|
+
|
|
|
+---
|
|
|
+
|
|
|
+## 3. 插入数据 (Create)
|
|
|
+
|
|
|
+### 3.1 单行插入
|
|
|
+
|
|
|
+```go
|
|
|
+err := empTbl.Insert(&Employee{
|
|
|
+ Name: "张三",
|
|
|
+ Age: 25,
|
|
|
+ Score: 95,
|
|
|
+ Dept: "工程部",
|
|
|
+})
|
|
|
+if err != nil {
|
|
|
+ fmt.Printf("插入失败: %v\n", err)
|
|
|
+}
|
|
|
+```
|
|
|
+
|
|
|
+### 3.2 批量插入
|
|
|
+
|
|
|
+```go
|
|
|
+// 插入 10000 条员工记录
|
|
|
+for i := 1; i <= 10000; i++ {
|
|
|
+ err := empTbl.Insert(&Employee{
|
|
|
+ Name: fmt.Sprintf("Emp%07d", i),
|
|
|
+ Age: int64(20 + (i % 50)), // 年龄 20~69
|
|
|
+ Score: int64(i % 1000), // 分数 0~999
|
|
|
+ Dept: []string{"工程部", "销售部", "人事部", "财务部", "运维部"}[i%5],
|
|
|
+ })
|
|
|
+ if err != nil {
|
|
|
+ panic(fmt.Sprintf("插入第 %d 行失败: %v", i, err))
|
|
|
+ }
|
|
|
+
|
|
|
+ // 每 1000 行打印进度
|
|
|
+ if i%1000 == 0 {
|
|
|
+ fmt.Printf("已插入 %d 行\n", i)
|
|
|
+ }
|
|
|
+}
|
|
|
+```
|
|
|
+
|
|
|
+### 3.3 性能优化:批量 Commit
|
|
|
+
|
|
|
+```go
|
|
|
+// 禁用自动提交,手动控制 Commit 时机
|
|
|
+db.SetAutoCommitDelay(0) // 禁用防抖
|
|
|
+
|
|
|
+// 批量插入 10 万行
|
|
|
+for i := 1; i <= 100000; i++ {
|
|
|
+ empTbl.Insert(&Employee{...})
|
|
|
+}
|
|
|
+
|
|
|
+// 一次性提交所有数据
|
|
|
+fmt.Println("开始 Commit...")
|
|
|
+t1 := time.Now()
|
|
|
+if err := db.Commit(); err != nil {
|
|
|
+ panic(err)
|
|
|
+}
|
|
|
+fmt.Printf("Commit 完成,耗时: %.2fs\n", time.Since(t1).Seconds())
|
|
|
+```
|
|
|
+
|
|
|
+**性能对比:**
|
|
|
+- 每行自动 Commit: 10万行约 100秒
|
|
|
+- 批量 Commit: 10万行约 4秒(**快 25 倍**)
|
|
|
+
|
|
|
+---
|
|
|
+
|
|
|
+## 4. 查询数据 (Read)
|
|
|
+
|
|
|
+### 4.1 主键精确查询
|
|
|
+
|
|
|
+```go
|
|
|
+// 根据 ID 查询
|
|
|
+emp, err := empTbl.Find(1)
|
|
|
+if err != nil {
|
|
|
+ fmt.Printf("未找到 ID=1 的记录\n")
|
|
|
+} else {
|
|
|
+ fmt.Printf("ID=%d, Name=%s, Age=%d, Score=%d, Dept=%s\n",
|
|
|
+ emp.ID, emp.Name, emp.Age, emp.Score, emp.Dept)
|
|
|
+}
|
|
|
+```
|
|
|
+
|
|
|
+### 4.2 条件查询
|
|
|
+
|
|
|
+```go
|
|
|
+// WHERE Age = 25
|
|
|
+rows, err := empTbl.Where("Age", "=", int64(25)).Find()
|
|
|
+if err != nil {
|
|
|
+ panic(err)
|
|
|
+}
|
|
|
+fmt.Printf("找到 %d 名 25 岁的员工\n", len(rows))
|
|
|
+for _, emp := range rows {
|
|
|
+ fmt.Printf(" - %s (Score: %d)\n", emp.Name, emp.Score)
|
|
|
+}
|
|
|
+```
|
|
|
+
|
|
|
+### 4.3 遍历所有记录
|
|
|
+
|
|
|
+```go
|
|
|
+// 全表扫描
|
|
|
+allEmps, err := empTbl.Find() // 无条件,返回所有记录
|
|
|
+if err != nil {
|
|
|
+ panic(err)
|
|
|
+}
|
|
|
+fmt.Printf("总共有 %d 名员工\n", len(allEmps))
|
|
|
+
|
|
|
+// 循环处理
|
|
|
+for i, emp := range allEmps {
|
|
|
+ if emp.Score >= 90 {
|
|
|
+ fmt.Printf("[%d] 优秀员工: %s, Score=%d\n", i+1, emp.Name, emp.Score)
|
|
|
+ }
|
|
|
+}
|
|
|
+```
|
|
|
+
|
|
|
+---
|
|
|
+
|
|
|
+## 5. 更新数据 (Update)
|
|
|
+
|
|
|
+### 5.1 根据主键更新
|
|
|
+
|
|
|
+```go
|
|
|
+// 更新 ID=1 的员工信息
|
|
|
+err := empTbl.Update(&Employee{
|
|
|
+ BaseModel: DB.BaseModel{ID: 1}, // 必须指定 ID
|
|
|
+ Name: "李四",
|
|
|
+ Age: 30,
|
|
|
+ Score: 98,
|
|
|
+ Dept: "技术部",
|
|
|
+})
|
|
|
+if err != nil {
|
|
|
+ fmt.Printf("更新失败: %v\n", err)
|
|
|
+}
|
|
|
+
|
|
|
+// 验证更新
|
|
|
+emp, _ := empTbl.Find(1)
|
|
|
+fmt.Printf("更新后: Name=%s, Age=%d\n", emp.Name, emp.Age)
|
|
|
+// 输出: 更新后: Name=李四, Age=30
|
|
|
+```
|
|
|
+
|
|
|
+### 5.2 批量更新(循环)
|
|
|
+
|
|
|
+```go
|
|
|
+// 给所有工程部员工加 10 分
|
|
|
+emps, _ := empTbl.Where("Dept", "=", "工程部").Find()
|
|
|
+for _, emp := range emps {
|
|
|
+ emp.Score += 10
|
|
|
+ empTbl.Update(emp)
|
|
|
+}
|
|
|
+fmt.Printf("已为 %d 名工程部员工加分\n", len(emps))
|
|
|
+```
|
|
|
+
|
|
|
+---
|
|
|
+
|
|
|
+## 6. 删除数据 (Delete)
|
|
|
+
|
|
|
+### 6.1 根据主键删除
|
|
|
+
|
|
|
+```go
|
|
|
+// 删除 ID=2 的员工
|
|
|
+err := empTbl.Delete(2)
|
|
|
+if err != nil {
|
|
|
+ fmt.Printf("删除失败: %v\n", err)
|
|
|
+}
|
|
|
+
|
|
|
+// 验证删除
|
|
|
+_, err = empTbl.Find(2)
|
|
|
+if err != nil {
|
|
|
+ fmt.Println("✅ ID=2 已成功删除")
|
|
|
+}
|
|
|
+```
|
|
|
+
|
|
|
+### 6.2 批量删除(循环)
|
|
|
+
|
|
|
+```go
|
|
|
+// 删除所有分数低于 60 的员工
|
|
|
+lowScores, _ := empTbl.Where("Score", "<", int64(60)).Find()
|
|
|
+fmt.Printf("找到 %d 名低分员工\n", len(lowScores))
|
|
|
+
|
|
|
+for _, emp := range lowScores {
|
|
|
+ empTbl.Delete(emp.ID)
|
|
|
+}
|
|
|
+fmt.Println("✅ 已删除所有低分员工")
|
|
|
+
|
|
|
+// 验证
|
|
|
+remaining, _ := empTbl.Count()
|
|
|
+fmt.Printf("剩余员工数: %d\n", remaining)
|
|
|
+```
|
|
|
+
|
|
|
+### 6.3 级联删除
|
|
|
+
|
|
|
+```go
|
|
|
+// 删除部门时,同时删除该部门所有员工
|
|
|
+deptToDelete := "临时工"
|
|
|
+emps, _ := empTbl.Where("Dept", "=", deptToDelete).Find()
|
|
|
+for _, emp := range emps {
|
|
|
+ empTbl.Delete(emp.ID)
|
|
|
+}
|
|
|
+fmt.Printf("已删除 %d 名 %s 员工\n", len(emps), deptToDelete)
|
|
|
+```
|
|
|
+
|
|
|
+---
|
|
|
+
|
|
|
+## 7. 条件查询详解
|
|
|
+
|
|
|
+### 7.1 比较运算符
|
|
|
+
|
|
|
+```go
|
|
|
+// 等于 (=)
|
|
|
+rows, _ := empTbl.Where("Age", "=", int64(25)).Find()
|
|
|
+
|
|
|
+// 不等于 (!=)
|
|
|
+rows, _ = empTbl.Where("Status", "!=", int64(0)).Find()
|
|
|
+
|
|
|
+// 大于 (>)
|
|
|
+rows, _ = empTbl.Where("Score", ">", int64(90)).Find()
|
|
|
+
|
|
|
+// 大于等于 (>=)
|
|
|
+rows, _ = empTbl.Where("Age", ">=", int64(30)).Find()
|
|
|
+
|
|
|
+// 小于 (<)
|
|
|
+rows, _ = empTbl.Where("Score", "<", int64(60)).Find()
|
|
|
+
|
|
|
+// 小于等于 (<=)
|
|
|
+rows, _ = empTbl.Where("Age", "<=", int64(25)).Find()
|
|
|
+```
|
|
|
+
|
|
|
+### 7.2 范围查询
|
|
|
+
|
|
|
+```go
|
|
|
+// WHERE Age >= 20 AND Age <= 30
|
|
|
+rows, _ := empTbl.
|
|
|
+ Where("Age", ">=", int64(20)).
|
|
|
+ Where("Age", "<=", int64(30)).
|
|
|
+ Find()
|
|
|
+fmt.Printf("20~30岁员工: %d 人\n", len(rows))
|
|
|
+```
|
|
|
+
|
|
|
+### 7.3 IN 查询
|
|
|
+
|
|
|
+```go
|
|
|
+// WHERE Dept IN ("工程部", "技术部")
|
|
|
+rows, _ := empTbl.Where("Dept", "in", []string{"工程部", "技术部"}).Find()
|
|
|
+fmt.Printf("工程/技术部员工: %d 人\n", len(rows))
|
|
|
+```
|
|
|
+
|
|
|
+### 7.4 LIKE 模糊查询
|
|
|
+
|
|
|
+```go
|
|
|
+// WHERE Name LIKE "张%"
|
|
|
+rows, _ := empTbl.Where("Name", "like", "张%").Find()
|
|
|
+fmt.Printf("姓张的员工: %d 人\n", len(rows))
|
|
|
+
|
|
|
+// WHERE Name LIKE "%三"
|
|
|
+rows, _ = empTbl.Where("Name", "like", "%三").Find()
|
|
|
+fmt.Printf("名字以'三'结尾的员工: %d 人\n", len(rows))
|
|
|
+```
|
|
|
+
|
|
|
+### 7.5 多条件组合
|
|
|
+
|
|
|
+```go
|
|
|
+// WHERE Age > 25 AND Score >= 80 AND Dept = "工程部"
|
|
|
+rows, _ := empTbl.
|
|
|
+ Where("Age", ">", int64(25)).
|
|
|
+ Where("Score", ">=", int64(80)).
|
|
|
+ Where("Dept", "=", "工程部").
|
|
|
+ Find()
|
|
|
+fmt.Printf("符合条件的员工: %d 人\n", len(rows))
|
|
|
+```
|
|
|
+
|
|
|
+### 7.6 OR 条件
|
|
|
+
|
|
|
+```go
|
|
|
+// 使用 OrCondition
|
|
|
+import "TsDB2/DB/core"
|
|
|
+
|
|
|
+cond := core.OrCondition{
|
|
|
+ core.EqCondition{Field: "Dept", Value: "工程部"},
|
|
|
+ core.EqCondition{Field: "Dept", Value: "技术部"},
|
|
|
+}
|
|
|
+rows, _ := empTbl.WhereCondition(cond).Find()
|
|
|
+```
|
|
|
+
|
|
|
+---
|
|
|
+
|
|
|
+## 8. 排序与分页
|
|
|
+
|
|
|
+### 8.1 升序排序
|
|
|
+
|
|
|
+```go
|
|
|
+// ORDER BY Age ASC
|
|
|
+rows, _ := empTbl.OrderBy("Age").Find()
|
|
|
+fmt.Println("按年龄升序:")
|
|
|
+for _, emp := range rows[:5] { // 只看前5条
|
|
|
+ fmt.Printf(" %s: %d岁\n", emp.Name, emp.Age)
|
|
|
+}
|
|
|
+```
|
|
|
+
|
|
|
+### 8.2 降序排序
|
|
|
+
|
|
|
+```go
|
|
|
+// ORDER BY Score DESC
|
|
|
+rows, _ := empTbl.OrderBy("Score", true).Find() // true = 降序
|
|
|
+fmt.Println("按分数降序(前5名):")
|
|
|
+for i, emp := range rows[:5] {
|
|
|
+ fmt.Printf(" [%d] %s: %d分\n", i+1, emp.Name, emp.Score)
|
|
|
+}
|
|
|
+```
|
|
|
+
|
|
|
+### 8.3 LIMIT 限制数量
|
|
|
+
|
|
|
+```go
|
|
|
+// ORDER BY Age ASC LIMIT 10
|
|
|
+rows, _ := empTbl.OrderBy("Age").Limit(10).Find()
|
|
|
+fmt.Printf("最年轻的 10 名员工:\n")
|
|
|
+for _, emp := range rows {
|
|
|
+ fmt.Printf(" %s: %d岁\n", emp.Name, emp.Age)
|
|
|
+}
|
|
|
+```
|
|
|
+
|
|
|
+### 8.4 OFFSET + LIMIT 分页
|
|
|
+
|
|
|
+```go
|
|
|
+// 每页 20 条
|
|
|
+pageSize := 20
|
|
|
+
|
|
|
+// 第1页
|
|
|
+page1, _ := empTbl.OrderBy("Age").Offset(0).Limit(pageSize).Find()
|
|
|
+fmt.Printf("第1页: %d 条\n", len(page1))
|
|
|
+
|
|
|
+// 第2页
|
|
|
+page2, _ := empTbl.OrderBy("Age").Offset(pageSize).Limit(pageSize).Find()
|
|
|
+fmt.Printf("第2页: %d 条\n", len(page2))
|
|
|
+
|
|
|
+// 第3页
|
|
|
+page3, _ := empTbl.OrderBy("Age").Offset(pageSize*2).Limit(pageSize).Find()
|
|
|
+fmt.Printf("第3页: %d 条\n", len(page3))
|
|
|
+```
|
|
|
+
|
|
|
+### 8.5 完整分页示例
|
|
|
+
|
|
|
+```go
|
|
|
+func printPage(tbl *DB.Table[Employee], pageNum, pageSize int) {
|
|
|
+ offset := (pageNum - 1) * pageSize
|
|
|
+ rows, _ := tbl.OrderBy("Age").Offset(offset).Limit(pageSize).Find()
|
|
|
+
|
|
|
+ fmt.Printf("\n=== 第 %d 页 (共 %d 条) ===\n", pageNum, len(rows))
|
|
|
+ for _, emp := range rows {
|
|
|
+ fmt.Printf(" %-15s %2d岁 %3d分 %s\n",
|
|
|
+ emp.Name, emp.Age, emp.Score, emp.Dept)
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+// 打印前3页
|
|
|
+for page := 1; page <= 3; page++ {
|
|
|
+ printPage(empTbl, page, 20)
|
|
|
+}
|
|
|
+```
|
|
|
+
|
|
|
+---
|
|
|
+
|
|
|
+## 9. 聚合查询
|
|
|
+
|
|
|
+### 9.1 COUNT 计数
|
|
|
+
|
|
|
+```go
|
|
|
+// SELECT COUNT(*) FROM employee
|
|
|
+total, err := empTbl.Count()
|
|
|
+if err != nil {
|
|
|
+ panic(err)
|
|
|
+}
|
|
|
+fmt.Printf("员工总数: %d\n", total)
|
|
|
+
|
|
|
+// 带条件的 COUNT
|
|
|
+youngCount, _ := empTbl.Where("Age", "<", int64(25)).Count()
|
|
|
+fmt.Printf("25岁以下员工: %d 人\n", youngCount)
|
|
|
+```
|
|
|
+
|
|
|
+### 9.2 MIN/MAX(通过排序实现)
|
|
|
+
|
|
|
+```go
|
|
|
+// 最小年龄
|
|
|
+minAgeRows, _ := empTbl.OrderBy("Age").Limit(1).Find()
|
|
|
+if len(minAgeRows) > 0 {
|
|
|
+ fmt.Printf("最小年龄: %d岁 (%s)\n", minAgeRows[0].Age, minAgeRows[0].Name)
|
|
|
+}
|
|
|
+
|
|
|
+// 最大分数
|
|
|
+maxScoreRows, _ := empTbl.OrderBy("Score", true).Limit(1).Find()
|
|
|
+if len(maxScoreRows) > 0 {
|
|
|
+ fmt.Printf("最高分数: %d分 (%s)\n", maxScoreRows[0].Score, maxScoreRows[0].Name)
|
|
|
+}
|
|
|
+```
|
|
|
+
|
|
|
+### 9.3 SUM/AVG(循环计算)
|
|
|
+
|
|
|
+```go
|
|
|
+// 计算平均分数
|
|
|
+allEmps, _ := empTbl.Find()
|
|
|
+var totalScore int64
|
|
|
+for _, emp := range allEmps {
|
|
|
+ totalScore += emp.Score
|
|
|
+}
|
|
|
+avgScore := float64(totalScore) / float64(len(allEmps))
|
|
|
+fmt.Printf("平均分数: %.2f\n", avgScore)
|
|
|
+
|
|
|
+// 各部门平均分
|
|
|
+deptMap := make(map[string][]int64)
|
|
|
+for _, emp := range allEmps {
|
|
|
+ deptMap[emp.Dept] = append(deptMap[emp.Dept], emp.Score)
|
|
|
+}
|
|
|
+for dept, scores := range deptMap {
|
|
|
+ var sum int64
|
|
|
+ for _, s := range scores {
|
|
|
+ sum += s
|
|
|
+ }
|
|
|
+ avg := float64(sum) / float64(len(scores))
|
|
|
+ fmt.Printf(" %s: 平均 %.2f 分 (%d 人)\n", dept, avg, len(scores))
|
|
|
+}
|
|
|
+```
|
|
|
+
|
|
|
+---
|
|
|
+
|
|
|
+## 10. 多表关联
|
|
|
+
|
|
|
+### 10.1 简单关联查询
|
|
|
+
|
|
|
+```go
|
|
|
+// 查询某员工的所有订单
|
|
|
+employeeID := int64(1)
|
|
|
+orders, _ := orderTbl.Where("EmployeeID", "=", employeeID).Find()
|
|
|
+fmt.Printf("员工 %d 的订单数: %d\n", employeeID, len(orders))
|
|
|
+for _, order := range orders {
|
|
|
+ fmt.Printf(" 订单ID=%d, 金额=%d, 状态=%d\n", order.ID, order.Amount, order.Status)
|
|
|
+}
|
|
|
+```
|
|
|
+
|
|
|
+### 10.2 嵌套循环关联
|
|
|
+
|
|
|
+```go
|
|
|
+// 查询每个员工的订单总数
|
|
|
+emps, _ := empTbl.Find()
|
|
|
+for _, emp := range emps[:10] { // 只看前10名员工
|
|
|
+ orders, _ := orderTbl.Where("EmployeeID", "=", int64(emp.ID)).Find()
|
|
|
+ fmt.Printf("%-15s: %d 个订单\n", emp.Name, len(orders))
|
|
|
+}
|
|
|
+```
|
|
|
+
|
|
|
+### 10.3 复杂关联 + 条件
|
|
|
+
|
|
|
+```go
|
|
|
+// 查询工程部员工的状态为"已完成"的订单
|
|
|
+deptEmps, _ := empTbl.Where("Dept", "=", "工程部").Find()
|
|
|
+var totalAmount int64
|
|
|
+var orderCount int
|
|
|
+
|
|
|
+for _, emp := range deptEmps {
|
|
|
+ orders, _ := orderTbl.
|
|
|
+ Where("EmployeeID", "=", int64(emp.ID)).
|
|
|
+ Where("Status", "=", int64(1)). // 1 = done
|
|
|
+ Find()
|
|
|
+
|
|
|
+ for _, order := range orders {
|
|
|
+ totalAmount += order.Amount
|
|
|
+ orderCount++
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+fmt.Printf("工程部已完成订单: %d 笔, 总金额: %d\n", orderCount, totalAmount)
|
|
|
+```
|
|
|
+
|
|
|
+---
|
|
|
+
|
|
|
+## 11. 事务与提交
|
|
|
+
|
|
|
+### 11.1 手动 Commit
|
|
|
+
|
|
|
+```go
|
|
|
+// 禁用自动提交
|
|
|
+db.SetAutoCommitDelay(0)
|
|
|
+
|
|
|
+// 执行多个操作
|
|
|
+empTbl.Insert(&Employee{Name: "A", Age: 25, Score: 90, Dept: "IT"})
|
|
|
+empTbl.Insert(&Employee{Name: "B", Age: 30, Score: 85, Dept: "HR"})
|
|
|
+empTbl.Delete(5)
|
|
|
+
|
|
|
+// 一次性提交
|
|
|
+if err := db.Commit(); err != nil {
|
|
|
+ fmt.Printf("提交失败: %v\n", err)
|
|
|
+ // 可以回滚或重试
|
|
|
+} else {
|
|
|
+ fmt.Println("✅ 事务提交成功")
|
|
|
+}
|
|
|
+```
|
|
|
+
|
|
|
+### 11.2 防抖自动提交
|
|
|
+
|
|
|
+```go
|
|
|
+// 设置 500ms 防抖
|
|
|
+db.SetAutoCommitDelay(500 * time.Millisecond)
|
|
|
+
|
|
|
+// 连续插入,最后一次操作后 500ms 自动 Commit
|
|
|
+for i := 1; i <= 100; i++ {
|
|
|
+ empTbl.Insert(&Employee{...})
|
|
|
+}
|
|
|
+
|
|
|
+// 等待自动提交
|
|
|
+time.Sleep(1 * time.Second)
|
|
|
+fmt.Println("数据已自动提交")
|
|
|
+```
|
|
|
+
|
|
|
+### 11.3 脏页阈值自动提交
|
|
|
+
|
|
|
+```go
|
|
|
+// 当脏页超过 2000 页时自动触发 Commit
|
|
|
+db.SetDirtyThreshold(2000)
|
|
|
+
|
|
|
+// 大量插入时会自动分批 Commit
|
|
|
+for i := 1; i <= 100000; i++ {
|
|
|
+ empTbl.Insert(&Employee{...})
|
|
|
+}
|
|
|
+// 无需手动 Commit,系统会根据脏页阈值自动提交
|
|
|
+```
|
|
|
+
|
|
|
+### 11.4 Close 兜底提交
|
|
|
+
|
|
|
+```go
|
|
|
+// 即使忘记 Commit,Close 时也会自动提交
|
|
|
+db, _ := DB.Open("test.db")
|
|
|
+tbl, _ := DB.UseTable[Employee](db, "test")
|
|
|
+
|
|
|
+for i := 1; i <= 1000; i++ {
|
|
|
+ tbl.Insert(&Employee{...})
|
|
|
+}
|
|
|
+
|
|
|
+// 没有手动 Commit
|
|
|
+db.Close() // ✅ Close 内部会调用 Commit,确保数据持久化
|
|
|
+
|
|
|
+// 重新打开验证
|
|
|
+db2, _ := DB.Open("test.db")
|
|
|
+tbl2, _ := DB.UseTable[Employee](db2, "test")
|
|
|
+count, _ := tbl2.Count()
|
|
|
+fmt.Printf("Close 后数据保留: %d 条\n", count) // 输出: 1000 条
|
|
|
+```
|
|
|
+
|
|
|
+---
|
|
|
+
|
|
|
+## 12. 高级特性
|
|
|
+
|
|
|
+### 12.1 Schema 演进(添加字段)
|
|
|
+
|
|
|
+```go
|
|
|
+// V1: 初始版本
|
|
|
+type EmployeeV1 struct {
|
|
|
+ DB.BaseModel
|
|
|
+ Name string `tsdb:"32byte"`
|
|
|
+ Age int64 `tsdb:"8byte"`
|
|
|
+}
|
|
|
+
|
|
|
+// V2: 添加新字段
|
|
|
+type EmployeeV2 struct {
|
|
|
+ DB.BaseModel
|
|
|
+ Name string `tsdb:"32byte"`
|
|
|
+ Age int64 `tsdb:"8byte"`
|
|
|
+ Email string `tsdb:"64byte"` // 新增字段
|
|
|
+}
|
|
|
+
|
|
|
+// TsDB2 自动处理 schema 版本迁移
|
|
|
+tbl, _ := DB.UseTable[EmployeeV2](db, "employee")
|
|
|
+```
|
|
|
+
|
|
|
+### 12.2 删除字段(软删除)
|
|
|
+
|
|
|
+```go
|
|
|
+// 标记字段为 dropped(不物理删除,保持兼容性)
|
|
|
+empCore.DropField("Score") // Score 字段标记为已删除
|
|
|
+
|
|
|
+// 查询时不再返回该字段
|
|
|
+emp, _ := tbl.Find(1)
|
|
|
+// emp.Score 将为默认值 0
|
|
|
+```
|
|
|
+
|
|
|
+### 12.3 删除表
|
|
|
+
|
|
|
+```go
|
|
|
+// 删除整个表(回收所有页)
|
|
|
+err := db.DropTable("order")
|
|
|
+if err != nil {
|
|
|
+ fmt.Printf("删除表失败: %v\n", err)
|
|
|
+}
|
|
|
+
|
|
|
+// 验证删除
|
|
|
+_, exists := db.CoreDB().FindTable("order")
|
|
|
+if !exists {
|
|
|
+ fmt.Println("✅ order 表已删除")
|
|
|
+}
|
|
|
+```
|
|
|
+
|
|
|
+### 12.4 索引管理
|
|
|
+
|
|
|
+```go
|
|
|
+// 查看所有索引
|
|
|
+table, _ := db.CoreDB().FindTable("employee")
|
|
|
+for _, idx := range table.GetIndexes() {
|
|
|
+ fmt.Printf("索引: %s, 类型: %d, 根页: %d\n",
|
|
|
+ idx.Name, idx.Type, idx.EntryPage)
|
|
|
+}
|
|
|
+
|
|
|
+// 删除索引
|
|
|
+table.DropIndex("idx_age")
|
|
|
+```
|
|
|
+
|
|
|
+### 12.5 性能监控
|
|
|
+
|
|
|
+```go
|
|
|
+// 统计信息
|
|
|
+fi, _ := os.Stat("mydb.db")
|
|
|
+fmt.Printf("数据库大小: %.2f MB\n", float64(fi.Size())/1024/1024)
|
|
|
+
|
|
|
+// 表统计
|
|
|
+count, _ := empTbl.Count()
|
|
|
+fmt.Printf("员工表行数: %d\n", count)
|
|
|
+
|
|
|
+// BufferPool 统计
|
|
|
+bp := db.CoreDB().GetBufferPool()
|
|
|
+fmt.Printf("缓存页数: %d\n", bp.Len())
|
|
|
+fmt.Printf("脏页数: %d\n", bp.DirtyCount())
|
|
|
+```
|
|
|
+
|
|
|
+### 12.6 并发安全
|
|
|
+
|
|
|
+```go
|
|
|
+// TsDB2 不是线程安全的,需要在应用层加锁
|
|
|
+var mu sync.Mutex
|
|
|
+
|
|
|
+go func() {
|
|
|
+ mu.Lock()
|
|
|
+ defer mu.Unlock()
|
|
|
+ empTbl.Insert(&Employee{...})
|
|
|
+}()
|
|
|
+
|
|
|
+go func() {
|
|
|
+ mu.Lock()
|
|
|
+ defer mu.Unlock()
|
|
|
+ empTbl.Find(1)
|
|
|
+}()
|
|
|
+```
|
|
|
+
|
|
|
+---
|
|
|
+
|
|
|
+## 🎯 完整示例:员工管理系统
|
|
|
+
|
|
|
+```go
|
|
|
+package main
|
|
|
+
|
|
|
+import (
|
|
|
+ "TsDB2/DB"
|
|
|
+ "fmt"
|
|
|
+ "time"
|
|
|
+)
|
|
|
+
|
|
|
+type Employee struct {
|
|
|
+ DB.BaseModel
|
|
|
+ Name string `tsdb:"32byte"`
|
|
|
+ Age int64 `tsdb:"8byte"`
|
|
|
+ Score int64 `tsdb:"8byte"`
|
|
|
+ Dept string `tsdb:"16byte"`
|
|
|
+}
|
|
|
+
|
|
|
+func main() {
|
|
|
+ // 1. 打开数据库
|
|
|
+ db, _ := DB.Open("company.db")
|
|
|
+ defer db.Close()
|
|
|
+ db.SetAutoCommitDelay(0) // 手动控制
|
|
|
+
|
|
|
+ // 2. 建表 + 索引(自动推导表名)
|
|
|
+ tbl, _ := DB.UseTable[Employee](db) // ✅ 无需传递 "employee"
|
|
|
+ core, _ := db.CoreDB().FindTable("employee")
|
|
|
+ core.CreateIndex("idx_age", page.IndexTypeBTree, []byte{2})
|
|
|
+ core.CreateIndex("idx_dept", page.IndexTypeBTree, []byte{4})
|
|
|
+
|
|
|
+ // 3. 批量插入
|
|
|
+ fmt.Println("插入数据...")
|
|
|
+ depts := []string{"工程部", "销售部", "人事部", "财务部", "运维部"}
|
|
|
+ for i := 1; i <= 1000; i++ {
|
|
|
+ tbl.Insert(&Employee{
|
|
|
+ Name: fmt.Sprintf("Emp%04d", i),
|
|
|
+ Age: int64(20 + (i % 50)),
|
|
|
+ Score: int64(50 + (i % 51)),
|
|
|
+ Dept: depts[i%5],
|
|
|
+ })
|
|
|
+ }
|
|
|
+ db.Commit()
|
|
|
+
|
|
|
+ // 4. 查询
|
|
|
+ fmt.Println("\n=== 查询示例 ===")
|
|
|
+
|
|
|
+ // 4.1 主键查询
|
|
|
+ emp, _ := tbl.Find(1)
|
|
|
+ fmt.Printf("ID=1: %s, %d岁\n", emp.Name, emp.Age)
|
|
|
+
|
|
|
+ // 4.2 条件查询
|
|
|
+ young, _ := tbl.Where("Age", "<", int64(25)).Find()
|
|
|
+ fmt.Printf("25岁以下: %d 人\n", len(young))
|
|
|
+
|
|
|
+ // 4.3 排序 + 分页
|
|
|
+ top10, _ := tbl.OrderBy("Score", true).Limit(10).Find()
|
|
|
+ fmt.Println("分数Top 10:")
|
|
|
+ for i, e := range top10 {
|
|
|
+ fmt.Printf(" [%d] %s: %d分\n", i+1, e.Name, e.Score)
|
|
|
+ }
|
|
|
+
|
|
|
+ // 4.4 聚合
|
|
|
+ total, _ := tbl.Count()
|
|
|
+ fmt.Printf("总人数: %d\n", total)
|
|
|
+
|
|
|
+ // 5. 更新
|
|
|
+ fmt.Println("\n=== 更新示例 ===")
|
|
|
+ tbl.Update(&Employee{
|
|
|
+ BaseModel: DB.BaseModel{ID: 1},
|
|
|
+ Score: 100,
|
|
|
+ })
|
|
|
+ emp, _ = tbl.Find(1)
|
|
|
+ fmt.Printf("ID=1 更新后分数: %d\n", emp.Score)
|
|
|
+
|
|
|
+ // 6. 删除
|
|
|
+ fmt.Println("\n=== 删除示例 ===")
|
|
|
+ tbl.Delete(2)
|
|
|
+ _, err := tbl.Find(2)
|
|
|
+ if err != nil {
|
|
|
+ fmt.Println("ID=2 已删除")
|
|
|
+ }
|
|
|
+
|
|
|
+ // 7. 复杂查询
|
|
|
+ fmt.Println("\n=== 复杂查询 ===")
|
|
|
+ itHighScore, _ := tbl.
|
|
|
+ Where("Dept", "=", "工程部").
|
|
|
+ Where("Score", ">", int64(80)).
|
|
|
+ Find()
|
|
|
+ fmt.Printf("工程部高分员工: %d 人\n", len(itHighScore))
|
|
|
+
|
|
|
+ fmt.Println("\n✅ 所有操作完成!")
|
|
|
+}
|
|
|
+```
|
|
|
+
|
|
|
+---
|
|
|
+
|
|
|
+## 📚 参考资料
|
|
|
+
|
|
|
+- **Analyzer 工具**: 可视化查看数据库内部结构
|
|
|
+- **full_test**: 完整功能测试用例
|
|
|
+- **no_commit_test**: 事务与自动提交流程测试
|
|
|
+
|
|
|
+---
|
|
|
+
|
|
|
+**祝使用愉快!** 🎉
|