Ir al contenido principal

SqlQueryBuilder

SqlQueryBuilder<T> es el punto de entrada principal para construir consultas SELECT completas. Es una clase sellada genérica — T es el tipo de entidad cuyas propiedades están disponibles como columnas tipadas.

var result = new SqlQueryBuilder<Order>(new SqlServerDialect())
.From("Orders", schema: "dbo")
.Select(x => x.Id, x => x.Total)
.Where(x => x.Total > 100)
.OrderBy(x => x.CreatedAt, ascending: false)
.Page(1, 20)
.Build();

SELECT​

Select — múltiples columnas​

Firma

SqlQueryBuilder<T> Select(params Expression<Func<T, object>>[] columns)

Descripción Agrega una o más columnas tipadas a la lista SELECT. Si nunca se llama, la consulta utiliza SELECT * por defecto.

Ejemplo

builder.Select(x => x.Id, x => x.Name);
// → SELECT [Id], [Name] FROM ...

Select — columna única con alias​

Firma

SqlQueryBuilder<T> Select(Expression<Func<T, object>> column, string? alias)

Descripción Agrega una columna tipada con alias opcional.

Ejemplo

builder.Select(x => x.Name, "UserName");
// → SELECT [Name] AS [UserName] FROM ...

SelectAll​

Firma

SqlQueryBuilder<T> SelectAll()

Descripción Restablece la lista de columnas a SELECT *, limpiando cualquier columna previamente agregada.


SelectRaw​

Firma

SqlQueryBuilder<T> SelectRaw(string rawSql)

Descripción Agrega un fragmento SQL literal a la lista SELECT. Útil para expresiones que no se pueden representar mediante columnas tipadas.

Ejemplo

builder.SelectRaw("GETDATE() AS [Now]");
// → SELECT GETDATE() AS [Now] FROM ...

SelectRawIf​

Firma

SqlQueryBuilder<T> SelectRawIf(bool condition, string rawSql)

Descripción Agrega una expresión SELECT literal solo cuando condition es true.


SelectIf​

Firma

SqlQueryBuilder<T> SelectIf(bool condition, Expression<Func<T, object>> column, string? alias = null)

Descripción Agrega una columna tipada solo cuando condition es true.


SelectCount — estrella​

Firma

SqlQueryBuilder<T> SelectCount(string? alias = null)

Descripción Agrega COUNT(*) a la lista SELECT.

Ejemplo

builder.SelectCount("total");
// → SELECT COUNT(*) AS [total] FROM ...

SelectCount — columna​

Firma

SqlQueryBuilder<T> SelectCount(Expression<Func<T, object>> column, string? alias = null)

Descripción Agrega COUNT([col]) a la lista SELECT.


SelectCountDistinct​

Firma

SqlQueryBuilder<T> SelectCountDistinct(Expression<Func<T, object>> column, string? alias = null)

Descripción Agrega COUNT(DISTINCT [col]) a la lista SELECT.


SelectSum​

Firma

SqlQueryBuilder<T> SelectSum(Expression<Func<T, object>> column, string? alias = null)

Descripción Agrega SUM([col]) a la lista SELECT.


SelectAvg​

Firma

SqlQueryBuilder<T> SelectAvg(Expression<Func<T, object>> column, string? alias = null)

Descripción Agrega AVG([col]) a la lista SELECT.


SelectMin​

Firma

SqlQueryBuilder<T> SelectMin(Expression<Func<T, object>> column, string? alias = null)

Descripción Agrega MIN([col]) a la lista SELECT.


SelectMax​

Firma

SqlQueryBuilder<T> SelectMax(Expression<Func<T, object>> column, string? alias = null)

Descripción Agrega MAX([col]) a la lista SELECT.


SelectCase​

Firma

SqlQueryBuilder<T> SelectCase(CaseWhenBuilder caseWhen)

Descripción Agrega una expresión CASE WHEN ... THEN ... ELSE ... END a la lista SELECT, construida mediante CaseWhenBuilder.

Ejemplo

var caseExpr = new CaseWhenBuilder()
.When("[Status] = 1", "Activo")
.When("[Status] = 2", "Inactivo")
.Else("Desconocido")
.As("EstadoLabel");

