Query Builder
Build complex SQL queries using the fluent API. Type-safe, readable, and SQL injection-proof.
Why Use the Query Builder?
- Readable - Method names match SQL keywords
- Type-safe - Compiler catches errors
- Secure - Parameters are automatically escaped
- Chainable - Build queries incrementally
SELECT Queries
Basic Select
result := db.select_from ("users").execute
-- SELECT * FROM users
Select Specific Columns
result := db.select_from ("users")
.columns ("id, name, email")
.execute
-- SELECT id, name, email FROM users
With WHERE Clause
result := db.select_from ("users")
.where ("active = ?", 1)
.execute
-- SELECT * FROM users WHERE active = 1
-- Multiple conditions
result := db.select_from ("users")
.where ("active = 1")
.and_where ("created_at > ?", "2024-01-01")
.execute
Ordering and Limiting
result := db.select_from ("users")
.order_by ("created_at DESC")
.limit (10)
.offset (20) -- Skip first 20
.execute
DISTINCT
result := db.select_from ("orders")
.columns ("customer_id")
.distinct
.execute
-- SELECT DISTINCT customer_id FROM orders
JOINs
-- INNER JOIN
result := db.select_from ("orders")
.columns ("orders.id, orders.total, users.name")
.join ("users", "users.id = orders.user_id")
.execute
-- LEFT JOIN
result := db.select_from ("users")
.columns ("users.name, COUNT(orders.id) as order_count")
.left_join ("orders", "orders.user_id = users.id")
.group_by ("users.id")
.execute
GROUP BY and HAVING
result := db.select_from ("orders")
.columns ("user_id, COUNT(*) as order_count, SUM(total) as total_spent")
.group_by ("user_id")
.having ("order_count >= 5")
.order_by ("total_spent DESC")
.execute
INSERT Queries
db.insert_into ("users")
.value ("name", "Alice")
.value ("email", "alice@example.com")
.value ("age", 30)
.execute
-- INSERT OR REPLACE (upsert)
db.insert_into ("settings")
.or_replace
.value ("key", "theme")
.value ("value", "dark")
.execute
UPDATE Queries
db.update ("users")
.set ("name", "Alice Smith")
.set ("updated_at", "datetime('now')")
.where ("id = ?", 1)
.execute
-- Increment a counter
db.update ("articles")
.set_raw ("view_count", "view_count + 1")
.where ("id = ?", article_id)
.execute
DELETE Queries
db.delete_from ("users")
.where ("id = ?", user_id)
.execute
-- Delete old records
db.delete_from ("logs")
.where ("created_at < datetime('now', '-30 days')")
.execute
Debugging: View Generated SQL
local
builder: SIMPLE_SQL_SELECT_BUILDER
do
builder := db.select_from ("users")
.columns ("id, name")
.where ("active = 1")
.order_by ("name")
.limit (10)
-- See the SQL without executing
print (builder.to_sql)
-- Output: SELECT id, name FROM users WHERE active = 1 ORDER BY name LIMIT 10
-- Then execute
result := builder.execute
end
Building Queries Dynamically
build_search_query (a_term: detachable STRING; a_category: detachable INTEGER; a_limit: INTEGER): SIMPLE_SQL_RESULT
local
builder: SIMPLE_SQL_SELECT_BUILDER
do
builder := db.select_from ("products")
.columns ("id, name, price")
if attached a_term as term then
builder := builder.where ("name LIKE ?", "%%" + term + "%%")
end
if attached a_category as cat then
if attached a_term then
builder := builder.and_where ("category_id = ?", cat)
else
builder := builder.where ("category_id = ?", cat)
end
end
builder := builder.order_by ("name").limit (a_limit)
Result := builder.execute
end
Next Steps
- Transactions - Group operations atomically
- Soft Deletes - Use soft delete scopes
- API Reference - Full SELECT builder documentation