golang快速构建SQL查询语句插件库sqlf的使用

Golang 快速构建 SQL 查询语句插件库 sqlf 的使用

sqlf 是一个快速的 Go SQL 查询构建器。

sqlf 的功能

sqlf 可以:

  • 帮助你在运行时高效构建 SQL 语句
  • 安全地更改受影响列的数量和参数数量
  • 在 SQL 语句中使用 SQL 表达式(如 UPDATE counters SET counter = counter + 1
  • 动态应用过滤器,添加 where 条件,更改结果排序等
  • 安全地在 SQL 片段中使用 ? 占位符 - sqlf 会根据需要将它们转换为 PostgreSQL 风格的 $1, $2,... 占位符
  • 像其他类似库一样,将结构体绑定到数据库列
  • 提供方法使用任何 database/sql 兼容的驱动程序执行查询

sqlf 不提供的功能

sqlf 不是 ORM,你仍然需要使用原始 SQL。它不提供:

  • 数据库模式迁移或任何其他数据库模式维护工具
  • 查询参数、列名和表名的编译时类型检查
  • OR 子句的包装器
  • 帮助开发者定位 SQL 语句问题的原因

性能表现

sqlf 性能优异。为了最大化性能并最小化内存占用,sqlf 会重用为查询构建分配的内存。负载越重,sqlf 工作得越快。

使用示例

构建复杂语句

var (
    region       string
    product      string
    productUnits int
    productSales float64
)

sqlf.SetDialect(sqlf.PostgreSQL)

err := sqlf.From("orders").
    With("regional_sales",
        sqlf.From("orders").
            Select("region, SUM(amount) AS total_sales").
            GroupBy("region")).
    With("top_regions",
        sqlf.From("regional_sales").
            Select("region").
            Where("total_sales > (SELECT SUM(total_sales)/10 FROM regional_sales)")).
    // 将查询字段映射到变量
    Select("region").To(&region).
    Select("product").To(&product).
    Select("SUM(quantity)").To(&productUnits).
    Select("SUM(amount) AS product_sales").To(&productSales).
    //
    Where("region IN (SELECT region FROM top_regions)").
    GroupBy("region, product").
    OrderBy("product_sales DESC").
    // 执行查询
    QueryAndClose(ctx, db, func(row *sql.Rows){
        // 为每个返回的行调用回调函数
        // 行值会自动扫描到绑定的变量
        fmt.Printf("%s\t%s\t%d\t$%.2f\n", region, product, productUnits, productSales)
    })
if err != nil {
    panic(err)
}

绑定结构体

type Offer struct {
    Id        int64   `db:"id"`
    ProductId int64   `db:"product_id"`
    Price     float64 `db:"price"`
    IsDeleted bool    `db:"is_deleted"`
}

var o Offer

err := sqlf.From("offers").
    Bind(&o).
    Where("id = ?", 42).
    QueryRowAndClose(ctx, db)
if err != nil {
    panic(err)
}

检索到私有字段

type Offer struct {
    id        int64
    productId int64
    price     float64
    isDeleted bool
}

var o Offer

err := sqlf.From("offers").
    Select("id").To(&o.id).
    Select("product_id").To(&o.productId).
    Select("price").To(&o.price).
    Select("is_deleted").To(&o.isDeleted).
    Where("id = ?", 42).
    QueryRowAndClose(ctx, db)
if err != nil {
    panic(err)
}

使用 sqlf.Stmt 构建基础查询

func (o *Offer) Select() *sqlf.Stmt {
    return sqlf.From("products").
        .Bind(o)
        // 忽略标记为删除的记录
        Where("is_deleted = false")
}

func (o Offer) Print() {
    fmt.Printf("%d\t%s\t$%.2f\n", o.id, o.name, o.price)
}

var o Offer

// 获取报价数据
err := o.Select().
    Where("id = ?", offerId).
    QueryRowAndClose(ctx, db)
if err != nil {
    panic(err)
}
o.Print()
// ...

// 选择并打印给定产品的5个最近放置的报价
err = o.Select().
    Where("product_id = ?", productId).
    OrderBy("id DESC").
    Limit(5).
    QueryAndClose(ctx, db, func(row *sql.Rows){
        o.Print()
    })
if err != nil {
    panic(err)
}
// ...

值绑定

var (
    minAmountRequested = true
    maxAmount float64
    minAmount float64
)

q := sqlf.From("offers").
    Select("MAX(amount)").To(&maxAmount).
    Where("is_deleted = false")

if minAmountRequested {
    q.Select("MIN(amount)").To(&minAmount)
}

err := q.QueryRowAndClose(ctx, db)
if err != nil {
    panic(err)
}
if minAmountRequested {
    fmt.Printf("Cheapest offer: $%.2f\n", minAmount)
}
fmt.Printf("Most expensive offer: $%.2f\n", minAmount)

连接查询

var (
    offerId     int64
    productName string
    price       float64
}

err := sqlf.From("offers o").
    Select("o.id").To(&offerId).
    Select("price").To(&price).
    Where("is_deleted = false").
    // 连接
    LeftJoin("products p", "p.id = o.product_id").
    // 将连接表中的列绑定到变量
    Select("p.name").To(&productName).
    // 打印前10个报价
    OrderBy("price DEST").
    Limit(10).
    QueryAndClose(ctx, db, func(row *sql.Rows){
        fmt.Printf("%d\t%s\t$%.2f\n", offerId, productName, price)
    })
if err != nil {
    panic(err)
}

子查询

q := sqlf.From("orders o").
    Select("date, region").
    SubQuery("(", ") AS prev_order_date",
        sqlf.From("orders po").
            Select("date").
            Where("region = o.region").
            Where("id < o.id").
            OrderBy("id DESC").
            Clause("LIMIT 1")).
    Where("date > CURRENT_DATE - interval '1 day'").
    OrderBy("id DESC")
fmt.Println(q.String())
q.Close()

联合查询

q := sqlf.From("tasks").
    Select("id, status").
    Where("status = ?", "new").
    Union(true, sqlf.PostgreSQL.From("tasks").
        Select("id, status").
        Where("status = ?", "wip"))
// ...
q.Close()

插入数据

var userId int64

_, err := sqlf.InsertInto("users").
    Set("email", "new@email.com").
    Set("address", "320 Some Avenue, Somewhereville, GA, US").
    Returning("id").To(&userId).
    Clause("ON CONFLICT (email) DO UPDATE SET address = users.address").
    QueryRowAndClose(ctx, db)

批量插入

_, err := sqlf.InsertInto("users").
    NewRow().
        Set("email", "first@email.com").
        Set("address", "320 Some Avenue, Somewhereville, GA, US").
    NewRow().
        Set("email", "second@email.com").
        Set("address", "320 Some Avenue, Somewhereville, GA, US").
    ExecAndClose(ctx, db)

更新数据

_, err := sqlf.Update("users").
    Set("email", "new@email.com").
    ExecAndClose(ctx, db)

删除数据

_, err := sqlf.DeleteFrom("products").
    Where("id = ?", 42)
    ExecAndClose(ctx, db)

以上是 sqlf 库的主要功能和用法示例,它提供了简洁的 API 来构建和执行各种 SQL 查询。


更多关于golang快速构建SQL查询语句插件库sqlf的使用的实战教程也可以访问 https://www.itying.com/category-94-b0.html

1 回复

更多关于golang快速构建SQL查询语句插件库sqlf的使用的实战系列教程也可以访问 https://www.itying.com/category-94-b0.html


使用sqlf快速构建SQL查询语句

sqlf是一个轻量级的Go语言SQL查询构建器,它提供了一种流畅、类型安全的方式来构建SQL查询语句。下面我将详细介绍sqlf的使用方法。

安装sqlf

go get github.com/leporo/sqlf

基本用法

简单查询

import "github.com/leporo/sqlf"

func main() {
    query := sqlf.Select("id, name, email").
        From("users").
        Where("deleted_at IS NULL").
        OrderBy("created_at DESC").
        Limit(10)
    
    sql, args := query.String(), query.Args()
    fmt.Println(sql)  // SELECT id, name, email FROM users WHERE deleted_at IS NULL ORDER BY created_at DESC LIMIT 10
    fmt.Println(args) // []
}

带参数的查询

query := sqlf.Select("id, name, email").
    From("users").
    Where("age > ?", 18).
    Where("status = ?", "active")

sql, args := query.String(), query.Args()
fmt.Println(sql)  // SELECT id, name, email FROM users WHERE age > ? AND status = ?
fmt.Println(args) // [18 "active"]

复杂条件

query := sqlf.Select("*").
    From("orders").
    Where("customer_id = ?", 123).
    Where("(status = ? OR status = ?)", "pending", "processing").
    Where("created_at > ?", time.Now().AddDate(0, -1, 0))

sql, args := query.String(), query.Args()

插入数据

query := sqlf.InsertInto("users").
    Set("name", "John Doe").
    Set("email", "john@example.com").
    Set("age", 30).
    Set("created_at", time.Now())

sql, args := query.String(), query.Args()
fmt.Println(sql)  // INSERT INTO users (name, email, age, created_at) VALUES (?, ?, ?, ?)
fmt.Println(args) // ["John Doe" "john@example.com" 30 "2023-05-01 12:00:00"]

更新数据

query := sqlf.Update("users").
    Set("name", "Jane Doe").
    Set("email", "jane@example.com").
    Where("id = ?", 1)

sql, args := query.String(), query.Args()

删除数据

query := sqlf.DeleteFrom("users").
    Where("id = ?", 1)

sql, args := query.String(), query.Args()

高级特性

子查询

subQuery := sqlf.Select("user_id").From("orders").Where("amount > ?", 100)

query := sqlf.Select("*").
    From("users").
    Where("id IN (?)", subQuery)

sql, args := query.String(), query.Args()

联合查询

query := sqlf.Select("u.id, u.name, o.order_date").
    From("users u").
    Join("orders o").On("u.id = o.user_id").
    Where("u.status = ?", "active")

sql, args := query.String(), query.Args()

批量插入

query := sqlf.InsertInto("users").Columns("name", "email")

for _, user := range users {
    query.Values(user.Name, user.Email)
}

sql, args := query.String(), query.Args()

原生SQL片段

query := sqlf.Select("*").
    From("users").
    Where("created_at BETWEEN ? AND ?", startDate, endDate).
    Where(sqlf.Expr("LOWER(name) LIKE ?", "%"+strings.ToLower(search)+"%"))

sql, args := query.String(), query.Args()

执行查询

sqlf本身不处理数据库连接,但可以轻松与标准库或ORM配合使用:

import "database/sql"

db, _ := sql.Open("postgres", "connection_string")

query := sqlf.Select("*").From("users").Where("id = ?", 1)

rows, err := db.Query(query.String(), query.Args()...)
if err != nil {
    log.Fatal(err)
}
defer rows.Close()

// 处理结果...

优势总结

  1. 类型安全:通过参数化查询避免SQL注入
  2. 链式调用:流畅的API设计
  3. 轻量级:不依赖其他库
  4. 可读性强:代码即SQL,易于理解和维护
  5. 灵活性:支持原生SQL片段和复杂查询

sqlf特别适合需要灵活构建SQL查询但又不想引入完整ORM的场景,它保持了SQL的直观性同时提供了更好的构建方式。

回到顶部