ORM组件提供了一些常用的条件查询方法,并且条件方法支持多种数据类型输入。

func (m *Model) Where(where interface{}, args...interface{}) *Model
func (m *Model) Wheref(format string, args ...interface{}) *Model
func (m *Model) WherePri(where interface{}, args ...interface{}) *Model
func (m *Model) WhereBetween(column string, min, max interface{}) *Model
func (m *Model) WhereLike(column string, like interface{}) *Model
func (m *Model) WhereIn(column string, in interface{}) *Model
func (m *Model) WhereNull(columns ...string) *Model
func (m *Model) WhereLT(column string, value interface{}) *Model
func (m *Model) WhereLTE(column string, value interface{}) *Model
func (m *Model) WhereGT(column string, value interface{}) *Model
func (m *Model) WhereGTE(column string, value interface{}) *Model

func (m *Model) WhereNotBetween(column string, min, max interface{}) *Model
func (m *Model) WhereNotLike(column string, like interface{}) *Model
func (m *Model) WhereNotIn(column string, in interface{}) *Model
func (m *Model) WhereNotNull(columns ...string) *Model

func (m *Model) WhereOr(where interface{}, args ...interface{}) *Model
func (m *Model) WhereOrBetween(column string, min, max interface{}) *Model
func (m *Model) WhereOrLike(column string, like interface{}) *Model
func (m *Model) WhereOrIn(column string, in interface{}) *Model
func (m *Model) WhereOrNull(columns ...string) *Model
func (m *Model) WhereOrLT(column string, value interface{}) *Model 
func (m *Model) WhereOrLTE(column string, value interface{}) *Model 
func (m *Model) WhereOrGT(column string, value interface{}) *Model 
func (m *Model) WhereOrGTE(column string, value interface{}) *Model

func (m *Model) WhereOrNotBetween(column string, min, max interface{}) *Model
func (m *Model) WhereOrNotLike(column string, like interface{}) *Model
func (m *Model) WhereOrNotIn(column string, in interface{}) *Model
func (m *Model) WhereOrNotNull(columns ...string) *Model

下面我们对其中的几个常用方法做简单介绍,其他条件查询方法用法类似。

Where/WhereOr查询条件

基本介绍