builder.SelectCase(caseExpr);
// → SELECT CASE WHEN [Status] = 1 THEN 'Activo' WHEN [Status] = 2 THEN 'Inactivo' ELSE 'Desconocido' END AS [EstadoLabel]

SelectCoalesce​

Firma

SqlQueryBuilder<T> SelectCoalesce(
Expression<Func<T, object>> column, string fallbackSql, string alias)

Descripción Agrega COALESCE([col], fallbackSql) AS [alias] a la lista SELECT.

Ejemplo

builder.SelectCoalesce(x => x.Department, "'Sin Dept'", "Departamento");
// → SELECT COALESCE([Department], 'Sin Dept') AS [Departamento] FROM ...

SelectCast​

Firma

SqlQueryBuilder<T> SelectCast(
Expression<Func<T, object>> column, string typeName, string alias)

Descripción Agrega CAST([col] AS typeName) AS [alias] a la lista SELECT.

Ejemplo

builder.SelectCast(x => x.Age, "FLOAT", "EdadDecimal");
// → SELECT CAST([Age] AS FLOAT) AS [EdadDecimal] FROM ...

SelectConcat​

Firma

SqlQueryBuilder<T> SelectConcat(string alias, params Expression<Func<T, object>>[] columns)

Descripción Agrega una expresión de concatenación de cadenas adaptada al dialecto. El operador varía por dialecto:

  • SQL Server: [col1] + [col2]
  • PostgreSQL / SQLite: "col1" || "col2"
  • MySQL: CONCAT(`col1`, `col2`)

Ejemplo

builder.SelectConcat("NombreCompleto", x => x.FirstName, x => x.LastName);
// SQL Server → SELECT [FirstName] + [LastName] AS [NombreCompleto]
// PostgreSQL → SELECT "FirstName" || "LastName" AS "NombreCompleto"

Funciones de Ventana​

SelectRowNumber​

Firma

SqlQueryBuilder<T> SelectRowNumber(
Expression<Func<T, object>>? partitionBy,
Expression<Func<T, object>> orderBy,
bool ascending = true,
string? alias = "RowNum")

// Sobrecarga multi-partición
SqlQueryBuilder<T> SelectRowNumber(
IReadOnlyList<Expression<Func<T, object>>> partitionBy,
Expression<Func<T, object>> orderBy,
bool ascending = true,
string? alias = "RowNum")

Descripción Agrega ROW_NUMBER() OVER (PARTITION BY [col] ORDER BY [col] ASC|DESC) AS [alias]. Pasa null como partitionBy para omitir la cláusula PARTITION BY.

Ejemplo

builder.SelectRowNumber(x => x.Department, x => x.Salary, ascending: false, alias: "RangoSalario");
// → SELECT ROW_NUMBER() OVER (PARTITION BY [Department] ORDER BY [Salary] DESC) AS [RangoSalario]

SelectRank​

Firma

SqlQueryBuilder<T> SelectRank(
Expression<Func<T, object>>? partitionBy,
Expression<Func<T, object>> orderBy,
bool ascending = true,
string? alias = "Rank")

Descripción Agrega RANK() OVER (...). Mismo comportamiento de saltos que el RANK estándar de SQL.


SelectDenseRank​

Firma

SqlQueryBuilder<T> SelectDenseRank(
Expression<Func<T, object>>? partitionBy,
Expression<Func<T, object>> orderBy,
bool ascending = true,
string? alias = "DenseRank")

Descripción Agrega DENSE_RANK() OVER (...). Sin saltos en los valores de ranking.


SelectLag​

Firma

SqlQueryBuilder<T> SelectLag(
Expression<Func<T, object>> column,
int offset,
Expression<Func<T, object>>? partitionBy,
Expression<Func<T, object>> orderBy,
bool ascending = true,
string? alias = null)

Descripción Agrega LAG([col], offset) OVER ([PARTITION BY ...] ORDER BY ...). Recupera el valor de una fila anterior dentro de la partición.

Ejemplo

