Postgres如何在订购后计算列?

时间:2018-04-05 12:18:09

标签: postgresql query-optimization

我在SELECT块中有很多计算列的大sql查询。此外,还有一个计算列的排序,并且只限制了100行。但是postgres会计算每一行的所有列,而不仅仅是100行。

让我解释一下。

让我们创建一些测试表:

CREATE TABLE test_main(col1 INTEGER);

并填写一些随机数据:

DO
$do$
BEGIN
  FOR r IN 1..100000 LOOP
    INSERT INTO test_main(col1) VALUES (trunc(random()*1000));
  END LOOP;
END
$do$;

然后创建一些额外的表:

CREATE TABLE test_main_agg1(
  col1 INTEGER,
  val INTEGER
);
CREATE TABLE test_main_agg2(
  col1 INTEGER,
  val INTEGER
);

并填写它:

DO
$do$
DECLARE
 r test_main%rowtype;
BEGIN
  FOR r IN SELECT * FROM test_main LOOP
    FOR i IN 1..5 LOOP
      INSERT INTO test_main_agg1(col1, val) VALUES (r.col1, trunc(random()*1000));
      INSERT INTO test_main_agg2(col1, val) VALUES (r.col1, trunc(random()*1000));
    END LOOP;
  END LOOP;
END
$do$;

当然,创建一些索引:

CREATE INDEX test_main_indx ON test_main(col1);
CREATE INDEX test_main_agg1_val_indx ON test_main_agg1(col1,val);
CREATE INDEX test_main_agg2_val_indx ON test_main_agg2(col1,val);

现在,如果我们执行此查询:

SELECT col1,
       (SELECT MAX(val) FROM test_main_agg1 g WHERE g.col1=m.col1) max_val1,
       (SELECT MAX(val) FROM test_main_agg2 g WHERE g.col1=m.col1) max_val2
  FROM test_main m
 LIMIT 100;

由于索引,它会非常快。如果我们添加ORDER BY col1,它仍然会很快。但是如果我们使用ORDER BY max_val1,那么大约需要2秒钟。 如果我们使用`ORDER BY max_val1对查询运行EXPLAIN ANALYZE,我们将看到以下行:

SubPlan 4
 -> Result (cost=4.06..4.07 rows=1 width=0) (actual time=0.011..0.011 rows=1 loops=100000)
  InitPlan 3 (returns $3)
   -> Limit (cost=0.42..4.06 rows=1 width=4) (actual time=0.010..0.010 rows=1 loops=100000)
    -> Index Only Scan Backward using test_main_agg2_val_indx on test_main_agg2 g_1 (cost=0.42..1818.25 rows=500 width=4) (actual time=0.010..0.010 rows=1 loops=100000)
     Index Cond: ((col1 = m.col1) AND (val IS NOT NULL))
     Heap Fetches: 100000

这意味着,postgres为100000行计算max_val2,但不仅仅计算100行。我理解为什么postgres需要计算max_val1,而不是max_val2

在执行排序和限制后,可能会有一些提示或类似的东西告诉postgres计算列?

1 个答案:

答案 0 :(得分:1)

LIMIT限制整个查询的输出,而不是主查询中的子查询。如果您只想要最多100行,则需要先选择它们,然后在该子集上应用max():

SELECT col1,
       (SELECT MAX(val) FROM test_main_agg1 g WHERE g.col1=m.col1) max_val1,
       (SELECT MAX(val) FROM test_main_agg2 g WHERE g.col1=m.col1) max_val2
FROM (
  select val, col1
  from test_main
  LIMIT 100
) m;

请注意,limit 没有ORDER BY并不合理。关系数据库中的行没有顺序。因此,除非指定排序顺序,否则表中没有“前100行”。