这两个方法用于传递查询条件参数,支持的参数为任意的string/map/slice/struct/*struct类型。

Where条件参数推荐使用字符串的参数传递方式(并使用?占位符预处理),因为map/struct类型作为查询参数无法保证顺序性,且在部分情况下(数据库有时会帮助你自动进行查询索引优化),数据库的索引和你传递的查询条件顺序有一定关系。

当使用多个Where方法连接查询条件时,多个条件之间使用And进行连接。 此外,当存在多个查询条件时,gdb会默认将多个条件分别使用()符号进行包含,这种设计可以非常友好地支持查询条件分组。

使用示例:

// WHERE `uid`=1
Where("uid=1")
Where("uid", 1)
Where("uid=?", 1)
Where(g.Map{"uid" : 1})
// WHERE `uid` <= 1000 AND `age` >= 18
Where(g.Map{
    "uid <=" : 1000,
    "age >=" : 18,
})

// WHERE (`uid` <= 1000) AND (`age` >= 18)
Where("uid <=?", 1000).Where("age >=?", 18)

// WHERE `level`=1 OR `money`>=1000000
Where("level=? OR money >=?", 1, 1000000)

// WHERE (`level`=1) OR (`money`>=1000000)
Where("level", 1).WhereOr("money >=", 1000000)

// WHERE `uid` IN(1,2,3)
Where("uid IN(?)", g.Slice{1,2,3})

使用struct参数的示例,其中ormtag用于指定struct属性与表字段的映射关系:

type Condition struct{
    Sex int `orm:"sex"`
    Age int `orm:"age"`
}
Where(Condition{1, 18})
// WHERE `sex`=1 AND `age`=18

使用示例

Where + string,条件参数使用字符串和预处理。

// 查询多条记录并使用Limit分页
// SELECT * FROM user WHERE uid>1 LIMIT 0,10
g.Model("user").Where("uid > ?", 1).Limit(0, 10).All()

// 使用Fields方法查询指定字段
// 未使用Fields方法指定查询字段时,默认查询为*
// SELECT uid,name FROM user WHERE uid>1 LIMIT 0,10
g.Model("user").Fields("uid,name").Where("uid > ?", 1).Limit(0, 10).All()

// 支持多种Where条件参数类型
// SELECT * FROM user WHERE uid=1 LIMIT 1
g.Model("user").Where("uid=1").One()
g.Model("user").Where("uid", 1).One()
g.Model("user").Where("uid=?", 1).One()

// SELECT * FROM user WHERE (uid=1) AND (name='john') LIMIT 1
g.Model("user").Where("uid", 1).Where("name", "john").One()
g.Model("user").Where("uid=?", 1).Where("name=?", "john").One()

// SELECT * FROM user WHERE (uid=1) OR (name='john') LIMIT 1
g.Model("user").Where("uid=?", 1).WhereOr("name=?", "john").One()

Where + slice,预处理参数可直接通过slice参数给定。

// SELECT * FROM user WHERE age>18 AND name like '%john%'
g.Model("user").Where("age>? AND name like ?", g.Slice{18, "%john%"}).All()

// SELECT * FROM user WHERE status=1
g.Model("user").Where("status=?", g.Slice{1}).All()

Where + map,条件参数使用任意map类型传递。

// SELECT * FROM user WHERE uid=1 AND name='john' LIMIT 1
g.Model("user").Where(g.Map{"uid" : 1, "name" : "john"}).One()

// SELECT * FROM user WHERE uid=1 AND age>18 LIMIT 1
g.Model("user").Where(g.Map{"uid" : 1, "age>" : 18}).One()

Where + struct/*structstruct标签支持 orm/json,映射属性到字段名称关系。

type User struct {
    Id       int    `json:"uid"`
    UserName string `orm:"name"`
}
// SELECT * FROM user WHERE uid =1 AND name='john' LIMIT 1
g.Model("user").Where(User{ Id : 1, UserName : "john"}).One()

// SELECT * FROM user WHERE uid =1 LIMIT 1
g.Model("user").Where(&User{ Id : 1}).One()

以上的查询条件相对比较简单,我们来看一个比较复杂的查询示例。

condition := g.Map{
    "title like ?"         : "%九寨%",
    "online"               : 1,
    "hits between ? and ?" : g.Slice{1, 10},
    "exp > 0"              : nil,
    "category"             : g.Slice{100, 200},
}
// SELECT * FROM article WHERE title like '%九寨%' AND online=1 AND hits between 1 and 10 AND exp > 0 AND category IN(100,200)
g.Model("article").Where(condition).All()

Wheref格式化条件字符串

在某些场景中,在输入带有字符串的条件语句时,往往需要结合fmt.Sprintf来格式化条件(当然,注意在字符串中使用占位符代替变量的输入而不是直接将变量格式化),因此我们提供了Where+fmt.Sprintf结合的便捷方法Wheref。使用示例:

// WHERE score > 100 and status in('succeeded','completed')
Wheref(`score > ? and status in (?)`, 100, g.Slice{"succeeded", "completed"})

WherePri支持主键的查询条件

WherePri方法的功能同Where,但提供了对表主键的智能识别,常用于根据主键的便捷数据查询。假如user表的主键为uid,我们来看一下WhereWherePri的区别:

// WHERE `uid`=1
Where("uid", 1)
WherePri(1)

// WHERE `uid` IN(1,2,3)
Where("uid", g.Slice{1,2,3})
WherePri(g.Slice{1,2,3})

可以看到,当使用WherePri方法且给定参数为单一的参数基本类型或者slice类型时,将会被识别为主键的查询条件值。

WhereBuilder复杂条件组合

WhereBuilder用以组合生成复杂的Where条件。

对象创建

我们可以使用ModelBuilder方法生成WhereBuilder对象。该方法定义如下:

// Builder creates and returns a WhereBuilder.
func (m *Model) Builder() *WhereBuilder

使用示例

// SELECT * FROM `user` WHERE `id`=1 AND `address`="USA" AND (`status`="active" OR `status`="pending")
m := g.Model("user")
all, err := m.Where("id", 1).Where("address", "USA").Where(
	m.Builder().Where("status", "active").WhereOr("status", "pending"),
).All()

注意事项:空数组条件引发的0=1条件

我们来看例子:

SQL1

m := g.Model("auth")
m.Where("status", g.Slice{"permitted", "inherited"}).Where("uid", 1).All()
// SELECT * FROM `auth` WHERE (`status` IN('permitted','inherited')) AND (`uid`=1)

SQL2

m := g.Model("auth")
m.Where("status", g.Slice{}).Where("uid", 1).All()
// SELECT * FROM `auth` WHERE (0=1) AND (`uid`=1)

可以看到,当给定的数组条件为空数组时,生成的SQL出现了0=1的无效条件,这是为什么呢?

在开发者没有显示声明可以过滤空数组条件时,ORM不会自动过滤空数组条件,以避免程序逻辑绕过SQL限制条件,引发不可预知的业务问题。如果开发者确定SQL限制条件是可以过滤的,那么可以显示调用OmitEmpty/OmitEmptyWhere方法来执行空条件过滤,如下:

m := g.Model("auth")
m.Where("status", g.Slice{}).Where("uid", 1).OmitEmpty().All()
// SELECT * FROM `auth` WHERE `uid`=1














Content Menu

  • No labels

18 Comments

  1. // 支持多种Where条件参数类型
    // SELECT * FROM user WHERE uid=1 LIMIT 1
    db.Model("user").Where("u.uid=1",).One()
    db.Model("user").Where("u.uid", 1).One()
    db.Model("user").Where("u.uid=?", 1).One()

    请问u.uid=1中的u是别名吗还是?

    1. 示例有问题,已修正

  2. 数据库的索引和你传递的查询条件顺序有一定关系?

    这个应该没关系吧。假如有表test,字段a上有索引,字段b没有索引,那么以下两种写法是等价的,mysql会自动优化,都会用到a索引。

    select *  from test where a=123 and b=234

    select * from test where b=234 and a=123

    1. 是的,如果你传递的是map数据类型,条件的顺序是不固定的,但是MySQL会自动优化的,所以不影响。如果想要自己控制条件的先后顺序,可以不用map数据类型传递条件参数。

    2. 在sql语句前面加上一个Explain就能看到索引使用情况了.

  3. 请问 dto 在where 中的使用用途该如何使用呢

    ```

    // UserForDao is the golang structure of table user for DAO operations like Where/Data.
    type User struct {
    g.Meta `orm:"dto:true"`
    Id interface{} // 用户ID
    Passport interface{} // 用户账号
    Password interface{} // 用户密码
    Nickname interface{} // 用户昵称
    CreateAt *gtime.Time // 创建时间
    UpdateAt *gtime.Time // 更新时间
    }
    ```
    自动生成的DTO代码如上
    调用代码如下
    ```
    dao
    .User.Ctx(ctx).Where(dto.User{Nickname: in.Nickname}).Scan(&user)
    ```
    此时生成的sql如下
    SELECT `id`,`passport`,`password`,`nickname`,`create_at`,`update_at` FROM `user` WHERE `id` IS NULL AND `passport` IS NULL AND `password` IS NULL AND `nickname`=? AND `create_at` IS NULL AND `update_at` IS NULL LIMIT 1

    未填充的字段 会搜索为NULL

    此时是否有可选项 如未填充字段则不搜索


    或者是否有DTO完整的用法案例解说呢
  4. 可以先执行两个OR再执行一个AND吗?类似这样:

    SELECT  TNAME, DNAME, SAL
    FROM   TEACHER
    WHERE    (DNAME='计算机' OR DNAME='生物')
    AND   SAL>1000
    ORDER BY  SAL
    1. 同问 你实现了吗

  5. Where(where interface{}, args...interface{})

    m.Where(dao.SysUser.Columns().UserType, input.UserType)
    如果条件里面只有input里面的一个参数,是不是可以自动识别字段名和值呢? 类似WherePr 一样多爽


    1. 可以用do,例如:

      m.Where(do.SysUser{
          UserType: input.UserType,
      )
      1. 我定义了个model.XXInput 

        type XXInput struct{

        Id uint

        }

        然后直接用查询条件直接m.Where(input).Delete()这样可以吗,我代码可以跑,但是不知道这样用对不对

  6. 郭强 WherePri有时会返回空,我测试的时候出现频率非常高,三四下就会出现一次,然后我换Where就正常了。

  7. Fileds 是不是写错了,应该是 Fields
  8. 请问链式sql 怎么指定索引,发现mysql 默认走了别的索引,导致sql耗时变大

  9. OmitEmpty好像无法过滤OR查询中空数组条件引发的0=1条件(虽然这对于OR查询没什么影响)

  10. gf的单侧覆盖率真的牛.... 不敢想象, 这工作量....

  11. 支持位运算查询吧?

    以及mysql中是 bit(20) ,在gf gen dao生成代码的时候转化成了int,不影响吧?

  12. 可否给whereXXX函数增加一个bool参数,表明这个条件是否生效,这样可以根据业务数据传入的值的校验结果来确定是否需要此条件