builder.SelectLag(x => x.Price, 1, null, x => x.CreatedAt, alias: "PrecioAnterior");
// → SELECT LAG([Price], 1) OVER (ORDER BY [CreatedAt] ASC) AS [PrecioAnterior]

SelectLead​

Firma

SqlQueryBuilder<T> SelectLead(
Expression<Func<T, object>> column,
int offset,
Expression<Func<T, object>>? partitionBy,
Expression<Func<T, object>> orderBy,
bool ascending = true,
string? alias = null)

Descripción Agrega LEAD([col], offset) OVER (...). Recupera el valor de una fila siguiente dentro de la partición.


SelectFirstValue​

Firma

SqlQueryBuilder<T> SelectFirstValue(
Expression<Func<T, object>> column,
Expression<Func<T, object>>? partitionBy,
Expression<Func<T, object>> orderBy,
bool ascending = true,
string? alias = null)

Descripción Agrega FIRST_VALUE([col]) OVER (...). Recupera el primer valor en el marco de ventana ordenado.


SelectLastValue​

Firma

SqlQueryBuilder<T> SelectLastValue(
Expression<Func<T, object>> column,
Expression<Func<T, object>>? partitionBy,
Expression<Func<T, object>> orderBy,
bool ascending = true,
string? alias = null)

Descripción Agrega LAST_VALUE([col]) OVER (...). Recupera el último valor en el marco de ventana ordenado.


SelectSumOver​

Firma

SqlQueryBuilder<T> SelectSumOver(
Expression<Func<T, object>> column,
Expression<Func<T, object>>? partitionBy,
Expression<Func<T, object>>? orderBy,
bool ascending = true,
string? alias = null)

Descripción Agrega SUM([col]) OVER ([PARTITION BY ...] [ORDER BY ...]). Útil para totales acumulados. El alias por defecto es Running{NombreColumna}.

Ejemplo

builder.SelectSumOver(x => x.Amount, x => x.CustomerId, x => x.CreatedAt, alias: "TotalAcumulado");
// → SELECT SUM([Amount]) OVER (PARTITION BY [CustomerId] ORDER BY [CreatedAt] ASC) AS [TotalAcumulado]

SelectAvgOver​

Firma

SqlQueryBuilder<T> SelectAvgOver(
Expression<Func<T, object>> column,
Expression<Func<T, object>>? partitionBy,
Expression<Func<T, object>>? orderBy,
bool ascending = true,
string? alias = null)

Descripción Agrega AVG([col]) OVER (...).


SelectCountOver​

Firma

SqlQueryBuilder<T> SelectCountOver(
Expression<Func<T, object>> column,
Expression<Func<T, object>>? partitionBy,
Expression<Func<T, object>>? orderBy,
bool ascending = true,
string? alias = null)

Descripción Agrega COUNT([col]) OVER (...).


SelectMinOver​

Firma

SqlQueryBuilder<T> SelectMinOver(
Expression<Func<T, object>> column,
Expression<Func<T, object>>? partitionBy,
Expression<Func<T, object>>? orderBy,
bool ascending = true,
string? alias = null)

Descripción Agrega MIN([col]) OVER (...).


SelectMaxOver​

Firma

SqlQueryBuilder<T> SelectMaxOver(
Expression<Func<T, object>> column,
Expression<Func<T, object>>? partitionBy,
Expression<Func<T, object>>? orderBy,
bool ascending = true,
string? alias = null)

Descripción Agrega MAX([col]) OVER (...).


SelectWindowRaw​

Firma

SqlQueryBuilder<T> SelectWindowRaw(string windowExpression, string alias)

Descripción Agrega una expresión de función de ventana literal al SELECT. Útil cuando las sobrecargas tipadas no cubren tu caso.

Ejemplo

builder.SelectWindowRaw("NTILE(4) OVER (ORDER BY [Score] DESC)", "Cuartil");
// → SELECT NTILE(4) OVER (ORDER BY [Score] DESC) AS [Cuartil]

FROM​

From — nombre de tabla​

Firma

SqlQueryBuilder<T> From(string tableName, string? schema = null)

Descripción Establece la tabla FROM. Si no se llama, toma typeof(T).Name como valor por defecto. El esquema es opcional.

