Fluent SQL builder library.
- Just start build
SQL
query fromSql
orPgSql
classes. - Use
Sqlify.Dapper
library allowingSqlify
andDapper
to be used together.
SELECT
,DELETE
,INSERT
,UPDATE
queriesWHERE
,JOIN
,ORDER BY
,GROUP BY
,HAVING BY
clausesLIKE
,EXISTS
,IN
,BETWEEN
predicatesCOUNT
,SUM
,MAX
,MIN
,AVG
,CAST
,COALESCE
,NULLIF
functionsUNION
queries- Multiple queries
- Table and Column aliases
- SQL injections free
- Partial
PostgreSQL
dialect support - Strongly typed (checked at compile time)
- GC friendly
// Create model for table Books with columns: Id, AuthorId, Rating
public interface IBooks : ITable
{
public Column<int> Id { get; }
public Column<int> AuthorId { get; }
public Column<double> Rating { get; }
}
// Create model for table Authors with columns: Id, Name
public interface IAuthors : ITable
{
public Column<int> Id { get; }
public Column<int> Name { get; }
}
var a = Sql.Table<IAuthors>("a");
var b = Sql.Table<IBooks>("b");
var selectQuery = Sql
.Select(a.Id, Sql.Count().As("Count"))
.From(a)
.Join(b, b.AuthorId == a.Id)
.Where(b.Rating > 2.0)
.Having(Sql.Count() >= 3)
.OrderByDesc(Sql.Count());
// SELECT a.Id, COUNT(*) AS Count
// FROM Authors a
// JOIN Books b ON b.AuthorId = a.Id
// WHERE b.Rating > @p1
// HAVING COUNT(*) >= @p2
// ORDER BY COUNT(*) DESC
-
SELECT
query -
DELETE
query -
INSERT
query -
UPDATE
query -
PostreSQL
dialectOFFSET
andLIMIT
clausesUPDATE RETURNING
clauseINSERT RETURNING
clauseDELETE RETURNING
clauseINSERT ON CONFLICT DO
clauseSELECT FOR
clause
As an example, consider the following database schema (authors
and books
tables with one-to-many relationship):
CREATE TABLE authors (
id integer PRIMARY KEY,
name varchar(64)
)
CREATE TABLE books (
id integer PRIMARY KEY,
name varchar(512),
author_id integer REFERENCES authors (id), -- one-to-many relationship
rating real,
qty integer
)
For these tables create corresponding interfaces:
[Table("authors")]
public interface IAuthorsTable : ITable
{
[Column("id")]
Column<int> Id { get; }
[Column("name")]
Column<string> Name { get; }
}
[Table("books")]
public interface IBooksTable : ITable
{
[Column("id")]
Column<int> Id { get; }
[Column("name")]
Column<string> Name { get; }
[Column("author_id")]
Column<int> AuthorId { get; }
[Column("rating")]
Column<double> Rating { get; }
[Column("qty")]
Column<int> Quantity { get; }
}
If the names of the columns in the database are the same as the names of the properties in models, then using TableAttribute
and ColumnAttrubute
are optional.
For example, for schema:
CREATE TABLE Authors (
Id integer PRIMARY KEY,
Name varchar(64),
BooksCount integer
)
you can define table like:
public interface IAuthorsTable : ITable
{
Column<int> Id { get; }
Column<int> Name { get; }
Column<int> BooksCount { get; }
}
var b = Sql.Table<IBooksTable>();
var query = Sql
.Select(b.Id, b.Name)
.From(b);
// SELECT books.id, books.name FROM books
var b = Sql.Table<IBooksTable>("t"); // table alias
var query = Sql
.Select(b.Id, b.Name)
.From(b);
// SELECT t.id, t.name FROM books t
var b = Sql.Table<IBooksTable>("t");
var query = Sql
.Select(b.Id, b.Name.As("author_name")) // column alias
.From(b);
// SELECT t.id, t.name AS author_name FROM books t
var b = Sql.Table<IBooksTable>();
var query = Sql
.Select(Sql.Count())
.From(b);
// SELECT COUNT(*) FROM books
var b = Sql.Table<IBooksTable>("b");
var query = Sql
.Select(Sql.Avg(b.Rating))
.From(b);
// SELECT AVG(b.rating) FROM books b
var b = Sql.Table<IBooksTable>("b");
var query = Sql
.Select(b.Rating.Cast<int>("INTEGER"))
.From(b);
// SELECT CAST(b.rating AS INTEGER) FROM books b
var b = Sql.Table<IBooksTable>("b");
var query = Sql
.Select(Sql.Coalesce(b.Name, "n/a"))
.From(b);
// SELECT COALESCE(b.name, @p1) FROM books b
var b = Sql.Table<IBooksTable>("b");
var query = Sql
.Select(Sql.NullIf(b.Name, "n/a"))
.From(b);
// SELECT NULLIF(b.name, @p1) FROM books b
Define a table orders
with computed column total
= qty
* price
:
[Table("orders")]
public interface IOrder : ITable
{
[Column("id")]
Column<int> Id { get; }
[Column("qty")]
Column<int> Qty { get; }
[Column("price")]
Column<int> Price { get; }
[Column("total")]
Expression<int> Total => Qty * Price; // <- computed column
}
var o = Sql.Table<IOrder>("o");
var query = Sql
.Select(o.Id, o.Total)
.From(o);
// SELECT o.id, o.qty * o.price AS total FROM orders o
var a = Sql.Table<IAuthorsTable>("a");
var query = Sql
.Select(a.Name)
.Distinct()
.From(a);
// SELECT DISTINCT a.name FROM authors a
var b = Sql.Table<IBooksTable>("b");
var query = Sql
.Select()
.From(b)
.Where(b.Name.IsNull.And(b.Rating <= 0));
// SELECT * FROM books b WHERE b.name IS NULL AND b.rating <= @p1
var b = Sql.Table<IBooksTable>("b");
var query = Sql
.Select()
.From(b)
.Where(
b.Name.IsNull,
b.Rating <= 0
);
// SELECT * FROM books b WHERE b.name IS NULL AND b.rating <= @p1
var a = Sql.Table<IAuthorsTable>("a");
var query = Sql
.Select()
.From(a)
.Where(a.Name.Like("A%")); // started with 'A'
// SELECT * FROM authors a WHERE a.name LIKE @p1
var a = Sql.Table<IAuthorsTable>("a");
var query = Sql
.Select()
.From(a)
.Where(a.Id.In(new[] {1, 2})); // where id==1 OR id==2
// SELECT * FROM authors a WHERE a.id IN @p1
var a = Sql.Table<IAuthorsTable>("a");
var b = Sql.Table<IBooksTable>("b");
var subQuery = Sql
.Select(b.AuthorId)
.From(b)
.Where(b.Rating > 3);
var query = Sql
.Select()
.From(a)
.Where(a.Id.In(subQuery)); // IN sub-query
// SELECT * FROM authors a WHERE a.id IN (SELECT b.author_id FROM books b WHERE b.rating > @p1)");
var a = Sql.Table<IAuthorsTable>("a");
var b = Sql.Table<IBooksTable>("b");
var subQuery = Sql
.Select()
.From(b)
.Where((a.Id == b.AuthorId).And(b.Rating > 3));
var query = Sql
.Select()
.From(a)
.WhereExists(subQuery);
// SELECT * FROM authors a WHERE EXISTS (SELECT * FROM books b WHERE a.id = b.author_id AND b.rating > @p1
var b = Sql.Table<IBooksTable>("b");
var query = Sql
.Select()
.From(b)
.Where(b.Rating.Between(2, 4));
// SELECT * FROM books b WHERE b.rating BETWEEN @p1 AND @p2
var a = Sql.Table<IAuthorsTable>("a");
var b = Sql.Table<IBooksTable>("b");
var query = Sql
.Select()
.Join(b, a.Id == b.AuthorId) // also LEFT, RIGHT, FULL JOIN
.From(a);
// SELECT * FROM authors a JOIN books b ON a.id = b.author_id
var b = Sql.Table<IBooksTable>("b");
var query = Sql
.Select()
.OrderByDesc(b.Rating)
.From(b);
// SELECT * FROM books b ORDER BY b.rating DESC
var b = Sql.Table<IBooksTable>("b");
var query = Sql
.Select(b.AuthorId, Sql.Count())
.GroupBy(b.AuthorId)
.From(b);
// SELECT b.author_id, COUNT(*) FROM books b GROUP BY b.author_id
var a = Sql.Table<IAuthorsTable>();
var b = Sql.Table<IBooksTable>();
MultipleQuery query = Sql
.Multiple(
Sql.Select().From(a),
Sql.Select().From(b)
);
// SELECT * FROM authors; SELECT * FROM books
var b = Sql.Table<IBooksTable>("b");
var query = Sql
.Select(b.AuthorId, Sql.Count())
.GroupBy(b.AuthorId)
.Having(Sql.Count() > 3)
.From(b);
// SELECT b.author_id, COUNT(*) FROM books b GROUP BY b.author_id HAVING COUNT(*) > @p1
var b = Sql.Table<IBooksTable>();
var query = Sql
.Delete(b)
.Where(b.Id == 1);
// DELETE FROM books WHERE books.id = @p1
var a = Sql.Table<IAuthorsTable>();
var query = Sql
.Insert(a)
.Values(a.Id, 1)
.Values(a.Name, "Adam");
// INSERT INTO authors (id, name) VALUES (@p1, @p2)
var b = Sql.Table<IBooksTable>();
var query = Sql
.Update(b)
.Set(b.Rating, b.Rating + 1)
.Where(b.AuthorId == 1);
// UPDATE books SET rating = books.rating + @p1 WHERE books.author_id = @p2
var a = Sql.Table<IAuthorsTable>("a");
PgSelectQuery query = PgSql
.Select()
.From(a)
.OrderBy(a.Name)
.Offset(5)
.Limit(10)
// SELECT * FROM authors a ORDER BY a.name OFFSET @p1 LIMIT @p2
var b = Sql.Table<IBooksTable>();
PgUpdateQuery query = PgSql
.Update(b)
.Set(b.Rating, b.Rating + 1)
.Returning(b.Id, b.Rating);
// UPDATE books SET rating = books.rating + @p1 RETURNING books.id, books.rating
var b = Sql.Table<IBooksTable>();
PgInsertQuery query = PgSql
.Insert(b)
.Values(b.Name, "name")
.Returning();
// INSERT INTO books (name) VALUES (@p1) RETURNING *
var b = Sql.Table<IBooksTable>();
PgInsertQuery query = PgSql
.Delete(b)
.Returning();
// DELETE FROM books RETURNING *
var b = Sql.Table<IBooksTable>("b");
PgInsertQuery query = PgSql
.Insert(b)
.Values(b.Id, 1)
.Values(b.Name, "foo bar")
.Values(b.Quantity, 5)
.OnConflict(
PgConflict.Columns(b.Name),
PgConflict
.DoUpdate()
.Set(b.Quantity, b.Quantity + 5)
);
// INSERT INTO books AS b (id, name, qty) VALUES (@p1, @p2, @p3)
// ON CONFLICT (b.name)
// DO UPDATE SET qty = b.qty + @p4"
var b = Sql.Table<IBooksTable>("b");
PgSelectQuery query = PgSql
.Select()
.From(b)
.Where(b.Id == 3)
.For(PgLockMode.Update); // mode: UPDATE, NO KEY UPDATE, SHARE, KEY SHARE
// SELECT * FROM books b WHERE b.id = @p1 FOR UPDATE
# build
dotnet build ./src
# running tests
dotnet test ./src
# pack
dotnet pack ./src -c=release