Skip to content

Commit 3bda19c

Browse files
authored
perf: flat SQL for single-op pipelines and group_by+summarise pattern (#50)d
Three QueryBuilder optimizations: 1. Single-op pipelines emit flat SQL without CTE wrapping 2. group_by+summarise pattern emits a single SELECT...GROUP BY 3. Table sources referenced directly by name instead of subquery
1 parent 20af083 commit 3bda19c

1 file changed

Lines changed: 24 additions & 9 deletions

File tree

lib/dux/query_builder.ex

Lines changed: 24 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -19,8 +19,21 @@ defmodule Dux.QueryBuilder do
1919
[] ->
2020
{"SELECT * FROM (#{source_sql}) __src", setup}
2121

22+
[single_op] ->
23+
# Single op: emit flat SQL without CTE wrapping
24+
initial_prev = direct_source_ref(source) || "(#{source_sql}) __src"
25+
{sql, _groups} = op_to_sql(single_op, initial_prev, [])
26+
{sql, setup}
27+
28+
[{:group_by, cols}, {:summarise, aggs}] ->
29+
# Common pattern: group_by + summarise. Emit flat SQL without CTE.
30+
initial_prev = direct_source_ref(source) || "(#{source_sql}) __src"
31+
{sql, _groups} = op_to_sql({:summarise, aggs}, initial_prev, cols)
32+
{sql, setup}
33+
2234
ops ->
23-
{ctes, _counter, _groups} = build_ctes(ops, source_sql, 0, [])
35+
initial_prev = direct_source_ref(source) || "(#{source_sql}) __src"
36+
{ctes, _counter, _groups} = build_ctes(ops, initial_prev, 0, [])
2437
last_cte = "__s#{length(ctes) - 1}"
2538

2639
cte_clauses =
@@ -33,6 +46,14 @@ defmodule Dux.QueryBuilder do
3346
end
3447
end
3548

49+
# For table sources, use the quoted table name directly instead of
50+
# wrapping in (SELECT * FROM "table") __src subquery.
51+
defp direct_source_ref({:table, %Dux.TableRef{name: name}}) do
52+
quote_ident(name)
53+
end
54+
55+
defp direct_source_ref(_), do: nil
56+
3657
@doc """
3758
Clear any IPC table refs stored in the process dictionary.
3859
@@ -178,16 +199,10 @@ defmodule Dux.QueryBuilder do
178199
# CTE building — each op becomes a CTE
179200
# ---------------------------------------------------------------------------
180201

181-
defp build_ctes([], source_sql, counter, _groups) do
182-
{["SELECT * FROM (#{source_sql}) __src"], counter + 1, []}
183-
end
184-
185-
defp build_ctes(ops, source_sql, counter, groups) do
186-
prev = "(#{source_sql}) __src"
187-
202+
defp build_ctes(ops, initial_prev, counter, groups) do
188203
{ctes, counter, groups} =
189204
Enum.reduce(ops, {[], counter, groups}, fn op, {ctes, n, groups} ->
190-
prev_ref = if ctes == [], do: prev, else: "__s#{n - 1}"
205+
prev_ref = if ctes == [], do: initial_prev, else: "__s#{n - 1}"
191206
{cte_sql, new_groups} = op_to_sql(op, prev_ref, groups)
192207
{ctes ++ [cte_sql], n + 1, new_groups}
193208
end)

0 commit comments

Comments
 (0)