当前位置: 首页 > news >正文

gorm sqlexpression如何公用

我来为你介绍 GORM 中 SQL Expression 的公用方式,让你能复用复杂的 SQL 表达式逻辑。
核心方式:使用 gorm.Expr + 自定义函数
GORM 的 Expr 方法允许你构建可复用的 SQL 表达式。
1. 基础封装:通用表达式函数
package expr

import (
"fmt"
"gorm.io/gorm"
)

// 通用表达式构建器
type SQLExpr struct {
SQL string
Vars []interface{}
}

func (e SQLExpr) Expression() string {
return e.SQL
}

// ========== 可复用的表达式函数 ==========

// DateEqual 日期相等(处理时区/格式问题)
func DateEqual(column string, date string) *gorm.SqlExpr {
return gorm.Expr("DATE(?) = ?", gorm.Expr(column), date)
}

// JSONExtract JSON字段提取
func JSONExtract(column, path string) *gorm.SqlExpr {
return gorm.Expr("JSON_UNQUOTE(JSON_EXTRACT(?, ?))", gorm.Expr(column), path)
}

// Coalesce 空值处理
func Coalesce(columns ...string) *gorm.SqlExpr {
if len(columns) == 0 {
return nil
}
sql := "COALESCE(" + columns[0]
for i := 1; i < len(columns); i++ {
sql += ", " + columns[i]
}
sql += ")"
return gorm.Expr(sql)
}

// IfNull NULL值替换
func IfNull(column string, defaultVal interface{}) *gorm.SqlExpr {
return gorm.Expr("IFNULL(?, ?)", gorm.Expr(column), defaultVal)
}

// Round 四舍五入
func Round(column string, decimals int) *gorm.SqlExpr {
return gorm.Expr("ROUND(?, ?)", gorm.Expr(column), decimals)
}

// Concat 字符串拼接
func Concat(columns ...string) *gorm.SqlExpr {
sql := "CONCAT("
for i, col := range columns {
if i > 0 {
sql += ", "
}
sql += "?"
}
sql += ")"

exprs := make([]interface{}, len(columns))
for i, col := range columns {
exprs[i] = gorm.Expr(col)
}
return gorm.Expr(sql, exprs...)
}

// AgeFromBirthday 根据生日计算年龄
func AgeFromBirthday(birthdayCol string) *gorm.SqlExpr {
return gorm.Expr(
"TIMESTAMPDIFF(YEAR, ?, CURDATE())",
gorm.Expr(birthdayCol),
)
}

// FullTextSearch 全文搜索(MySQL)
func FullTextSearch(columns []string, keyword string) *gorm.SqlExpr {
cols := ""
for i, c := range columns {
if i > 0 {
cols += ", "
}
cols += c
}
return gorm.Expr(
fmt.Sprintf("MATCH(%s) AGAINST (? IN BOOLEAN MODE)", cols),
keyword,
)
}

2. 使用方式
package main

import (
"expr" // 上面定义的包
"gorm.io/gorm"
)

type User struct {
ID uint
Name string
Birthday *time.Time
Profile string `gorm:"type:json"`
Score float64
BackupName string
}

// ========== 在查询中复用 ==========

// 查询:计算年龄
var users []struct {
User
Age int
}
db.Model(&User{}).
Select("*, ?", expr.AgeFromBirthday("birthday")).
Scan(&users)

// 查询:JSON提取 + 空值处理
db.Model(&User{}).
Where("? = ?", expr.JSONExtract("profile", "$.city"), "北京").
Find(&users)

// 查询:使用Coalesce
db.Model(&User{}).
Select("id, ? as display_name", expr.Coalesce("name", "backup_name", "'未知'")).
Find(&users)

// 查询:分数四舍五入
db.Model(&User{}).
Select("id, ? as rounded_score", expr.Round("score", 2)).
Find(&users)

// 查询:全文搜索
db.Model(&User{}).
Where("? > 0", expr.FullTextSearch([]string{"name", "profile"}, "gorm")).
Find(&users)

3. 高级封装:Scope 模式(最推荐)
使用 GORM 的 Scopes 实现链式复用:
package scopes

import (
"gorm.io/gorm"
"yourapp/expr"
)

// 可复用的 Scope 函数

// WithAge 添加年龄计算字段
func WithAge() func(db *gorm.DB) *gorm.DB {
return func(db *gorm.DB) *gorm.DB {
return db.Select("*, ? as age", expr.AgeFromBirthday("birthday"))
}
}

// WithDisplayName 添加显示名称(处理空值)
func WithDisplayName() func(db *gorm.DB) *gorm.DB {
return func(db *gorm.DB) *gorm.DB {
return db.Select(
"*, ? as display_name",
expr.Coalesce("name", "backup_name"),
)
}
}

// WhereCity 按城市筛选(JSON字段)
func WhereCity(city string) func(db *gorm.DB) *gorm.DB {
return func(db *gorm.DB) *gorm.DB {
return db.Where("? = ?", expr.JSONExtract("profile", "$.city"), city)
}
}

// WhereActive 筛选活跃用户(综合条件)
func WhereActive() func(db *gorm.DB) *gorm.DB {
return func(db *gorm.DB) *gorm.DB {
return db.Where("last_login_at > DATE_SUB(NOW(), INTERVAL 30 DAY)")
}
}

// OrderByScore 按分数排序(处理NULL)
func OrderByScore(desc bool) func(db *gorm.DB) *gorm.DB {
return func(db *gorm.DB) *gorm.DB {
order := "ASC"
if desc {
order = "DESC"
}
return db.Order(gorm.Expr("? "+order, expr.Coalesce("score", "0")))
}
}