Ejemplo

builder.From("Users", schema: "dbo");
// SQL Server → FROM [dbo].[Users]
// PostgreSQL → FROM "dbo"."Users"

From — subconsulta​

Firma

SqlQueryBuilder<T> From(SqlQueryResult subquery, string alias)

Descripción Usa un SqlQueryResult precompilado como fuente FROM: FROM (subquery) alias.

Ejemplo

var inner = new SqlQueryBuilder<User>(dialect)
.From("Users")
.Where(x => x.IsActive)
.Build();

new SqlQueryBuilder<User>(dialect)
.From(inner, "activos")
.Select(x => x.Name)
.Build();
// → SELECT "Name" FROM (SELECT * FROM "Users" WHERE "IsActive" = true) activos

JOINs​

Todos los métodos de join aceptan un nombre de tabla (entre comillas según el dialecto), un alias opcional y una condición ON como string literal.

InnerJoin​

Firma

SqlQueryBuilder<T> InnerJoin(string table, string? alias, string on)

Ejemplo

builder.From("Users").InnerJoin("Orders", "o", "[Users].[Id] = [o].[UserId]");
// → FROM [Users] INNER JOIN [Orders] o ON [Users].[Id] = [o].[UserId]

LeftJoin​

Firma

SqlQueryBuilder<T> LeftJoin(string table, string? alias, string on)

Descripción Agrega una cláusula LEFT JOIN.


RightJoin​

Firma

SqlQueryBuilder<T> RightJoin(string table, string? alias, string on)

Descripción Agrega una cláusula RIGHT JOIN.


FullOuterJoin​

Firma

SqlQueryBuilder<T> FullOuterJoin(string table, string? alias, string on)

Descripción Agrega una cláusula FULL OUTER JOIN.


CrossJoin​

Firma

SqlQueryBuilder<T> CrossJoin(string table, string? alias = null)

Descripción Agrega una cláusula CROSS JOIN. No requiere condición ON.


InnerJoinSubquery​

Firma

SqlQueryBuilder<T> InnerJoinSubquery(SqlQueryResult subquery, string alias, string on)

Descripción Agrega un INNER JOIN contra una tabla derivada (subconsulta). Los parámetros de la subconsulta se renombran automáticamente para evitar colisiones.

Ejemplo

var sub = new SqlQueryBuilder<Order>(dialect)
.From("Orders")
.Select(x => x.UserId)
.SelectSum(x => x.Total, "TotalGastado")
.GroupBy(x => x.UserId)
.Build();

builder.From("Users").InnerJoinSubquery(sub, "totales", "[Users].[Id] = [totales].[UserId]");
// → FROM [Users] INNER JOIN (SELECT [UserId], SUM([Total]) AS [TotalGastado] FROM [Orders] GROUP BY [UserId]) [totales] ON [Users].[Id] = [totales].[UserId]

LeftJoinSubquery​

Firma

SqlQueryBuilder<T> LeftJoinSubquery(SqlQueryResult subquery, string alias, string on)

Descripción Agrega un LEFT JOIN contra una tabla derivada (subconsulta).


RightJoinSubquery​

Firma

SqlQueryBuilder<T> RightJoinSubquery(SqlQueryResult subquery, string alias, string on)

Descripción Agrega un RIGHT JOIN contra una tabla derivada (subconsulta).


FullOuterJoinSubquery​

Firma

SqlQueryBuilder<T> FullOuterJoinSubquery(SqlQueryResult subquery, string alias, string on)

Descripción Agrega un FULL OUTER JOIN contra una tabla derivada (subconsulta).


WHERE​

Múltiples llamadas WHERE se combinan con AND. La sobrecarga de ValiFlow<T> / Expression y la de SqlWhereBuilder usan prefijos de nombre de parámetro diferentes (p vs pw) y pueden coexistir sin conflictos.

Where — ValiFlow​

Firma

SqlQueryBuilder<T> Where(ValiFlow<T> filter)

Descripción Establece la cláusula WHERE a partir de un filtro ValiFlow<T>. Los parámetros usan el prefijo p.

Ejemplo

