可以将文章内容翻译成中文,广告屏蔽插件可能会导致该功能失效(如失效,请关闭广告屏蔽插件后再试):
问题:
Lets say I have a table1:
id name ------------- 1 "one" 2 "two" 3 "three"
And a table2 with a foreign key to the first:
id tbl1_fk option value ------------------------------- 1 1 1 1 2 2 1 1 3 1 2 1 4 3 2 1
Now I want to have as a query result:
table1.id | table1.name | option | value ------------------------------------- 1 "one" 1 1 2 "two" 1 1 3 "three" 1 "one" 2 1 2 "two" 3 "three" 2 1
How do I achieve that?
I already tried:
SELECT table1.id, table1.name, table2.option, table2.value FROM table1 AS table1 LEFT outer JOIN table2 AS table2 ON table1.id = table2.tbl1fk
but the result seems to omit the null vales:
1 "one" 1 1 2 "two" 1 1 1 "one" 2 1 3 "three" 2 1
SOLVED: thanks to Mahmoud Gamal: (plus the GROUP BY) Solved with this query
SELECT t1.id, t1.name, t2.option, t2.value FROM ( SELECT t1.id, t1.name, t2.option FROM table1 AS t1 CROSS JOIN table2 AS t2 ) AS t1 LEFT JOIN table2 AS t2 ON t1.id = t2.tbl1fk AND t1.option = t2.option group by t1.id, t1.name, t2.option, t2.value ORDER BY t1.id, t1.name
回答1:
You have to use CROSS JOIN to get every possible combination of name from the first table with the option from the second table. Then LEFT JOIN these combination with the second table. Something like:
SELECT t1.id, t1.name, t2.option, t2.value FROM ( SELECT t1.id, t1.name, t2.option FROM table1 AS t1 CROSS JOIN table2 AS t2 ) AS t1 LEFT JOIN table2 AS t2 ON t1.id = t2.tbl1_fk AND t1.option = t2.option
回答2:
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.
回答3:
try this
SELECT table1.id, table1.name, table2.option, table2.value FROM table1 AS table11 JOIN table2 AS table2 ON table1.id = table2.tbl1_fk
回答4:
This is enough:
select * from table1 left join table2 on table1.id=table2.tbl1_fk ;