Simple version: option = group
It's not specified in the Q, but it seems like option is supposed to define a group somehow. In this case, the query can simply be:
SELECT t1.id, t1.name, t2.option, t2.value
FROM (SELECT generate_series(1, max(option)) AS option FROM table2) o
CROSS JOIN table1 t1
LEFT JOIN table2 t2 ON t2.option = o.option AND t2.tbl1_fk = t1.id
ORDER BY o.option, t1.id;
Or, if options are not numbered in sequence, starting with 1
:
...
FROM (SELECT DISTINCT option FROM table2) o
...
Returns:
id | name | option | value
----+-------+--------+-------
1 | one | 1 | 1
2 | two | 1 | 1
3 | three | |
1 | one | 2 | 1
2 | two | |
3 | three | 2 | 1
- Faster and cleaner, avoiding the big
CROSS JOIN
and the big GROUP BY
.
- You get distinct rows with a group number (
grp
) per set.
- Requires Postgres 8.4+.
More complex: group indicated by sequence of rows
WITH t2 AS (
SELECT *, count(step OR NULL) OVER (ORDER BY id) AS grp
FROM (
SELECT *, lag(tbl1_fk, 1, 2147483647) OVER (ORDER BY id) >= tbl1_fk AS step
FROM table2
) x
)
SELECT g.grp, t1.id, t1.name, t2.option, t2.value
FROM (SELECT generate_series(1, max(grp)) AS grp FROM t2) g
CROSS JOIN table1 t1
LEFT JOIN t2 ON t2.grp = g.grp AND t2.tbl1_fk = t1.id
ORDER BY g.grp, t1.id;
Result:
grp | id | name | option | value
-----+----+-------+--------+-------
1 | 1 | one | 1 | 1
1 | 2 | two | 1 | 1
1 | 3 | three | |
2 | 1 | one | 2 | 1
2 | 2 | two | |
2 | 3 | three | 2 | 1
-> SQLfiddle for both.
How?
Explaining the complex version ...
Every set is started with a tbl1_fk
<= the last one. I check for this with the window function lag()
. To cover the corner case of the first row (no preceding row) I provide the biggest possible integer 2147483647
the default for lag()
.
With count()
as aggregate window function I add the running count to each row, effectively forming the group number grp
.
I could get a single instance for every group with:
(SELECT DISTINCT grp FROM t2) g
But it's faster to just get the maximum and employ the nifty generate_series()
for the reduced CROSS JOIN
.
This CROSS JOIN
produces exactly the rows we need without any surplus. Avoids the need for a later GROUP BY
.
LEFT JOIN t2
to that, using grp
in addition to tbl1_fk
to make it distinct.
Sort any way you like - which is possible now with a group number.