var filter = new ValiFlow<User>().EqualTo(x => x.IsActive, true).GreaterThan(x => x.Age, 18);
builder.From("Users").Where(filter);
// → WHERE [IsActive] = @p0 AND [Age] > @p1

Where — Expression​

Firma

SqlQueryBuilder<T> Where(Expression<Func<T, bool>> predicate)

Descripción Establece la cláusula WHERE a partir de una expresión lambda. Los parámetros usan el prefijo p.

Nota: Llamar tanto a Where(ValiFlow<T>) como a Where(Expression<...>) no está soportado — la segunda llamada sobreescribe la primera. Usa solo un predicado tipado; combínalo con Where(SqlWhereBuilder) para condiciones compuestas.


Where — SqlWhereBuilder (instancia)​

Firma

SqlQueryBuilder<T> Where(SqlWhereBuilder<T> whereBuilder)

Descripción Establece la cláusula WHERE a partir de un SqlWhereBuilder<T> preconfigurado. Los parámetros usan el prefijo pw. Puede combinarse con la sobrecarga de predicado tipado.


Where — SqlWhereBuilder (inline)​

Firma

SqlQueryBuilder<T> Where(Action<SqlWhereBuilder<T>> configure)

Descripción Configura la cláusula WHERE de forma inline usando una acción SqlWhereBuilder<T>.

Ejemplo

builder.Where(w => w.EqualTo(x => x.IsActive, true).GreaterThan(x => x.Age, 18));
// → WHERE [IsActive] = @pw0 AND [Age] > @pw1

WhereRaw​

Firma

SqlQueryBuilder<T> WhereRaw(string rawSql)

Descripción Agrega un fragmento SQL literal a la cláusula WHERE (combinado con AND). Los parámetros en el fragmento son responsabilidad del caller.

Ejemplo

builder.WhereRaw("[CreatedAt] > DATEADD(day, -30, GETDATE())");
// → WHERE [CreatedAt] > DATEADD(day, -30, GETDATE())

WhereExists​

Firma

SqlQueryBuilder<T> WhereExists(SqlQueryResult subquery)

Descripción Agrega EXISTS (subquery) a la cláusula WHERE. Los parámetros de la subconsulta se fusionan y renombran automáticamente.

Ejemplo

var sub = new SqlQueryBuilder<Order>(dialect)
.From("Orders")
.WhereRaw("[Orders].[UserId] = [Users].[Id]")
.Build();

builder.From("Users").WhereExists(sub);
// → WHERE EXISTS (SELECT * FROM [Orders] WHERE [Orders].[UserId] = [Users].[Id])

WhereNotExists​

Firma

SqlQueryBuilder<T> WhereNotExists(SqlQueryResult subquery)

Descripción Agrega NOT EXISTS (subquery) a la cláusula WHERE.


WhereInSubquery​

Firma

SqlQueryBuilder<T> WhereInSubquery<TValue>(Expression<Func<T, TValue>> column, SqlQueryResult subquery)

Descripción Agrega [col] IN (subquery) a la cláusula WHERE. Los parámetros de la subconsulta se renombran automáticamente.

Ejemplo

var sub = new SqlQueryBuilder<Order>(dialect)
.From("Orders")
.Select(x => x.UserId)
.Build();

builder.From("Users").WhereInSubquery(x => x.Id, sub);
// → WHERE [Id] IN (SELECT [UserId] FROM [Orders])

WhereNotInSubquery​

Firma

SqlQueryBuilder<T> WhereNotInSubquery<TValue>(Expression<Func<T, TValue>> column, SqlQueryResult subquery)

Descripción Agrega [col] NOT IN (subquery) a la cláusula WHERE.


WhereIf — sobrecarga builder​

Firma

SqlQueryBuilder<T> WhereIf(bool condition, Action<SqlWhereBuilder<T>> configure)

Descripción Aplica una cláusula WHERE con SqlWhereBuilder solo cuando condition es true.


WhereIf — sobrecarga expression​

Firma

SqlQueryBuilder<T> WhereIf(bool condition, Expression<Func<T, bool>> predicate)

