Использование одного коррелированного подзапроса SQL для получения двух столбцов - PullRequest
6 голосов
/ 06 ноября 2011

Моя проблема представлена ​​следующим запросом:

SELECT 
  b.row_id, b.x, b.y, b.something,
  (SELECT a.x FROM my_table a WHERE a.row_id = (b.row_id - 1), a.something != 42 ) AS source_x,
  (SELECT a.y FROM my_table a WHERE a.row_id = (b.row_id - 1), a.something != 42 ) AS source_y
FROM 
  my_table b

Я дважды использую один и тот же оператор подзапроса для получения source_x и source_y. Вот почему мне интересно, возможно ли сделать это, используя только один подзапрос?

Потому что, как только я запускаю этот запрос на моих реальных данных (миллионы строк), кажется, что он никогда не завершится и займет несколько часов, если не дней (мое соединение прерывается до конца).

Я использую PostgreSQL 8.4

Ответы [ 4 ]

8 голосов
/ 06 ноября 2011

Я думаю, что вы можете использовать этот подход:

SELECT b.row_id
     , b.x
     , b.y
     , b.something
     , a.x
     , a.y
  FROM my_table b
  left join my_table a on a.row_id = (b.row_id - 1)
                      and a.something != 42
3 голосов
/ 07 ноября 2011

@ DavidEG опубликовал лучший синтаксис для запроса.

Однако ваша проблема определенно не только в методе запроса .JOIN вместо двух подзапросов может в лучшем случае ускорить процесс в два раза.Скорее всего меньше.Это не объясняет "часы".Даже с миллионами строк прилично настроенный Postgres должен выполнить простой запрос за секунды, а не часы.

  • Первое, что выделяется, это синтаксическая ошибка вВаш запрос:

    ... WHERE a.row_id = (b.row_id - 1), a.something != 42
    

    AND или OR здесь необходим, а не запятая.

  • Следующим, что нужно проверить, являются indexes .Если row_id не является первичным ключом, возможно, у вас нет индекса.Для оптимальной производительности этого конкретного запроса создайте многостолбцовый индекс на (row_id, something), например:

    CREATE INDEX my_table_row_id_something_idx ON my_table (row_id, something)
    
  • Если фильтр исключает одно и то же значение каждый раз в something != 42, вы также можете использовать частичный индекс вместо этого для дополнительного ускорения:

    CREATE INDEX my_table_row_id_something_idx ON my_table (row_id)
    WHERE something != 42
    

    Это будет иметь существенное значение, только если 42 является общим значением или something является столбцом большего размера, чем просто целое число.(Индекс с двумя integer столбцами обычно занимает тот же размер на диске, что и индекс с одним из-за выравнивания данных. См .:

  • Когда производительность является проблемой, онавсегда хорошая идея проверить ваши настройки . Стандартные настройки в Postgres используют минимальные ресурсы во многих дистрибутивах и не предназначены для обработки "миллионов строк".

  • В зависимости от вашей текущей версии Postgres, обновление до текущей версии (9.1 на момент написания) может очень помочь.

  • В конечном счете, аппаратное обеспечение также всегда является фактором. Настройка и оптимизация могут вас только достичь.

0 голосов
/ 06 ноября 2011
SELECT b.row_id, b.x, b.y, b.something, a.x, a.y
  FROM my_table b
  LEFT JOIN (
    SELECT row_id + 1, x, y
      FROM my_table
      WHERE something != 42
  ) AS a ON a.row_id = b.row_id;
0 голосов
/ 06 ноября 2011

старомодный синтаксис:

SELECT 
  b.row_id, b.x, b.y, b.something
  , a.x AS source_x
  , a.y AS source
FROM my_table b
    ,my_table a 
WHERE a.row_id = b.row_id - 1
  AND a.something != 42
  ;

Join-синтаксис:

SELECT 
  b.row_id, b.x, b.y, b.something
  , a.x AS source_x
  , a.y AS source
FROM my_table b
JOIN my_table a 
  ON (a.row_id = b.row_id - 1)
WHERE a.something != 42
  ;
...