Golang Common Table Expressions PostgreSQL MySQL

WITH

Most Bun queries support CTEs via With method:

q1 := db.NewSelect()
q2 := db.NewSelect()

q := db.NewInsert().
    With("q1", q1).
    With("q2", q2).
    Table("q1", "q2")

For example, you can use CTEs to bulk-delete rows that match some predicates:

const limit = 1000

for {
	subq := db.NewSelect().
		Model((*Comment)(nil)).
		Where("created_at < now() - interval '90 day'").
		Limit(limit)

	res, err := db.NewDelete().
		With("todo", subq).
		Model((*Comment)(nil)).
		Table("todo").
		Where("comment.id = todo.id").
		Exec(ctx)
	if err != nil {
		panic(err)
	}

	num, err := res.RowsAffected()
	if err != nil {
		panic(err)
	}
	if num < limit {
		break
	}
}
WITH todo AS (
    SELECT * FROM comments
    WHERE created_at < now() - interval '90 day'
    LIMIT 1000
)
DELETE FROM comments AS comment USING todo
WHERE comment.id = todo.id

Or copy data between tables:

src := db.NewSelect().Model((*Comment)(nil))

res, err := db.NewInsert().
    With("src", src).
    Table("comments_backup", "src").
    Exec(ctx)
WITH src AS (SELECT * FROM comments)
INSERT INTO comments_backups SELECT * FROM src

VALUES

Bun also provides ValuesQueryopen in new window to help building CTEs:

values := db.NewValues(&[]*Book{book1, book2})

res, err := db.NewUpdate().
    With("_data", values).
    Model((*Book)(nil)).
    Table("_data").
    Set("title = _data.title").
    Set("text = _data.text").
    Where("book.id = _data.id").
    Exec(ctx)
WITH _data (id, title, text) AS (VALUES (1, 'title1', 'text1'), (2, 'title2', 'text2'))
UPDATE books AS book
SET title = _data.title, text = _data.text
FROM _data
WHERE book.id = _data.id

WithOrder

You can also use WithOrderopen in new window to include row rank in values:

users := []User{
	{ID: 1, "one@my.com"},
	{ID: 2, "two@my.com"},
}

err := db.NewSelect().
	With("data", db.NewValues(&users).WithOrder()).
	Model(&users).
	Where("user.id = data.id").
	OrderExpr("data._order").
	Scan(ctx)
WITH "data" ("id", "email", _order) AS (
  VALUES
    (42::BIGINT, 'one@my.com'::VARCHAR, 0),
    (43::BIGINT, 'two@my.com'::VARCHAR, 1)
)
SELECT "user"."id", "user"."email"
FROM "users" AS "user"
WHERE (user.id = data.id)
ORDER BY data._order