Descripción Aplica un predicado WHERE lambda solo cuando condition es true.


GROUP BY / HAVING​

GroupBy​

Firma

SqlQueryBuilder<T> GroupBy(params Expression<Func<T, object>>[] columns)

Descripción Agrega columnas GROUP BY.

Ejemplo

builder.GroupBy(x => x.Department, x => x.Status);
// → GROUP BY [Department], [Status]

GroupByRaw​

Firma

SqlQueryBuilder<T> GroupByRaw(string rawSql)

Descripción Agrega un fragmento SQL literal a la cláusula GROUP BY. Útil para consultas multi-tabla donde se necesitan nombres de columna calificados con la tabla.

Ejemplo

builder.GroupByRaw("[Products].[Id], [Products].[Name]");

GroupByIf​

Firma

SqlQueryBuilder<T> GroupByIf(bool condition, Expression<Func<T, object>> column)

Descripción Agrega una columna GROUP BY solo cuando condition es true.


Having — string literal​

Firma

SqlQueryBuilder<T> Having(string rawHavingSql)

Descripción Establece una cláusula HAVING literal. Se usa después de GroupBy.

Ejemplo

builder.GroupBy(x => x.Department).Having("COUNT(*) > 5");
// → GROUP BY [Department] HAVING COUNT(*) > 5

Having — SqlHavingBuilder (instancia)​

Firma

SqlQueryBuilder<T> Having(SqlHavingBuilder<T> havingBuilder)

Descripción Establece la cláusula HAVING a partir de un SqlHavingBuilder<T>. Puede combinarse con la sobrecarga de string literal — ambas se unen con AND.


Having — SqlHavingBuilder (inline)​

Firma

SqlQueryBuilder<T> Having(Action<SqlHavingBuilder<T>> configure)

Descripción Configura la cláusula HAVING de forma inline.

Ejemplo

builder.Having(h => h.CountGreaterThan(5).SumGreaterThan(x => x.Amount, 1000m));

HavingIf — sobrecarga builder​

Firma

SqlQueryBuilder<T> HavingIf(bool condition, Action<SqlHavingBuilder<T>> configure)

Descripción Aplica una cláusula HAVING fluida solo cuando condition es true.


HavingIf — sobrecarga string literal​

Firma

SqlQueryBuilder<T> HavingIf(bool condition, string rawHavingSql)

Descripción Aplica una cláusula HAVING literal solo cuando condition es true.


ORDER BY​

OrderBy​

Firma

SqlQueryBuilder<T> OrderBy(Expression<Func<T, object>> column, bool ascending = true)

Descripción Agrega una columna ORDER BY principal.

Ejemplo

builder.OrderBy(x => x.CreatedAt, ascending: false);
// → ORDER BY [CreatedAt] DESC

OrderBy — con NullsOrder​

Firma

SqlQueryBuilder<T> OrderBy(Expression<Func<T, object>> column, bool ascending, NullsOrder nulls)

Descripción Agrega ORDER BY con NULLS FIRST / NULLS LAST opcional. El parámetro nulls se ignora silenciosamente en dialectos que no lo soportan (SQL Server, MySQL).

Ejemplo

builder.OrderBy(x => x.DeletedAt, ascending: true, NullsOrder.Last);
// PostgreSQL → ORDER BY "DeletedAt" ASC NULLS LAST
// SQL Server → ORDER BY [DeletedAt] ASC (cláusula nulls omitida)

ThenBy​

Firma

SqlQueryBuilder<T> ThenBy(Expression<Func<T, object>> column, bool ascending = true)

Descripción Agrega una columna ORDER BY secundaria. Funcionalmente equivalente a una segunda llamada a OrderBy.


OrderByIf​

Firma

SqlQueryBuilder<T> OrderByIf(bool condition, Expression<Func<T, object>> column, bool ascending = true)

Descripción Aplica ORDER BY solo cuando condition es true.


OrderByRaw​

Firma

SqlQueryBuilder<T> OrderByRaw(string rawSql)

Descripción Agrega una expresión ORDER BY literal textualmente. Útil para expresiones de ordenamiento multi-tabla o calculadas.

Ejemplo

