如何在go-jet中构建CASE条件实现字段空值判断与日期比较
解决go-jet中CASE条件无法作为布尔表达式用于Where()的问题
问题背景
需要实现表中AffectedTo字段的判断逻辑:若该字段为null则返回true,若非null则与输入日期比较。使用go-jet的CASE条件时,无法直接得到布尔表达式用于Where();尝试用caseCondition.IS_NOT_NULL()获取布尔表达式,但因caseCondition本身非空,该语句始终返回true。
依赖库与原代码
package postgres import ( "context" "errors" "fmt" "time" "financing-offer/internal/core/entity" "financing-offer/internal/database" "financing-offer/internal/database/dbmodels/finoffer/public/enum" "financing-offer/internal/database/dbmodels/finoffer/public/model" "financing-offer/internal/database/dbmodels/finoffer/public/table" "github.com/go-jet/jet/v2/postgres" "github.com/go-jet/jet/v2/qrm" )
func (b *BlackListSymbolRepository) GetByAffectTime(ctx context.Context, symbolId int64, affectedFrom time.Time, affectedTo time.Time) ([]entity.BlacklistSymbol, error) { // Create a CASE condition caseCondition := postgres.CASE(). WHEN(table.BlacklistSymbol.AffectedTo.IS_NULL()).THEN(postgres.Bool(true)). ELSE(table.BlacklistSymbol.AffectedTo.GT(postgres.TimestampT(affectedFrom))) // Combine the conditions in the WHERE clause stm := table.BlacklistSymbol.SELECT(table.BlacklistSymbol.AllColumns). WHERE( table.BlacklistSymbol.SymbolID.EQ(postgres.Int64(symbolId)). AND(table.BlacklistSymbol.Status.EQ(enum.Blacklistsymbolstatus.Active)). AND(table.BlacklistSymbol.AffectedFrom.LT_EQ(postgres.TimestampT(affectedTo))). AND(caseCondition.IS_NOT_NULL()), // Use the CASE condition ) dest := make([]model.BlacklistSymbol, 0) if err := stm.QueryContext(ctx, b.getDbFunc(ctx), &dest); err != nil { if errors.Is(err, qrm.ErrNoRows) { return []entity.BlacklistSymbol{}, nil } return nil, fmt.Errorf("BlackListSymbolRepository GetByAffectTime %w", err) } return MapBlacklistSymbolsDbToEntity(dest), nil }
解决方案
方案一:用OR组合条件(推荐)
你的逻辑可以直接简化为(AffectedTo IS NULL OR AffectedTo > affectedFrom),这种写法更符合SQL规范,数据库也更容易生成优化的查询计划,无需使用CASE。
修改后的代码:
func (b *BlackListSymbolRepository) GetByAffectTime(ctx context.Context, symbolId int64, affectedFrom time.Time, affectedTo time.Time) ([]entity.BlacklistSymbol, error) { // 直接用OR组合判断逻辑 affectedToCondition := table.BlacklistSymbol.AffectedTo.IS_NULL(). OR(table.BlacklistSymbol.AffectedTo.GT(postgres.TimestampT(affectedFrom))) stm := table.BlacklistSymbol.SELECT(table.BlacklistSymbol.AllColumns). WHERE( table.BlacklistSymbol.SymbolID.EQ(postgres.Int64(symbolId)). AND(table.BlacklistSymbol.Status.EQ(enum.Blacklistsymbolstatus.Active)). AND(table.BlacklistSymbol.AffectedFrom.LT_EQ(postgres.TimestampT(affectedTo))). AND(affectedToCondition), // 直接传入布尔条件 ) dest := make([]model.BlacklistSymbol, 0) if err := stm.QueryContext(ctx, b.getDbFunc(ctx), &dest); err != nil { if errors.Is(err, qrm.ErrNoRows) { return []entity.BlacklistSymbol{}, nil } return nil, fmt.Errorf("BlackListSymbolRepository GetByAffectTime %w", err) } return MapBlacklistSymbolsDbToEntity(dest), nil }
方案二:正确使用CASE表达式
若坚持使用CASE,需要明确将CASE的结果与true比较——go-jet的CASE表达式返回的是Expression类型,必须转换为布尔条件才能用于Where(),无需调用IS_NOT_NULL()。
修改后的CASE部分代码:
// 正确构建CASE布尔条件 caseCondition := postgres.CASE(). WHEN(table.BlacklistSymbol.AffectedTo.IS_NULL()).THEN(postgres.Bool(true)). ELSE(table.BlacklistSymbol.AffectedTo.GT(postgres.TimestampT(affectedFrom))). END().EQ(postgres.Bool(true)) // 判断CASE结果为true // 在WHERE中直接使用 AND(caseCondition)
内容的提问来源于stack exchange,提问作者JackAndrea
相关产品推荐
相关产品推荐

