Selecto.Advanced.CTE (Selecto v0.4.3)
Common Table Expression (CTE) support for PostgreSQL WITH clauses.
Provides comprehensive support for non-recursive and recursive CTEs, enabling hierarchical queries, query modularity, and complex data processing patterns.
examples
Examples
# Non-recursive CTE
selecto
|> Selecto.with_cte("high_value_customers", fn ->
Selecto.configure(customer_domain, connection)
|> Selecto.select(["customer_id", "first_name", "last_name"])
|> Selecto.aggregate([{"payment.amount", :sum, as: "total_spent"}])
|> Selecto.join(:inner, "payment", on: "customer.customer_id = payment.customer_id")
|> Selecto.group_by(["customer.customer_id", "customer.first_name", "customer.last_name"])
|> Selecto.having([{"total_spent", {:>, 100}}])
end,
columns: ["customer_id", "first_name", "last_name", "total_spent"],
join: [type: :inner, owner_key: :customer_id, related_key: :customer_id, fields: :infer]
)
|> Selecto.select(["film.title", "high_value_customers.first_name"])
# Recursive CTE for hierarchical data
selecto
|> Selecto.with_recursive_cte("org_hierarchy",
base_query: fn ->
# Anchor: top-level managers
Selecto.configure(employee_domain, connection)
|> Selecto.select(["employee_id", "name", "manager_id", {:literal, 0, as: "level"}])
|> Selecto.filter([{"manager_id", nil}])
end,
recursive_query: fn cte ->
# Recursive: employees under each manager
Selecto.configure(employee_domain, connection)
|> Selecto.select(["employee.employee_id", "employee.name", "employee.manager_id",
{:func, "org_hierarchy.level + 1", as: "level"}])
|> Selecto.join(:inner, cte, on: "employee.manager_id = org_hierarchy.employee_id")
end,
columns: ["employee_id", "name", "manager_id", "level"],
join: [type: :inner, owner_key: :employee_id, related_key: :employee_id, fields: :infer]
)
Link to this section Summary
Functions
Create a non-recursive CTE specification.
Create a recursive CTE specification.
Detect circular dependencies in a list of CTEs.
Validate a CTE specification.
Link to this section Functions
create_cte(name, query_builder, opts \\ [])
Create a non-recursive CTE specification.
parameters
Parameters
name- CTE name for the WITH clausequery_builder- Function that returns a Selecto queryopts- Options including :columns, :dependencies
examples
Examples
# Simple CTE
CTE.create_cte("active_customers", fn ->
Selecto.configure(customer_domain, connection)
|> Selecto.filter([{"active", true}])
end)
# CTE with explicit columns
CTE.create_cte("customer_stats",
fn ->
Selecto.configure(customer_domain, connection)
|> Selecto.select(["customer_id", {:func, "COUNT", ["rental_id"], as: "rental_count"}])
|> Selecto.join(:left, "rental", on: "customer.customer_id = rental.customer_id")
|> Selecto.group_by(["customer_id"])
end,
columns: ["customer_id", "rental_count"]
)
create_recursive_cte(name, opts)
Create a recursive CTE specification.
parameters
Parameters
name- CTE name for the WITH clausebase_query- Function that returns the anchor queryrecursive_query- Function that takes the CTE reference and returns recursive queryopts- Options including :columns, :dependencies
examples
Examples
# Hierarchical employee structure
CTE.create_recursive_cte("employee_hierarchy",
base_query: fn ->
# Anchor: top-level managers
Selecto.configure(employee_domain, connection)
|> Selecto.select(["employee_id", "name", "manager_id", {:literal, 0, as: "level"}])
|> Selecto.filter([{"manager_id", nil}])
end,
recursive_query: fn cte_ref ->
# Recursive: subordinates
Selecto.configure(employee_domain, connection)
|> Selecto.select(["employee.employee_id", "employee.name", "employee.manager_id",
{:func, "employee_hierarchy.level + 1", as: "level"}])
|> Selecto.join(:inner, cte_ref, on: "employee.manager_id = employee_hierarchy.employee_id")
end
)
detect_circular_dependencies(ctes)
Detect circular dependencies in a list of CTEs.
Returns {:error, cycle} if a circular dependency is found, {:ok, ordered_ctes} if CTEs can be ordered for execution.
validate_cte(spec)
Validate a CTE specification.
Ensures the CTE name is valid, queries are properly formed, and dependencies don't create circular references.