builder.OrderByRaw("[Products].[Price] DESC, [Name] ASC");

Paginación​

Take​

Firma

SqlQueryBuilder<T> Take(int count)

Descripción Limita el número de filas devueltas. Se mapea a TOP N (SQL Server sin OFFSET) o LIMIT N.

Ejemplo

builder.From("Users").Take(10);
// SQL Server → SELECT TOP 10 * FROM [Users]
// PostgreSQL → SELECT * FROM "Users" LIMIT 10

Skip​

Firma

SqlQueryBuilder<T> Skip(int count)

Descripción Omite el número de filas especificado. Se mapea a OFFSET en todos los dialectos.

Ejemplo

builder.From("Users").OrderBy(x => x.Id).Skip(20).Take(10);
// SQL Server → SELECT * FROM [Users] ORDER BY [Id] ASC OFFSET 20 ROWS FETCH NEXT 10 ROWS ONLY
// PostgreSQL → SELECT * FROM "Users" ORDER BY "Id" ASC LIMIT 10 OFFSET 20

Nota: SQL Server requiere una cláusula ORDER BY cuando se usa OFFSET.


Page​

Firma

SqlQueryBuilder<T> Page(int pageNumber, int pageSize)

Descripción Establece paginación basada en número de página (base 1). Page(2, 20) equivale a Skip(20).Take(20).

Ejemplo

builder.From("Users").OrderBy(x => x.Id).Page(3, 25);
// SQL Server → OFFSET 50 ROWS FETCH NEXT 25 ROWS ONLY

DISTINCT / WithHint​

Distinct​

Firma

SqlQueryBuilder<T> Distinct()

Descripción Agrega DISTINCT a la cláusula SELECT.

Ejemplo

builder.From("Orders").Select(x => x.CustomerId).Distinct();
// → SELECT DISTINCT [CustomerId] FROM [Orders]

WithHint​

Firma

SqlQueryBuilder<T> WithHint(string hint)

Descripción Agrega un table hint después del FROM. Principalmente usado con SQL Server para hints como NOLOCK.

Ejemplo

builder.From("Users").WithHint("NOLOCK");
// SQL Server → FROM [Users] WITH (NOLOCK)

Nota: Este método genera el hint para todos los dialectos. Úsalo únicamente con SQL Server — otros dialectos incluirán un fragmento WITH (...) inválido.


Operaciones de Conjunto​

Union​

Firma

SqlQueryBuilder<T> Union(SqlQueryResult other)

Descripción Agrega un UNION (eliminando duplicados) con una consulta precompilada.

Ejemplo

builder.From("ActiveUsers").Union(
new SqlQueryBuilder<User>(dialect).From("InactiveUsers").Build());
// → SELECT * FROM [ActiveUsers] UNION SELECT * FROM [InactiveUsers]

UnionAll​

Firma

SqlQueryBuilder<T> UnionAll(SqlQueryResult other)

Descripción Agrega un UNION ALL (incluyendo duplicados) con una consulta precompilada.


Except​

Firma

SqlQueryBuilder<T> Except(SqlQueryResult other)

Descripción Agrega EXCEPT — retorna filas de la consulta principal que no están en la otra consulta.


Intersect​

Firma

SqlQueryBuilder<T> Intersect(SqlQueryResult other)

Descripción Agrega INTERSECT — retorna solo las filas que aparecen en ambas consultas.


CTEs​

WithCte — SqlQueryResult​

Firma

SqlQueryBuilder<T> WithCte(string name, SqlQueryResult cteQuery)

Descripción Antepone una Expresión de Tabla Común: WITH nombre AS (cteQuery). Múltiples llamadas agregan múltiples CTEs.

Ejemplo

var activosCte = new SqlQueryBuilder<User>(dialect)
.From("Users")
.Where(w => w.EqualTo(x => x.IsActive, true))
.Build();

new SqlQueryBuilder<User>(dialect)
.WithCte("UsuariosActivos", activosCte)
.From("UsuariosActivos")
.Build();
// → WITH "UsuariosActivos" AS (SELECT * FROM "Users" WHERE ...) SELECT * FROM "UsuariosActivos"

