sql – 重用计算的选择值

我正在尝试使用
ST_SnapToGrid,然后使用GROUP BY网格单元格(x,y).这是我先做的事情:

SELECT
  COUNT(*)                      AS n,
  ST_X(ST_SnapToGrid(geom, 50)) AS x,
  ST_Y(ST_SnapToGrid(geom, 50)) AS y
FROM points
GROUP BY x, y

我不想为x和y重新计算ST_SnapToGrid.所以我改为使用子查询:

SELECT
  COUNT(*)   AS n,
  ST_X(geom) AS x,
  ST_Y(geom) AS y
FROM (
  SELECT 
      ST_SnapToGrid(geom, 50) AS geom
  FROM points
) AS tmp
GROUP BY x, y

但是当我运行EXPLAIN时,这两个查询都具有完全相同的执行计划:

GroupAggregate  (...)
  ->  Sort  (...)
        Sort Key: (st_x(st_snaptogrid(points.geom, 0::double precision))), (st_y(st_snaptogrid(points.geom, 0::double precision)))
        ->  Seq Scan on points  (...)

问题:PostgreSQL会重用ST_SnapToGrid()的结果值吗?
如果没有,有没有办法让它这样做?

最佳答案 测试时间

您没有在EXPLAIN输出中看到每行的单个函数的评估.

使用EXPLAIN ANALYZE进行测试以获得实际查询时间以比较整体效果.运行几次以排除缓存工件.对于像这样的简单查询,您可以获得更可靠的总运行时数:

EXPLAIN (ANALYZE, TIMING OFF) SELECT ...

需要Postgres 9.2. Per documentation

TIMING

Include actual startup time and time spent in each node in the output. The overhead of repeatedly reading the system clock can slow
down the query significantly on some systems, so it may be useful to
set this parameter to FALSE when only actual row counts, and not exact
times, are needed. Run time of the entire statement is always
measured, even when node-level timing is turned off with this option.
This parameter may only be used when ANALYZE is also enabled. It
defaults to TRUE.

防止重复评估

通常,子查询中的表达式会被计算一次.但是如果Postgres认为会更快,那么它就会崩溃琐碎的子查询.

要引入优化障碍,可以使用CTE而不是子查询.这保证了Postgres只计算一次ST_SnapToGrid(geom,50):

WITH cte AS (
   SELECT ST_SnapToGrid(geom, 50) AS geom1
   FROM   points
   )
SELECT COUNT(*)   AS n
     , ST_X(geom1) AS x
     , ST_Y(geom1) AS y
FROM   cte
GROUP  BY geom1;         -- see below

但是,由于CTE的开销较大,因此它可能比子查询慢.函数调用可能非常便宜.通常,Postgres更了解如何优化查询计划.如果你更了解,只会引入这样的优化障碍.

简化

我将子查询/ CTE中计算点的名称更改为geom1,以澄清它与原始geom的不同.这有助于澄清更重要的事情:

GROUP BY geom1

代替:


   
  
    GROUP BY x, y 
  

这显然更便宜 – 并且可能影响函数调用是否重复.所以,这可能是最快的:

SELECT COUNT(*) AS n
     , ST_X(ST_SnapToGrid(geom, 50)) AS x
     , ST_y(ST_SnapToGrid(geom, 50)) AS y
FROM   points
GROUP  BY ST_SnapToGrid(geom, 50);         -- same here!

或许这个:

SELECT COUNT(*)    AS n
     , ST_X(geom1) AS x
     , ST_y(geom1) AS y
FROM (
   SELECT ST_SnapToGrid(geom, 50) AS geom1
   FROM   points
   ) AS tmp
GROUP  BY geom1;

使用EXPLAIN ANALYZE或EXPLAIN(ANALYZE,TIMING OFF)测试所有三个并亲自查看.测试>>猜测.

点赞