Можно ли эффективно присоединиться к подмножеству строк левой таблицы в Postgres? - PullRequest
4 голосов
/ 03 февраля 2012

Допустим, у меня есть следующие таблицы:

table_1                  table_2
id_a    name             id_a    id_b
1       c                1       1
2       a                1       2
3       b                2       1
                         2       2

Теперь рассмотрим следующее левое внешнее соединение:

SELECT *
FROM table_1
LEFT OUTER JOIN table_2 USING (id_a)

id_a    name  id_b  
1       c     1
1       c     2
2       a     1
2       a     2
3       b

Теперь представьте, что 'FROM table_1' на самом деле является сложнымзапрос, например:

SELECT * FROM huge_table WHERE expensive_conditions_producing_three_rows

Можно ли написать запрос, который объединяет только левую строку с минимальным именем, без повторного выполнения подзапроса полностью?Вы можете предположить, что у вас есть некоторый контроль над подзапросом, т.е. вы можете добавить ORDER BY при необходимости.

Другими словами, конечный результат должен выглядеть следующим образом:

id_a    name  id_b
1       c
2       a     1
2       a     2
3       b

Я подумал об использовании SELECT INTO, чтобы поместить результаты подзапроса во временную таблицу.Тогда не будет проблемой вычислить минимум для использования в условии JOIN ON.Но я бы предпочел избежать этого, если только это не единственное решение.

Редактировать: Я подожду пару дней, а затем приму лучшее решение, независимо от версии PG.Однако, тот, который работает в PG 8.3 и более ранних версиях, был бы очень признателен.

Ответы [ 2 ]

5 голосов
/ 03 февраля 2012

Использование Оконные функции (доступно из PostgreSQL 8.4):

SELECT *
FROM
      ( SELECT *
             , ROW_NUMBER() OVER (ORDER BY SomeColumn) AS RowNum
        FROM table_1
      ) AS a
  LEFT JOIN
      table_2 AS b
    ON 
       (join condition)
    AND
       a.RowNum = 1
3 голосов
/ 03 февраля 2012

Используйте CTE (общее табличное выражение) для этого (доступно для PostgreSQL 8.4 или новее):

WITH cte AS (
    SELECT id_a, name
    FROM   table_1
    WHERE  expensive_conditions_producing_three_rows
    )
SELECT c.id_a, c.name, t2.id_b
FROM   cte c
LEFT   JOIN table2 t2 ON t2.id_a = c.id_a
                     AND t2.name = (SELECT min(name) FROM cte)
...