WithCte — inline​

Firma

SqlQueryBuilder<T> WithCte(string name, Action<SqlQueryBuilder<T>> configure)

Descripción Antepone un CTE definido de forma inline. Crea un SqlQueryBuilder<T> anidado internamente.


WithRecursive​

Firma

SqlQueryBuilder<T> WithRecursive(string name, SqlQueryResult anchor, SqlQueryResult recursive)

Descripción Agrega un CTE recursivo. Genera: WITH [RECURSIVE] nombre AS (anchor UNION ALL recursive). La palabra clave RECURSIVE se agrega automáticamente para dialectos que la requieren (PostgreSQL, SQLite, MySQL).

Ejemplo

var anchor = new SqlQueryBuilder<Category>(dialect).From("Categories").Where(x => x.ParentId == null).Build();
var rec = new SqlQueryBuilder<Category>(dialect).From("Categories").WhereRaw("[Categories].[ParentId] = [arbol].[Id]").Build();

builder.WithRecursive("arbol", anchor, rec).From("arbol").Build();
// PostgreSQL → WITH RECURSIVE "arbol" AS (... UNION ALL ...) SELECT * FROM "arbol"
// SQL Server → WITH [arbol] AS (... UNION ALL ...) SELECT * FROM [arbol]

Bloqueo de Filas​

ForUpdate​

Firma

SqlQueryBuilder<T> ForUpdate()

Descripción Agrega FOR UPDATE al final de la consulta para bloqueo exclusivo de filas. No hace nada en dialectos que no lo soportan (SQL Server — usa WithHint("UPDLOCK"); SQLite — ignorado silenciosamente).


ForShare​

Firma

SqlQueryBuilder<T> ForShare()

Descripción Agrega FOR SHARE al final de la consulta para bloqueo compartido de filas. No hace nada en dialectos que no lo soportan.


Tag / Build / ToPreviewSql​

Tag — simple​

Firma

SqlQueryBuilder<T> Tag(string description)

Descripción Etiqueta la consulta. Cuando se llama Build(), la descripción se antepone como un comentario SQL (-- descripción). Útil para rastreo en logs.

Ejemplo

builder.From("Users").Tag("Obtener usuarios activos").Build();
// SQL: -- Obtener usuarios activos
// SELECT * FROM [Users]

Tag — con logger​

Firma

SqlQueryBuilder<T> Tag(string description, Action<string>? logger)

Descripción Igual que el anterior, pero también invoca logger con "[SQL] {descripción}" cuando se llama Build(). Si logger es null, el tag solo se incrusta como comentario SQL.

Ejemplo

builder.Tag("Obtener usuarios activos", msg => Console.WriteLine(msg));
// Salida en consola: [SQL] Obtener usuarios activos

ToPreviewSql​

Firma

string ToPreviewSql()

Descripción Devuelve una vista previa del SQL en medio de la cadena fluida sin finalizar el constructor. Útil en la ventana de observación del depurador. Devuelve un mensaje alternativo si el estado del constructor está incompleto.


Build​

Firma

SqlQueryResult Build()

Descripción Ensambla y devuelve el SqlQueryResult final. Esta llamada es terminal — el constructor puede reutilizarse pero el resultado es inmutable.



Ejemplos avanzados​

1) JOIN + WHERE + ORDER​

var q = new SqlQueryBuilder<Order>()
.SelectAll()
.From("Orders o")
.Join("Users u", "o.UserId = u.Id")
.Where(o => o.Status == "Open")
.OrderByDesc(o => o.CreatedAt)
.Take(100);

2) GROUP BY + HAVING​

var q = new SqlQueryBuilder<Order>()
.SelectCount()
.SelectRaw("o.CustomerId")
.From("Orders o")
.GroupByRaw("o.CustomerId")
.HavingRaw("COUNT(*) > 5");

3) CTE + Window​

var q = new SqlQueryBuilder<Order>()
.WithCte("recent", "SELECT * FROM Orders WHERE CreatedAt > @p0")
.SelectRaw("Id, ROW_NUMBER() OVER (ORDER BY CreatedAt DESC) AS rn")
.From("recent");