使用 Scope:
// 链式组合,高度复用
db.Model(&User{}).
Scopes(
scopes.WithAge(),
scopes.WithDisplayName(),
scopes.WhereCity("北京"),
scopes.WhereActive(),
scopes.OrderByScore(true),
).
Find(&users)

// 也可以单独使用
db.Model(&User{}).Scopes(scopes.WithAge()).Find(&users)

4. 复杂表达式:子查询封装
package expr

// SubQueryExpr 子查询表达式
type SubQueryExpr struct {
DB *gorm.DB
Alias string
}

func (s SubQueryExpr) Expression() string {
return fmt.Sprintf("(%s) as %s", s.DB.ToSQL(func(db *gorm.DB) *gorm.DB {
return db
}), s.Alias)
}

// AvgScoreByGroup 分组平均分子查询
func AvgScoreByGroup(groupCol string) *gorm.DB {
return db.Model(&User{}).
Select(groupCol, "AVG(score) as avg_score").
Group(groupCol)
}

// RankByScore 分数排名子查询
func RankByScore() *gorm.DB {
return db.Model(&User{}).
Select("id", "RANK() OVER (ORDER BY score DESC) as rank")
}

5. 完整实战示例
package main

import (
"gorm.io/gorm"
"yourapp/expr"
"yourapp/scopes"
)

// 业务场景:获取用户列表,包含计算字段,支持筛选和排序
func GetUserList(ctx context.Context, req UserListReq) ([]UserDTO, error) {
var result []UserDTO

query := db.Model(&User{}).
Scopes(
scopes.WithAge(), // 复用:计算年龄
scopes.WithDisplayName(), // 复用:显示名称
)

// 动态条件
if req.City != "" {
query = query.Scopes(scopes.WhereCity(req.City))
}
if req.ActiveOnly {
query = query.Scopes(scopes.WhereActive())
}

// 排序
switch req.SortBy {
case "score":
query = query.Scopes(scopes.OrderByScore(req.Desc))
case "age":
query = query.Order(expr.AgeFromBirthday("birthday"))
}

err := query.Find(&result).Error
return result, err
}

总结对比
方式 适用场景 复用级别
expr 函数包 简单SQL表达式 表达式级
Scopes 模式 完整的查询条件/字段 查询片段级
子查询封装 复杂统计/排名 查询块级
推荐实践:将常用的表达式抽到 expr 包,将完整的查询逻辑抽到 scopes 包,这样可以在不同业务模块中灵活组合复用。

http://www.jsqmd.com/news/863649/

相关文章:

  • 2026清流县黄金回收白银回收铂金回收店铺实力排行榜TOP5;K金+金条+银条+首饰回收靠谱门店及联系方式推荐 - 前途无量YY
  • Sobelow进阶使用:多格式输出与集成开发环境配置
  • Lavalink高级过滤器使用教程:音效处理与实时音频增强终极指南
  • 智能批改工具的效能比较与专业引领——基于“题小小”与通用大模型的作文教学实证分析 - 博客万
  • 洗发水十大品牌排行榜:以蓬松效果闻名的洗发水品牌 - 速递信息
  • E-Hentai Downloader:3分钟掌握漫画批量下载的终极免费方案
  • Topit:Mac窗口置顶工具终极指南 - 三步告别繁琐窗口切换
  • LangChain-Chatchat 开发与应用(七) 自定义文档解析器-搞定那些不听话的PDF
  • 2026年普通家庭选白色十字门冰箱,大白405成首选之选! - 品牌企业推荐师(官方)
  • 3步掌握抖音批量下载:免费高效的抖音下载器完整指南
  • 五寨县黄金回收哪家强?铭润稳居第一 - 亦辰小黄鸭
  • 在不同网络环境下体验taotoken api服务的连接成功率
  • 余生黄金回收金凤区全攻略|6街道2镇全覆盖 黄金回收哪家更靠谱 - 润富黄金珠宝行
  • 0x.Tools高级技巧:如何利用动态查询系统进行多维性能分析
  • 蘑菇博客持续集成实践:Github Actions自动化部署完整流程
  • 武宁县黄金回收哪家强?铭润稳居第一 - 亦辰小黄鸭
  • 可观测体系最佳实践:Prometheus+Grafana+Loki+Jaeger
  • 如何高效下载抖音无水印视频:douyin-downloader的5个核心技巧
  • 2026年天津黄金回收平台,福昌夏凭实力脱颖而出 - 黄金上门回收
  • QKeyMapper:Windows跨设备按键映射解决方案,提升游戏与办公操作效率
  • CANN asc-devkit int4x2转half函数
  • 终极指南:如何用Python脚本免费实现百度网盘高速下载
  • 从传统推荐系统到AI原生架构:Gorse推荐引擎的技术演进与架构哲学
  • 08-前后端分离改造-把Chatchat嵌入你的业务系统
  • 武平县黄金回收哪家强?铭润稳居第一 - 亦辰小黄鸭
  • 泗洪县黄金回收哪家强?铭润稳居第一 - 亦辰小黄鸭
  • 镇江本地黄金回收六家老店服务周到诚信经营值得信赖 六大品牌 优选长悦 - 专业黄金回收
  • 第九篇:《软件测试中的常见误区与事实》
  • 泗水县黄金回收哪家强?铭润稳居第一 - 亦辰小黄鸭
  • 2026洗发水排行榜:不同发质都爱的5款修护洗发水 - 速递信息