使用单个SQL相关子查询来获取两列

问题描述 投票:5回答:4

我的问题由以下查询表示:

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_xsource_y。这就是为什么我想知道是否可以只使用一个子查询来做到这一点?

因为一旦我对我的真实数据(数百万行)运行此查询,它似乎永远不会完成并花费数小时,如果不是几天(我的连接在结束前挂起)。

我正在使用PostgreSQL 8.4

sql performance postgresql indexing correlated-subquery
4个回答
3
投票

@DavidEG发布了查询的最佳语法。

但是,您的问题绝对不仅仅是查询技术。 JOIN而不是两个子查询可以最多加速两倍。很可能更少。这并不能解释“小时”。即使有数百万行,一个体面设置的Postgres也应该在几秒钟内完成简单的查询,而不是几小时。

  • 首先要突出的是查询中的语法错误: ... WHERE a.row_id = (b.row_id - 1), a.something != 42 这里需要ANDOR,而不是逗号。
  • 接下来要检查的是索引。如果row_id不是主键,则可能没有索引。为了获得此特定查询的最佳性能,请在multi-column index上创建一个(row_id, something),如下所示: CREATE INDEX my_table_row_id_something_idx ON my_table (row_id, something)
  • 如果过滤器每次在something != 42you中排除相同的值,也可以使用partial index代替额外的加速: CREATE INDEX my_table_row_id_something_idx ON my_table (row_id) WHERE something != 42 如果42是一个常见值,或者something是一个比整数更大的列,这只会产生实质性的差异。 (由于数据对齐,具有两个integer列的索引通常在磁盘上占据与仅有一个索引相同的大小。请参阅: Calculating and saving space in PostgreSQL Is a composite index also good for queries on the first field?
  • 当表演成为一个问题时,check your settings总是一个好主意。 Postgres中的标准设置在许多发行版中使用最少的资源,并且不能处理“数百万行”。
  • 根据你的Postgres的实际版本,upgrade to a current version(撰写本文时为9.1)可能会有很大帮助。
  • 最终,hardware也是一个因素。调整和优化只能让你到目前为止。

8
投票

我想你可以使用这种方法:

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

0
投票

老式语法:

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
  ;

加入语法:

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
  ;

0
投票
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;
© www.soinside.com 2019 - 2024. All rights reserved.