WITH CTE AS (
SELECT
*,
SUM(amount) OVER (ORDER BY inserted_at DESC) AS running_total
FROM transactions
)
SELECT *
FROM CTE
WHERE running_total < 1000;
It’s supposed to find the top N rows where their total amount is less than 1000.
I can’t figure out how to reference the columns in the CTE query…
** (Ecto.QueryError) lib/foo/transaction.ex:27: field `running_total` in `where` does not exist in schema Transaction in query:
from t0 in Transaction,
where: t0.running_total < ^1000,
select: t0
CTE and subquery in this case are the same. It’s more about what you think looks more readable. To use it in ecto just wrap your query in subquery(your_query_here) and use it in joins or froms.
In general, CTEs can have better, worse, or the same performance as subqueries. CTEs have the option to be materialized, which means the result is evaluated once, independently of the rest of the query, and then referenced wherever needed.
The CTE example in the docs uses the name passed to with_cte as a table name in the subsequent join:
Product
|> recursive_ctes(true)
|> with_cte("category_tree", as: ^category_tree_query)
|> join(:left, [p], c in "category_tree", on: c.id == p.category_id)
Re: the subquery angle - here’s an example of filtering by a window function using a subquery, from the Postgres docs:
SELECT depname, empno, salary, enroll_date
FROM
(SELECT depname, empno, salary, enroll_date,
rank() OVER (PARTITION BY depname ORDER BY salary DESC, empno) AS pos
FROM empsalary
) AS ss
WHERE pos < 3;