PostgreSQL 9.6中窗口函数中的查询行为不稳定

时间:2017-12-26 11:38:37

标签: sql postgresql openstreetmap postgis

我从geofabrik.de下载了我国家的OSM数据,成功将其导入到Ubuntu 16.04上安装的PostgreSQL 9.6并多次使用它。我还创建了Web应用程序,它可以正常工作。所以我决定添加另一个功能,从某些点返回最近的特殊点(例如餐馆)。对于一个最接近的点它可以工作,但是当我想要它们的返回数组时,它不起作用。所以我分解了我的问题并发现了奇怪的行为。当我执行以下查询时:

SELECT t.osm_id
      FROM (
        SELECT DISTINCT ON (a.points) a.points, v.osm_id AS osm_id, MIN(ST_DISTANCE(v.the_geom, a.points)) OVER (PARTITION BY a.points ORDER BY ST_DISTANCE(v.the_geom, a.points))
        FROM (SELECT ST_GEOMFROMEWKT('SRID=4326;POINT(17.104854583740238 48.15099866770469)') AS points) a
        CROSS JOIN ways_vertices_pgr v
      ) AS t

它返回:

| osm_id            |
| ----------------- |
| 2338524511        |

当我在地图上显示此点时,它被放置在远离原点的位置,在我更改了子查询中的点后,结果保持不变。另外我知道显示点和原点之间有很多点,应该通过查询返回。然后我尝试运行以下查询:

SELECT t.*, t.osm_id
      FROM (
        SELECT DISTINCT ON (a.points) a.points, v.osm_id AS osm_id, MIN(ST_DISTANCE(v.the_geom, a.points)) OVER (PARTITION BY a.points ORDER BY ST_DISTANCE(v.the_geom, a.points))
        FROM (SELECT ST_GEOMFROMEWKT('SRID=4326;POINT(17.104854583740238 48.15099866770469)') AS points) a
        CROSS JOIN ways_vertices_pgr v
      ) AS t

然后它返回:

| points                                             | osm_id   | min                  | osm_id     |
| -------------------------------------------------- | -------- | -------------------- | --------   |
| 0101000020E6100000010000C0D71A3140FFC3A1EC53134840 | 33169309 | 0.000124886435658481 | 33169309   |

除SELECT部分​​之外的整个查询保持相同,但结果不同,现在它是正确的。任何人都可以建议我如何更改查询以正常工作?

2 个答案:

答案 0 :(得分:2)

使用distinct on时,您需要order by。我认为这是第一个查询所需的逻辑:

    SELECT DISTINCT ON (a.points) a.points, v.osm_id AS osm_id,ST_DISTANCE(v.the_geom, a.points) as dist
    FROM (SELECT ST_GEOMFROMEWKT('SRID=4326;POINT(17.104854583740238 48.15099866770469)') AS points) a CROSS JOIN
         ways_vertices_pgr v
    ORDER BY a.points, dist;

答案 1 :(得分:2)

使用您的查询检查EXPLAIN ANALYZE的输出,以查看添加列时结果发生变化的确切原因。可能它正在使用略微不同的执行计划,这会影响行的排序。

根据定义,

DISTINCT ON是非确定性的,这意味着结果可以在执行之间发生变化。来自the PostgreSQL 9.6 manual

  

SELECT DISTINCT ON ...请注意,除非查询在足够的列上排序以保证到达DISTINCT过滤器的行的唯一排序,否则集合的“第一行”是不可预测的。 (DISTINCT ON处理在ORDER BY排序后发生。)

根据戈登的建议添加ORDER BY应该会给你可重复的结果。