golang高效构建SQL查询语句插件库Squirrel的使用

Golang高效构建SQL查询语句插件库Squirrel的使用

Stability: Maintenance

GoDoc

Build Status

简介

Squirrel是一个Go语言的流式SQL生成器,它可以帮助你从可组合的部分构建SQL查询。Squirrel不是一个ORM

基本用法

安装

import "github.com/Masterminds/squirrel"

查询示例

import sq "github.com/Masterminds/squirrel"

// 基础查询
users := sq.Select("*").From("users").Join("emails USING (email_id)")

// 添加条件
active := users.Where(sq.Eq{"deleted_at": nil})

// 生成SQL
sql, args, err := active.ToSql()

// sql == "SELECT * FROM users JOIN emails USING (email_id) WHERE deleted_at IS NULL"

插入示例

sql, args, err := sq.
    Insert("users").Columns("name", "age").
    Values("moe", 13).Values("larry", sq.Expr("? + 5", 12)).
    ToSql()

// sql == "INSERT INTO users (name,age) VALUES (?,?),(?,? + 5)"

执行查询

stooges := users.Where(sq.Eq{"username": []string{"moe", "larry", "curly", "shemp"}})
three_stooges := stooges.Limit(3)
rows, err := three_stooges.RunWith(db).Query()

// 等同于:
rows, err := db.Query("SELECT * FROM users WHERE username IN (?,?,?,?) LIMIT 3",
                     "moe", "larry", "curly", "shemp")

条件查询构建

if len(q) > 0 {
    users = users.Where("name LIKE ?", fmt.Sprint("%", q, "%"))
}

高级特性

语句缓存和构建器

// StmtCache缓存准备好的语句
dbCache := sq.NewStmtCache(db)

// StatementBuilder保持语法整洁
mydb := sq.StatementBuilder.RunWith(dbCache)
select_users := mydb.Select("*").From("users")

PostgreSQL支持

psql := sq.StatementBuilder.PlaceholderFormat(sq.Dollar)

// 使用问号作为占位符...
sql, _, _ := psql.Select("*").From("elephants").Where("name IN (?,?)", "Dumbo", "Verna").ToSql()

// ...squirrel会根据PlaceholderFormat替换它们
// sql == "SELECT * FROM elephants WHERE name IN ($1,$2)"

// 可以检索id...
query := sq.Insert("nodes").
    Columns("uuid", "type", "data").
    Values(node.Uuid, node.Type, node.Data).
    Suffix("RETURNING \"id\"").
    RunWith(m.db).
    PlaceholderFormat(sq.Dollar)

query.QueryRow().Scan(&node.id)

转义问号

可以通过插入两个问号来转义问号:

SELECT * FROM nodes WHERE meta->'format' ??| array[?,?]

使用Dollar占位符时会生成:

SELECT * FROM nodes WHERE meta->'format' ?| array[$1,$2]

完整示例Demo

package main

import (
	"database/sql"
	"fmt"
	"log"

	sq "github.com/Masterminds/squirrel"
	_ "github.com/lib/pq"
)

func main() {
	// 连接数据库
	db, err := sql.Open("postgres", "user=postgres dbname=test sslmode=disable")
	if err != nil {
		log.Fatal(err)
	}
	defer db.Close()

	// 创建StatementBuilder
	psql := sq.StatementBuilder.PlaceholderFormat(sq.Dollar)

	// 1. 查询示例
	query := psql.Select("id", "name", "email").
		From("users").
		Where(sq.Eq{"active": true}).
		Limit(10)

	sql, args, err := query.ToSql()
	if err != nil {
		log.Fatal(err)
	}
	fmt.Println("生成的SQL:", sql)
	fmt.Println("参数:", args)

	// 执行查询
	rows, err := query.RunWith(db).Query()
	if err != nil {
		log.Fatal(err)
	}
	defer rows.Close()

	// 处理结果...

	// 2. 插入示例
	insert := psql.Insert("users").
		Columns("name", "age").
		Values("John", 30).
		Values("Alice", sq.Expr("? + 5", 25))

	sql, args, err = insert.ToSql()
	if err != nil {
		log.Fatal(err)
	}
	fmt.Println("\n生成的插入SQL:", sql)
	fmt.Println("参数:", args)

	// 执行插入
	_, err = insert.RunWith(db).Exec()
	if err != nil {
		log.Fatal(err)
	}

	// 3. 更新示例
	update := psql.Update("users").
		Set("name", "Bob").
		Set("age", sq.Expr("age + 1")).
		Where(sq.Eq{"id": 1})

	sql, args, err = update.ToSql()
	if err != nil {
		log.Fatal(err)
	}
	fmt.Println("\n生成的更新SQL:", sql)
	fmt.Println("参数:", args)

	// 4. 条件查询构建
	searchTerm := "john"
	query = psql.Select("*").From("users")
	if searchTerm != "" {
		query = query.Where("name LIKE ?", fmt.Sprintf("%%%s%%", searchTerm))
	}

	sql, args, err = query.ToSql()
	if err != nil {
		log.Fatal(err)
	}
	fmt.Println("\n条件查询SQL:", sql)
	fmt.Println("参数:", args)
}

常见问题

  1. 如何构建复合键/元组的IN查询?

    使用OrEq组合:

    sq.Or{
      sq.Eq{"col1": 1, "col2": 2},
      sq.Eq{"col1": 3, "col2": 4}}
    

    生成的SQL:

    WHERE (col1 = 1 AND col2 = 2) OR (col1 = 3 AND col2 = 4)
    
  2. 为什么Eq{"mynumber": []uint8{1,2,3}}不会转换为IN查询?

    因为[]byte在Go中只是[]uint8的别名,无法区分两者。

  3. 某些功能文档不完善?

    测试用例可以作为文档的一部分参考。

