sql"…" — Plantilla Segura y Scalar
En esta página
El literal sql"..." es una forma más expresiva de construir queries dinámicas.
Cualquier {valor} dentro de la plantilla se compila como ? y se vincula
automáticamente — escribes la query como si fuera una cadena interpolada, pero
con total seguridad contra inyección.
El resultado es un TaggedSqlQuery con cuatro métodos:
| Método | Qué hace |
|---|---|
.query(db) |
Ejecuta SELECT, devuelve lista de maps |
.execute(db) |
Ejecuta DDL/DML, devuelve filas afectadas |
.scalar(db) |
Devuelve el primer valor de la primera fila |
.one(db) |
Devuelve la primera fila como map |
Filtra libros por año con {min_year} y usa .scalar para contar el total.
// Feature: Database — `sql"..."` tagged template with auto-parametrization
// Syntax: `sql"SELECT ... WHERE x = {value}"` — interpolations become `?`
// and the values are bound. Returns a TaggedSqlQuery with methods
// `:query(db)`, `:execute(db)`, `:one(db)`, `:scalar(db)`.
// When to use: dynamic, safe queries without string concatenation.
use std::database::Database
let db = Database.open("sqlite://:memory:").unwrap()
defer db.close()
db.execute(sql"CREATE TABLE books (id INTEGER PRIMARY KEY, title TEXT, year INTEGER)").unwrap()
db.execute(sql"INSERT INTO books VALUES (1, 'Zolo Guide', 2025)").unwrap()
db.execute(sql"INSERT INTO books VALUES (2, 'Lua in 24h', 2010)").unwrap()
db.execute(sql"INSERT INTO books VALUES (3, 'Rust Book', 2020)").unwrap()
const min_year = 2020
// `{min_year}` compiles to `?` + binding — safe against injection.
let q = sql"SELECT title, year FROM books WHERE year >= {min_year} ORDER BY year"
let rows = q.query(db).unwrap()
for row in rows {
print(" {row.title} ({row.year})")
}
// expected:
// Rust Book (2020)
// Zolo Guide (2025)
// `:scalar` returns the first value of the first column.
let total = sql#"SELECT COUNT(*) "total" FROM books"#.scalar(db).unwrap()
print("total: {total}")
// expected: total: 3
Requiere la CLI o el host de Zolo; ábrelo en el playground o ejecútalo localmente.
.scalar es el atajo ideal para agregaciones (COUNT, MAX, SUM) y
consultas que devuelven exactamente una celda:
Cuenta SKUs por valor con sql"...{target}".scalar(db) — el {target} es
vinculado, no concatenado.
// Feature: `sql"...".scalar(db)` — first column of first row, as a value
// Syntax: returns the value directly (number/string/bool/nil) rather
// than a row object.
// When to use: COUNT(*), MAX/MIN/SUM aggregates, SELECT 1, single
// scalar lookups by primary key — anywhere the result is exactly
// one cell.
use std::database::Database
let db = Database.open("sqlite://:memory:").unwrap()
defer db.close()
db.execute(sql"CREATE TABLE items (id INTEGER PRIMARY KEY, sku TEXT)").unwrap()
db.execute(sql"INSERT INTO items VALUES (1, 'a')").unwrap()
db.execute(sql"INSERT INTO items VALUES (2, 'b')").unwrap()
db.execute(sql"INSERT INTO items VALUES (3, 'a')").unwrap()
// Interpolated value is bound, not concatenated — safe.
let target = "a"
let n = sql"SELECT COUNT(*) FROM items WHERE sku = {target}".scalar(db).unwrap()
print("count: {n}")
// expected: count: 2
// `scalar` also works without interpolation.
let total = sql"SELECT COUNT(*) FROM items".scalar(db).unwrap()
print("total: {total}")
// expected: total: 3
Requiere la CLI o el host de Zolo; ábrelo en el playground o ejecútalo localmente.
Desafío
Usa sql"..." para calcular el precio medio con AVG(price) y muestra el
resultado formateado con dos decimales.
Consulta también