PostgreSQL LEFT OUTER JOIN查询语法

时间:2013-03-25 12:51:26

标签: sql postgresql left-join cross-join

假设我有table1

  id      name
-------------
  1       "one"
  2       "two"
  3       "three"

带有第一个外键的table2

id    tbl1_fk    option   value
-------------------------------
 1      1         1        1
 2      2         1        1
 3      1         2        1
 4      3         2        1

现在我希望得到一个查询结果:

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

我如何实现这一目标?

我已经尝试过:

SELECT
  table1.id,
  table1.name,
  table2.option,
  table2.value
FROM table1 AS table1
LEFT outer JOIN table2 AS table2 ON table1.id = table2.tbl1fk

但结果似乎省略了空值:

1    "one"    1   1
2    "two"    1   1
1    "one"    2   1
3    "three"  2   1

解决:感谢Mahmoud Gamal :(加上GROUP BY) 解决了这个问题

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

4 个答案:

答案 0 :(得分:7)

您必须使用CROSS JOIN从第一个表中获取name的所有可能组合,并从第二个表中获取option。然后LEFT JOIN这些与第二个表的组合。类似的东西:

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

SQL Fiddle Demo

答案 1 :(得分:3)

简易版:选项=组

它没有在Q中指定,但似乎选项应该以某种方式定义一个组。在这种情况下,查询可以只是:

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;

或者,如果选项没有按顺序编号,则从1开始:

...
FROM  (SELECT DISTINCT option FROM table2) o
...

返回:

 id | name  | option | value
----+-------+--------+-------
  1 | one   |      1 |     1
  2 | two   |      1 |     1
  3 | three |        |
  1 | one   |      2 |     1
  2 | two   |        |
  3 | three |      2 |     1
  • 更快清洁,避免使用大CROSS JOIN和大GROUP BY
  • 每组都有一个不同的行,其中包含一个组号(grp)。
  • 需要Postgres 8.4 +。

更复杂:由行序列

表示的组
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;

结果:

 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.

如何?

解释复杂版本......

  • 每个集合都以tbl1_fk< =最后一个集合开始。我用window function lag()检查了这一点。为了覆盖第一行(没有前一行)的角落情况,我提供了lag() count()的默认值。

  • 使用grp作为聚合窗口函数,我将运行计数添加到每一行,有效地形成组编号(SELECT DISTINCT grp FROM t2) g

  • 我可以为每个组获取一个实例:

    CROSS JOIN

    但是,获得最大值并使用漂亮的biggest possible integer 2147483647来减少CROSS JOIN更快。

  • 这个GROUP BY产生我们需要的行而没有任何剩余。避免以后需要LEFT JOIN t2

  • grp,除了tbl1_fk之外,还使用{{1}}来区分它。

  • 按您喜欢的方式排序 - 现在可以使用组号进行排序。

答案 2 :(得分:0)

试试这个

SELECT
  table1.id, table1.name, table2.option, table2.value  FROM table1 AS table11
JOIN table2 AS table2 ON table1.id = table2.tbl1_fk

答案 3 :(得分:-4)

这就够了:

select * from table1 left join table2 on table1.id = table2.tbl1_fk;