许可证

Squirrel采用MIT许可证发布。


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

1 回复

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


Squirrel: Golang高效构建SQL查询语句插件库

Squirrel是一个Go语言的SQL查询构建器库,它可以帮助开发者以更优雅、更安全的方式构建SQL查询语句,避免SQL注入风险,同时提高代码可读性。

基本特性

  1. 链式调用:提供流畅的API接口
  2. 类型安全:减少SQL注入风险
  3. 多数据库支持:支持MySQL、PostgreSQL、SQLite等
  4. 可扩展性:可以自定义SQL方言

安装

go get github.com/Masterminds/squirrel

基本使用示例

1. 简单查询

import sq "github.com/Masterminds/squirrel"

// 构建SELECT查询
query, args, err := sq.Select("id", "name", "email").
    From("users").
    Where(sq.Eq{"active": true}).
    OrderBy("name ASC").
    Limit(10).
    ToSql()

// 输出: SELECT id, name, email FROM users WHERE active = ? ORDER BY name ASC LIMIT 10
// args: [true]

2. 条件查询

// 复杂条件查询
query, args, err := sq.Select("*").
    From("orders").
    Where(sq.And{
        sq.Eq{"status": "completed"},
        sq.Or{
            sq.Gt{"amount": 1000},
            sq.Lt{"created_at": "2023-01-01"},
        },
    }).
    ToSql()

// 输出: SELECT * FROM orders WHERE (status = ?) AND ((amount > ?) OR (created_at < ?))
// args: ["completed", 1000, "2023-01-01"]

3. 插入数据

query, args, err := sq.Insert("users").
    Columns("name", "email", "created_at").
    Values("John Doe", "john@example.com", time.Now()).
    ToSql()

// 输出: INSERT INTO users (name,email,created_at) VALUES (?,?,?)
// args: ["John Doe", "john@example.com", "2023-11-15 12:00:00"]

4. 更新数据

query, args, err := sq.Update("users").
    Set("name", "Jane Doe").
    Set("email", "jane@example.com").
    Where(sq.Eq{"id": 1}).
    ToSql()

// 输出: UPDATE users SET name = ?, email = ? WHERE id = ?
// args: ["Jane Doe", "jane@example.com", 1]

5. 删除数据

query, args, err := sq.Delete("users").
    Where(sq.Eq{"id": 1}).
    ToSql()

// 输出: DELETE FROM users WHERE id = ?
// args: [1]

高级特性

1. 子查询

subQuery := sq.Select("user_id").From("orders").Where(sq.Gt{"amount": 1000})

query, args, err := sq.Select("*").
    From("users").
    Where(sq.Eq{"id": subQuery}).
    ToSql()

// 输出: SELECT * FROM users WHERE id IN (SELECT user_id FROM orders WHERE amount > ?)
// args: [1000]

2. JOIN操作

query, args, err := sq.Select("u.name", "o.amount").
    From("users u").
    Join("orders o ON u.id = o.user_id").
    Where(sq.Gt{"o.amount": 500}).
    ToSql()

// 输出: SELECT u.name, o.amount FROM users u JOIN orders o ON u.id = o.user_id WHERE o.amount > ?
// args: [500]

3. 批量插入

query, args, err := sq.Insert("users").
    Columns("name", "email").
    Values("User1", "user1@example.com").
    Values("User2", "user2@example.com").
    Values("User3", "user3@example.com").
    ToSql()

// 输出: INSERT INTO users (name,email) VALUES (?,?),(?,?),(?,?)
// args: ["User1", "user1@example.com", "User2", "user2@example.com", "User3", "user3@example.com"]

4. 占位符方言支持

// 使用PostgreSQL的$n占位符
psql := sq.StatementBuilder.PlaceholderFormat(sq.Dollar)
query, args, err := psql.Select("*").From("users").Where(sq.Eq{"id": 1}).ToSql()

// 输出: SELECT * FROM users WHERE id = $1
// args: [1]

实际应用示例

func GetActiveUsers(db *sql.DB, minOrders int) ([]User, error) {
    query, args, err := sq.Select("u.id", "u.name", "u.email", "COUNT(o.id) as order_count").
        From("users u").
        LeftJoin("orders o ON u.id = o.user_id").
        Where(sq.Eq{"u.active": true}).
        GroupBy("u.id").
        Having("COUNT(o.id) >= ?", minOrders).
        OrderBy("order_count DESC").
        ToSql()
    
    if err != nil {
        return nil, err
    }
    
    rows, err := db.Query(query, args...)
    if err != nil {
        return nil, err
    }
    defer rows.Close()
    
    var users []User
    for rows.Next() {
        var u User
        if err := rows.Scan(&u.ID, &u.Name, &u.Email, &u.OrderCount); err != nil {
            return nil, err
        }
        users = append(users, u)
    }
    
    return users, nil
}

优势总结

  1. 安全性:自动处理参数化查询,防止SQL注入
  2. 可读性:链式调用使代码更易读和维护
  3. 灵活性:支持复杂查询构建
  4. 多数据库支持:通过占位符格式支持不同数据库

Squirrel是构建SQL查询的强大工具,特别适合需要动态构建查询的应用场景。它既保持了SQL的灵活性,又提供了Go语言的类型安全和结构化的构建方式。

回到顶部