mirror of
https://github.com/hanzoai/sqlbuilder.git
synced 2026-10-01 00:37:42 +00:00
Add OrderByAsc and OrderByDesc methods to all builders
Implement new OrderByAsc(col) and OrderByDesc(col) methods for
SelectBuilder, UnionBuilder, UpdateBuilder, and DeleteBuilder. These
methods allow chaining multiple ORDER BY columns with different sort
directions, addressing the limitation where the old OrderBy/Asc/Desc
pattern could only apply a single direction to all columns.
Key changes:
- Add OrderByAsc(col string) and OrderByDesc(col string) to all four builders
- Each method appends the column with explicit ASC/DESC suffix
- Support chaining for complex ordering: OrderByDesc("score").OrderByAsc("name")
- Deprecate OrderBy/Asc/Desc methods with clear migration guidance
- Add comprehensive tests (TestSelectBuilder_OrderByAscDesc)
- Add example functions demonstrating usage
- Update all existing examples to use new API
- Update README.md with new "Build ORDER BY clause" section
This design is inspired by MyBatis-Plus and provides a more intuitive
and flexible API for handling multiple ORDER BY columns with different
sorting directions.
fix #214
This commit is contained in:
@@ -11,6 +11,7 @@
|
||||
- [Pre-defined SQL builders](#pre-defined-sql-builders)
|
||||
- [Build `WHERE` clause](#build-where-clause)
|
||||
- [Share `WHERE` clause among builders](#share-where-clause-among-builders)
|
||||
- [Build `ORDER BY` clause](#build-order-by-clause)
|
||||
- [Build SQL for different systems](#build-sql-for-different-systems)
|
||||
- [Using `Struct` as a light weight ORM](#using-struct-as-a-light-weight-orm)
|
||||
- [Nested SQL](#nested-sql)
|
||||
@@ -203,6 +204,26 @@ fmt.Println(ub)
|
||||
|
||||
Refer to the [WhereClause](https://pkg.go.dev/github.com/huandu/go-sqlbuilder#WhereClause) examples to learn its usage.
|
||||
|
||||
### Build `ORDER BY` clause
|
||||
|
||||
The `ORDER BY` clause is commonly used to sort query results. This package provides convenient methods to build `ORDER BY` clauses with proper ordering directions.
|
||||
|
||||
For scenarios where you need to order by multiple columns with different directions (ASC/DESC), use `OrderByAsc` and `OrderByDesc` methods. These methods can be chained to add multiple columns with their specific ordering.
|
||||
|
||||
```go
|
||||
sb := sqlbuilder.NewSelectBuilder()
|
||||
sb.Select("id", "name", "score").From("users")
|
||||
sb.OrderByDesc("score").OrderByAsc("name")
|
||||
|
||||
sql, args := sb.Build()
|
||||
fmt.Println(sql)
|
||||
|
||||
// Output:
|
||||
// SELECT id, name, score FROM users ORDER BY score DESC, name ASC
|
||||
```
|
||||
|
||||
The older `OrderBy` method combined with `Asc`/`Desc` is still available but deprecated, as it only supports a single ordering direction for all columns. The new `OrderByAsc` and `OrderByDesc` methods provide more flexibility and clarity when working with multiple columns.
|
||||
|
||||
### Build SQL for different systems
|
||||
|
||||
SQL syntax and parameter placeholders can differ across systems. To address these variations, this package introduces a concept termed "flavor".
|
||||
@@ -412,7 +433,7 @@ var baseUserSelect = sqlbuilder.NewSelectBuilder().
|
||||
|
||||
func ListActiveUsers(limit, offset int) (string, []interface{}) {
|
||||
sb := baseUserSelect.Clone() // independent copy
|
||||
sb.OrderBy("id").Asc()
|
||||
sb.OrderByAsc("id")
|
||||
sb.Limit(limit).Offset(offset)
|
||||
return sb.Build()
|
||||
}
|
||||
|
||||
+1
-1
@@ -68,7 +68,7 @@ func ExampleCTEBuilder() {
|
||||
sb.Where(
|
||||
sb.LessEqualThan("orders.price", 200),
|
||||
"valid_users.level < orders.min_level",
|
||||
).OrderBy("orders.price").Desc()
|
||||
).OrderByDesc("orders.price")
|
||||
|
||||
sql, args := sb.Build()
|
||||
fmt.Println(sql)
|
||||
|
||||
@@ -151,13 +151,40 @@ func (db *DeleteBuilder) AddWhereClause(whereClause *WhereClause) *DeleteBuilder
|
||||
}
|
||||
|
||||
// OrderBy sets columns of ORDER BY in DELETE.
|
||||
//
|
||||
// Deprecated: Use OrderByAsc or OrderByDesc instead for better support of multiple ORDER BY columns with different directions.
|
||||
// OrderBy combined with Asc/Desc only supports a single direction for all columns.
|
||||
func (db *DeleteBuilder) OrderBy(col ...string) *DeleteBuilder {
|
||||
db.orderByCols = col
|
||||
db.marker = deleteMarkerAfterOrderBy
|
||||
return db
|
||||
}
|
||||
|
||||
// OrderByAsc sets a column of ORDER BY in DELETE with ASC order.
|
||||
// It supports chaining multiple calls to add multiple ORDER BY columns with different directions.
|
||||
//
|
||||
// db.OrderByAsc("name").OrderByDesc("id")
|
||||
// // Generates: ORDER BY name ASC, id DESC
|
||||
func (db *DeleteBuilder) OrderByAsc(col string) *DeleteBuilder {
|
||||
db.orderByCols = append(db.orderByCols, col+" ASC")
|
||||
db.marker = deleteMarkerAfterOrderBy
|
||||
return db
|
||||
}
|
||||
|
||||
// OrderByDesc sets a column of ORDER BY in DELETE with DESC order.
|
||||
// It supports chaining multiple calls to add multiple ORDER BY columns with different directions.
|
||||
//
|
||||
// db.OrderByDesc("id").OrderByAsc("name")
|
||||
// // Generates: ORDER BY id DESC, name ASC
|
||||
func (db *DeleteBuilder) OrderByDesc(col string) *DeleteBuilder {
|
||||
db.orderByCols = append(db.orderByCols, col+" DESC")
|
||||
db.marker = deleteMarkerAfterOrderBy
|
||||
return db
|
||||
}
|
||||
|
||||
// Asc sets order of ORDER BY to ASC.
|
||||
//
|
||||
// Deprecated: Use OrderByAsc instead. Asc only supports a single direction for all ORDER BY columns.
|
||||
func (db *DeleteBuilder) Asc() *DeleteBuilder {
|
||||
db.order = "ASC"
|
||||
db.marker = deleteMarkerAfterOrderBy
|
||||
@@ -165,6 +192,8 @@ func (db *DeleteBuilder) Asc() *DeleteBuilder {
|
||||
}
|
||||
|
||||
// Desc sets order of ORDER BY to DESC.
|
||||
//
|
||||
// Deprecated: Use OrderByDesc instead. Desc only supports a single direction for all ORDER BY columns.
|
||||
func (db *DeleteBuilder) Desc() *DeleteBuilder {
|
||||
db.order = "DESC"
|
||||
db.marker = deleteMarkerAfterOrderBy
|
||||
|
||||
@@ -245,13 +245,40 @@ func (sb *SelectBuilder) GroupBy(col ...string) *SelectBuilder {
|
||||
}
|
||||
|
||||
// OrderBy sets columns of ORDER BY in SELECT.
|
||||
//
|
||||
// Deprecated: Use OrderByAsc or OrderByDesc instead for better support of multiple ORDER BY columns with different directions.
|
||||
// OrderBy combined with Asc/Desc only supports a single direction for all columns.
|
||||
func (sb *SelectBuilder) OrderBy(col ...string) *SelectBuilder {
|
||||
sb.orderByCols = append(sb.orderByCols, col...)
|
||||
sb.marker = selectMarkerAfterOrderBy
|
||||
return sb
|
||||
}
|
||||
|
||||
// OrderByAsc sets a column of ORDER BY in SELECT with ASC order.
|
||||
// It supports chaining multiple calls to add multiple ORDER BY columns with different directions.
|
||||
//
|
||||
// sb.OrderByAsc("name").OrderByDesc("id")
|
||||
// // Generates: ORDER BY name ASC, id DESC
|
||||
func (sb *SelectBuilder) OrderByAsc(col string) *SelectBuilder {
|
||||
sb.orderByCols = append(sb.orderByCols, col+" ASC")
|
||||
sb.marker = selectMarkerAfterOrderBy
|
||||
return sb
|
||||
}
|
||||
|
||||
// OrderByDesc sets a column of ORDER BY in SELECT with DESC order.
|
||||
// It supports chaining multiple calls to add multiple ORDER BY columns with different directions.
|
||||
//
|
||||
// sb.OrderByDesc("id").OrderByAsc("name")
|
||||
// // Generates: ORDER BY id DESC, name ASC
|
||||
func (sb *SelectBuilder) OrderByDesc(col string) *SelectBuilder {
|
||||
sb.orderByCols = append(sb.orderByCols, col+" DESC")
|
||||
sb.marker = selectMarkerAfterOrderBy
|
||||
return sb
|
||||
}
|
||||
|
||||
// Asc sets order of ORDER BY to ASC.
|
||||
//
|
||||
// Deprecated: Use OrderByAsc instead. Asc only supports a single direction for all ORDER BY columns.
|
||||
func (sb *SelectBuilder) Asc() *SelectBuilder {
|
||||
sb.order = "ASC"
|
||||
sb.marker = selectMarkerAfterOrderBy
|
||||
@@ -259,6 +286,8 @@ func (sb *SelectBuilder) Asc() *SelectBuilder {
|
||||
}
|
||||
|
||||
// Desc sets order of ORDER BY to DESC.
|
||||
//
|
||||
// Deprecated: Use OrderByDesc instead. Desc only supports a single direction for all ORDER BY columns.
|
||||
func (sb *SelectBuilder) Desc() *SelectBuilder {
|
||||
sb.order = "DESC"
|
||||
sb.marker = selectMarkerAfterOrderBy
|
||||
|
||||
+106
-3
@@ -44,7 +44,7 @@ func ExampleSelectBuilder() {
|
||||
"modified_at > created_at + "+sb.Var(86400), // It's allowed to write arbitrary SQL.
|
||||
)
|
||||
sb.GroupBy("status").Having(sb.NotIn("status", 4, 5))
|
||||
sb.OrderBy("modified_at").Asc()
|
||||
sb.OrderByAsc("modified_at")
|
||||
sb.Limit(10).Offset(5)
|
||||
|
||||
s, args := sb.Build()
|
||||
@@ -73,7 +73,7 @@ func ExampleSelectBuilder_advancedUsage() {
|
||||
sb.In("status", Flatten([]int{1, 2, 3})...),
|
||||
sb.Between("created_at", start, end),
|
||||
)
|
||||
sb.OrderBy("modified_at").Desc()
|
||||
sb.OrderByDesc("modified_at")
|
||||
|
||||
innerSb.Select("*")
|
||||
innerSb.From("banned")
|
||||
@@ -407,7 +407,7 @@ func ExampleSelectBuilder_LateralAs() {
|
||||
Where(
|
||||
"all_sales.salesperson_id = salesperson.id",
|
||||
).
|
||||
OrderBy("amount").Desc().Limit(1),
|
||||
OrderByDesc("amount").Limit(1),
|
||||
"max_sale",
|
||||
),
|
||||
)
|
||||
@@ -449,3 +449,106 @@ func TestSelectBuilderClone(t *testing.T) {
|
||||
s2After := clone.String()
|
||||
a.NotEqual(s1After, s2After)
|
||||
}
|
||||
|
||||
func ExampleSelectBuilder_OrderByAsc() {
|
||||
sb := NewSelectBuilder()
|
||||
sb.Select("id", "name", "score")
|
||||
sb.From("users")
|
||||
sb.Where(sb.GreaterThan("score", 0))
|
||||
sb.OrderByAsc("name")
|
||||
|
||||
sql, args := sb.Build()
|
||||
fmt.Println(sql)
|
||||
fmt.Println(args)
|
||||
|
||||
// Output:
|
||||
// SELECT id, name, score FROM users WHERE score > ? ORDER BY name ASC
|
||||
// [0]
|
||||
}
|
||||
|
||||
func ExampleSelectBuilder_OrderByDesc() {
|
||||
sb := NewSelectBuilder()
|
||||
sb.Select("id", "name", "score")
|
||||
sb.From("users")
|
||||
sb.Where(sb.GreaterThan("score", 0))
|
||||
sb.OrderByDesc("score")
|
||||
|
||||
sql, args := sb.Build()
|
||||
fmt.Println(sql)
|
||||
fmt.Println(args)
|
||||
|
||||
// Output:
|
||||
// SELECT id, name, score FROM users WHERE score > ? ORDER BY score DESC
|
||||
// [0]
|
||||
}
|
||||
|
||||
func ExampleSelectBuilder_OrderByAsc_multiple() {
|
||||
sb := NewSelectBuilder()
|
||||
sb.Select("id", "name", "score")
|
||||
sb.From("users")
|
||||
sb.Where(sb.GreaterThan("score", 0))
|
||||
// Chain multiple OrderByAsc and OrderByDesc calls with different directions
|
||||
sb.OrderByDesc("score").OrderByAsc("name").OrderByDesc("id")
|
||||
|
||||
sql, args := sb.Build()
|
||||
fmt.Println(sql)
|
||||
fmt.Println(args)
|
||||
|
||||
// Output:
|
||||
// SELECT id, name, score FROM users WHERE score > ? ORDER BY score DESC, name ASC, id DESC
|
||||
// [0]
|
||||
}
|
||||
|
||||
func TestSelectBuilder_OrderByAscDesc(t *testing.T) {
|
||||
a := assert.New(t)
|
||||
|
||||
// Test OrderByAsc with single column
|
||||
sb := NewSelectBuilder()
|
||||
sb.Select("*").From("users").OrderByAsc("name")
|
||||
sql, _ := sb.Build()
|
||||
a.Equal("SELECT * FROM users ORDER BY name ASC", sql)
|
||||
|
||||
// Test OrderByDesc with single column
|
||||
sb = NewSelectBuilder()
|
||||
sb.Select("*").From("users").OrderByDesc("id")
|
||||
sql, _ = sb.Build()
|
||||
a.Equal("SELECT * FROM users ORDER BY id DESC", sql)
|
||||
|
||||
// Test chaining OrderByAsc and OrderByDesc
|
||||
sb = NewSelectBuilder()
|
||||
sb.Select("*").From("users")
|
||||
sb.OrderByDesc("score").OrderByAsc("name")
|
||||
sql, _ = sb.Build()
|
||||
a.Equal("SELECT * FROM users ORDER BY score DESC, name ASC", sql)
|
||||
|
||||
// Test multiple OrderByDesc calls
|
||||
sb = NewSelectBuilder()
|
||||
sb.Select("*").From("users")
|
||||
sb.OrderByDesc("score").OrderByDesc("id")
|
||||
sql, _ = sb.Build()
|
||||
a.Equal("SELECT * FROM users ORDER BY score DESC, id DESC", sql)
|
||||
|
||||
// Test multiple OrderByAsc calls
|
||||
sb = NewSelectBuilder()
|
||||
sb.Select("*").From("users")
|
||||
sb.OrderByAsc("name").OrderByAsc("email")
|
||||
sql, _ = sb.Build()
|
||||
a.Equal("SELECT * FROM users ORDER BY name ASC, email ASC", sql)
|
||||
|
||||
// Test mixed ordering with more complex scenario
|
||||
sb = NewSelectBuilder()
|
||||
sb.Select("id", "name", "score", "created_at").From("users")
|
||||
sb.Where(sb.GreaterThan("score", 0))
|
||||
sb.OrderByDesc("score").OrderByAsc("name").OrderByDesc("created_at")
|
||||
sql, args := sb.Build()
|
||||
a.Equal("SELECT id, name, score, created_at FROM users WHERE score > ? ORDER BY score DESC, name ASC, created_at DESC", sql)
|
||||
a.Equal([]interface{}{0}, args)
|
||||
|
||||
// Test that OrderByAsc/OrderByDesc work with table aliases
|
||||
sb = NewSelectBuilder()
|
||||
sb.Select("u.id", "u.name", "o.total").From("users u")
|
||||
sb.Join("orders o", "u.id = o.user_id")
|
||||
sb.OrderByDesc("o.total").OrderByAsc("u.name")
|
||||
sql, _ = sb.Build()
|
||||
a.Equal("SELECT u.id, u.name, o.total FROM users u JOIN orders o ON u.id = o.user_id ORDER BY o.total DESC, u.name ASC", sql)
|
||||
}
|
||||
|
||||
@@ -107,13 +107,40 @@ func (ub *UnionBuilder) union(opt string, builders ...Builder) *UnionBuilder {
|
||||
}
|
||||
|
||||
// OrderBy sets columns of ORDER BY in SELECT.
|
||||
//
|
||||
// Deprecated: Use OrderByAsc or OrderByDesc instead for better support of multiple ORDER BY columns with different directions.
|
||||
// OrderBy combined with Asc/Desc only supports a single direction for all columns.
|
||||
func (ub *UnionBuilder) OrderBy(col ...string) *UnionBuilder {
|
||||
ub.orderByCols = col
|
||||
ub.marker = unionMarkerAfterOrderBy
|
||||
return ub
|
||||
}
|
||||
|
||||
// OrderByAsc sets a column of ORDER BY in SELECT with ASC order.
|
||||
// It supports chaining multiple calls to add multiple ORDER BY columns with different directions.
|
||||
//
|
||||
// ub.OrderByAsc("name").OrderByDesc("id")
|
||||
// // Generates: ORDER BY name ASC, id DESC
|
||||
func (ub *UnionBuilder) OrderByAsc(col string) *UnionBuilder {
|
||||
ub.orderByCols = append(ub.orderByCols, col+" ASC")
|
||||
ub.marker = unionMarkerAfterOrderBy
|
||||
return ub
|
||||
}
|
||||
|
||||
// OrderByDesc sets a column of ORDER BY in SELECT with DESC order.
|
||||
// It supports chaining multiple calls to add multiple ORDER BY columns with different directions.
|
||||
//
|
||||
// ub.OrderByDesc("id").OrderByAsc("name")
|
||||
// // Generates: ORDER BY id DESC, name ASC
|
||||
func (ub *UnionBuilder) OrderByDesc(col string) *UnionBuilder {
|
||||
ub.orderByCols = append(ub.orderByCols, col+" DESC")
|
||||
ub.marker = unionMarkerAfterOrderBy
|
||||
return ub
|
||||
}
|
||||
|
||||
// Asc sets order of ORDER BY to ASC.
|
||||
//
|
||||
// Deprecated: Use OrderByAsc instead. Asc only supports a single direction for all ORDER BY columns.
|
||||
func (ub *UnionBuilder) Asc() *UnionBuilder {
|
||||
ub.order = "ASC"
|
||||
ub.marker = unionMarkerAfterOrderBy
|
||||
@@ -121,6 +148,8 @@ func (ub *UnionBuilder) Asc() *UnionBuilder {
|
||||
}
|
||||
|
||||
// Desc sets order of ORDER BY to DESC.
|
||||
//
|
||||
// Deprecated: Use OrderByDesc instead. Desc only supports a single direction for all ORDER BY columns.
|
||||
func (ub *UnionBuilder) Desc() *UnionBuilder {
|
||||
ub.order = "DESC"
|
||||
ub.marker = unionMarkerAfterOrderBy
|
||||
|
||||
+2
-2
@@ -26,7 +26,7 @@ func ExampleUnion() {
|
||||
)
|
||||
|
||||
ub := Union(sb1, sb2)
|
||||
ub.OrderBy("created_at").Desc()
|
||||
ub.OrderByDesc("created_at")
|
||||
|
||||
sql, args := ub.Build()
|
||||
fmt.Println(sql)
|
||||
@@ -46,7 +46,7 @@ func ExampleUnionAll() {
|
||||
)
|
||||
|
||||
ub := UnionAll(sb, Build("TABLE demo.user_profile"))
|
||||
ub.OrderBy("created_at").Asc()
|
||||
ub.OrderByAsc("created_at")
|
||||
ub.Limit(100).Offset(5)
|
||||
|
||||
sql, args := ub.Build()
|
||||
|
||||
@@ -207,13 +207,40 @@ func (ub *UpdateBuilder) Div(field string, value interface{}) string {
|
||||
}
|
||||
|
||||
// OrderBy sets columns of ORDER BY in UPDATE.
|
||||
//
|
||||
// Deprecated: Use OrderByAsc or OrderByDesc instead for better support of multiple ORDER BY columns with different directions.
|
||||
// OrderBy combined with Asc/Desc only supports a single direction for all columns.
|
||||
func (ub *UpdateBuilder) OrderBy(col ...string) *UpdateBuilder {
|
||||
ub.orderByCols = col
|
||||
ub.marker = updateMarkerAfterOrderBy
|
||||
return ub
|
||||
}
|
||||
|
||||
// OrderByAsc sets a column of ORDER BY in UPDATE with ASC order.
|
||||
// It supports chaining multiple calls to add multiple ORDER BY columns with different directions.
|
||||
//
|
||||
// ub.OrderByAsc("name").OrderByDesc("id")
|
||||
// // Generates: ORDER BY name ASC, id DESC
|
||||
func (ub *UpdateBuilder) OrderByAsc(col string) *UpdateBuilder {
|
||||
ub.orderByCols = append(ub.orderByCols, col+" ASC")
|
||||
ub.marker = updateMarkerAfterOrderBy
|
||||
return ub
|
||||
}
|
||||
|
||||
// OrderByDesc sets a column of ORDER BY in UPDATE with DESC order.
|
||||
// It supports chaining multiple calls to add multiple ORDER BY columns with different directions.
|
||||
//
|
||||
// ub.OrderByDesc("id").OrderByAsc("name")
|
||||
// // Generates: ORDER BY id DESC, name ASC
|
||||
func (ub *UpdateBuilder) OrderByDesc(col string) *UpdateBuilder {
|
||||
ub.orderByCols = append(ub.orderByCols, col+" DESC")
|
||||
ub.marker = updateMarkerAfterOrderBy
|
||||
return ub
|
||||
}
|
||||
|
||||
// Asc sets order of ORDER BY to ASC.
|
||||
//
|
||||
// Deprecated: Use OrderByAsc instead. Asc only supports a single direction for all ORDER BY columns.
|
||||
func (ub *UpdateBuilder) Asc() *UpdateBuilder {
|
||||
ub.order = "ASC"
|
||||
ub.marker = updateMarkerAfterOrderBy
|
||||
@@ -221,6 +248,8 @@ func (ub *UpdateBuilder) Asc() *UpdateBuilder {
|
||||
}
|
||||
|
||||
// Desc sets order of ORDER BY to DESC.
|
||||
//
|
||||
// Deprecated: Use OrderByDesc instead. Desc only supports a single direction for all ORDER BY columns.
|
||||
func (ub *UpdateBuilder) Desc() *UpdateBuilder {
|
||||
ub.order = "DESC"
|
||||
ub.marker = updateMarkerAfterOrderBy
|
||||
|
||||
+2
-2
@@ -43,7 +43,7 @@ func ExampleUpdateBuilder() {
|
||||
),
|
||||
"modified_at > created_at + "+ub.Var(86400), // It's allowed to write arbitrary SQL.
|
||||
)
|
||||
ub.OrderBy("id").Asc()
|
||||
ub.OrderByAsc("id")
|
||||
|
||||
sql, args := ub.Build()
|
||||
fmt.Println(sql)
|
||||
@@ -105,7 +105,7 @@ func ExampleUpdateBuilder_SQL() {
|
||||
ub.Assign("type", "sys"),
|
||||
)
|
||||
ub.SQL("/* after set */")
|
||||
ub.OrderBy("id").Desc()
|
||||
ub.OrderByDesc("id")
|
||||
ub.SQL("/* after order by */")
|
||||
ub.Limit(10)
|
||||
ub.SQL("/* after limit */")
|
||||
|
||||
Reference in New Issue
Block a user