文章目录
前言
- 对比GROM多表关联和原生Sql多表关联
一、GROM多表关联
- 适用于返回全部数据
- 需要逻辑外键(不会在数据库创建任何约束)
- 适合三个表以下的关联
- 有几张表就会查询几次
go
type Product struct {
gorm.Model // 包含ID, CreatedAt, UpdatedAt, DeletedAt
CateID uint `gorm:"not null;index:idx_cate_id" json:"cate_id"` // 普通索引
TagID string `gorm:"size:255;not null;index:idx_tag_id" json:"tag_id"` // 多标签字符串存储,如"1,2,3"
Name string `gorm:"size:255;not null;uniqueIndex:idx_name" json:"name"` // 唯一索引
Sku string `gorm:"size:50;not null;uniqueIndex:idx_sku" json:"sku"` // 唯一索引
Price float64 `gorm:"type:decimal(10,2);not null;default:0.00" json:"price"`
Stock int `gorm:"not null;default:0" json:"stock"`
StockWarning int `gorm:"not null;default:0" json:"stock_warning"`
CostPrice float64 `gorm:"type:decimal(10,2);not null;default:0.00;" json:"cost_price"`
Status int8 `gorm:"type:tinyint;not null;default:1;index:idx_status" json:"status"` // 普通索引
Description *string `gorm:"type:text" json:"description"`
ImageUrl *string `gorm:"size:255" json:"image_url"`
Unit string `gorm:"size:255;not null" json:"unit"`
// 🔗 关联关系:多对一 (多个商品属于一个分类)
// 🔗 关联关系:多对一 (多个商品属于一个分类)
// 逻辑外键 不会同步到数据库中,仅在此模型或项目内使用
Category OrderCate `gorm:"foreignKey:CateID" json:"category"`
}
//GROM示例
// 查询商品及其分类信息(GORM联查)
func GetProduvtWithCategroy(id uint) (*Product , error){
var product Product
// 步骤1. First(&product) 先根据id查询商品表,获取到producr.CateID
// 步骤2. Preload("Category"),再根据获取到的分类id查询分类表,获取到分类信息后返回到Category字段
// 合并结果,将分类信息填充到 product.Category字段
result := DB.Preload("Category").Where("id = ?" , id).First(&product)
return &product, result.Error
}
二、原生Sql多表关联
- 适用于三个表以上关联
- 只查询一次
- 适用于返回精准字段
go
// 商品列表结果结构体(用于API返回)
type ProductListResult struct {
ID uint `json:"id"`
Name string `json:"name"`
Price float64 `json:"price"`
Stock int `json:"stock"`
Status int8 `json:"status"`
CateID uint `json:"cate_id"`
CategoryName string `json:"category_name"`
ImageUrl *string `json:"image_url"`
}
// 一方面减少查询次数,一方面精准控制返回字段
// 步骤1. FROM products p; 查询商品表 p为表代号
// 步骤2. LEFT JOIN products_cate c ON p.cate_id = c.id;左连接分类表(根据cate_id关联分类表主键id,左连接保证即使分类表中没有对应记录,商品信息也能显示)
// 步骤3. WHERE p.deleted_at IS NULL 过滤已删除的商品
// 步骤4. ORDER BY p.created_at DESC 按创建时间倒序排序
// 步骤5. SELECT 查询指定字段, COALESCE:判断字段是否为空,如果为空则返回默认值
// 步骤6. LIMIT 10 OFFSET 0 分页查询,每页10条,从第0条开始
// 步骤7. Raw() 执行SQL语句,Scan() 将查询结果映射到results切片中
// 左连接: 左连接会保留左表的所有数据,即使右表中没有匹配的数据,也会保留左表的数据。
// 右连接: 右连接会保留右表所有的数据,即使左表中没有匹配的数据,也会保留右表数据。
// 内连接: 内连接只返回两个表中匹配的数据,如果一个表中没有匹配的数据,则不会返回结果。
func GetProductListWithCategory() ([]ProductListResult, error) {
var results []ProductListResult
sql := `
SELECT
p.id,
p.name,
p.price,
p.stock,
p.status,
p.cate_id,
COALESCE(c.name, '未分类') as category_name,
p.image_url
FROM products p
LEFT JOIN products_cate c ON p.cate_id = c.id
WHERE p.deleted_at IS NULL
ORDER BY p.created_at DESC
`
err := DB.Raw(sql).Scan(&results).Error
return results, err
}