对具有多个自联接的大型表执行缓慢的空间查询

2023-12-10

我正在 Postgres 9.3.9 中的一个大表上进行查询。它是一个空间数据集,并且具有空间索引。比如说,我需要找到 3 种类型的物体:A、B 和 C。标准是 B 和 C 都在 A 的一定距离内,比如 500 米。

我的查询是这样的:

select 
  school.osm_id as school_osm_id, 
  school.name as school_name, 
  school.way as school_way, 
  restaurant.osm_id as restaurant_osm_id, 
  restaurant.name as restaurant_name, 
  restaurant.way as restaurant_way, 
  bar.osm_id as bar_osm_id, 
  bar.name as bar_name, 
  bar.way as bar_way 
from (
    select osm_id, name, amenity, way, way_geo 
    from planet_osm_point 
    where amenity = 'school') as school, 
   (select osm_id, name, amenity, way, way_geo 
    from planet_osm_point 
    where amenity = 'restaurant') as restaurant, 
   (select osm_id, name, amenity, way, way_geo 
    from planet_osm_point 
    where amenity = 'bar') as bar 
where ST_DWithin(school.way_geo, restaurant.way_geo, 500, false) 
  and ST_DWithin(school.way_geo, bar.way_geo, 500, false);

这个查询给了我我想要的,但是它需要很长的时间,比如 13 秒来执行。我想知道是否有另一种方法来编写查询并使其更有效。

查询计划:

Nested Loop  (cost=74.43..28618.65 rows=1 width=177) (actual time=33.513..11235.212 rows=10591 loops=1)
   Buffers: shared hit=530967 read=8733
   ->  Nested Loop  (cost=46.52..28586.46 rows=1 width=174) (actual time=31.998..9595.212 rows=4235 loops=1)
         Buffers: shared hit=389863 read=8707
         ->  Bitmap Heap Scan on planet_osm_point  (cost=18.61..2897.83 rows=798 width=115) (actual time=7.862..150.607 rows=8811 loops=1)
               Recheck Cond: (amenity = 'school'::text)
               Buffers: shared hit=859 read=5204
               ->  Bitmap Index Scan on idx_planet_osm_point_amenity  (cost=0.00..18.41 rows=798 width=0) (actual time=5.416..5.416 rows=8811 loops=1)
                     Index Cond: (amenity = 'school'::text)
                     Buffers: shared hit=3 read=24
         ->  Bitmap Heap Scan on planet_osm_point planet_osm_point_1  (cost=27.91..32.18 rows=1 width=115) (actual time=1.064..1.069 rows=0 loops=8811)
               Recheck Cond: ((way_geo && _st_expand(planet_osm_point.way_geo, 500::double precision)) AND (amenity = 'restaurant'::text))
               Filter: ((planet_osm_point.way_geo && _st_expand(way_geo, 500::double precision)) AND _st_dwithin(planet_osm_point.way_geo, way_geo, 500::double precision, false))
               Rows Removed by Filter: 0
               Buffers: shared hit=389004 read=3503
               ->  BitmapAnd  (cost=27.91..27.91 rows=1 width=0) (actual time=1.058..1.058 rows=0 loops=8811)
                     Buffers: shared hit=384528 read=2841
                     ->  Bitmap Index Scan on idx_planet_osm_point_waygeo  (cost=0.00..9.05 rows=137 width=0) (actual time=0.193..0.193 rows=64 loops=8811)
                           Index Cond: (way_geo && _st_expand(planet_osm_point.way_geo, 500::double precision))
                           Buffers: shared hit=146631 read=2841
                     ->  Bitmap Index Scan on idx_planet_osm_point_amenity  (cost=0.00..18.41 rows=798 width=0) (actual time=0.843..0.843 rows=6291 loops=8811)
                           Index Cond: (amenity = 'restaurant'::text)
                           Buffers: shared hit=237897
   ->  Bitmap Heap Scan on planet_osm_point planet_osm_point_2  (cost=27.91..32.18 rows=1 width=115) (actual time=0.375..0.383 rows=3 loops=4235)
         Recheck Cond: ((way_geo && _st_expand(planet_osm_point.way_geo, 500::double precision)) AND (amenity = 'bar'::text))
         Filter: ((planet_osm_point.way_geo && _st_expand(way_geo, 500::double precision)) AND _st_dwithin(planet_osm_point.way_geo, way_geo, 500::double precision, false))
         Rows Removed by Filter: 1
         Buffers: shared hit=141104 read=26
         ->  BitmapAnd  (cost=27.91..27.91 rows=1 width=0) (actual time=0.368..0.368 rows=0 loops=4235)
               Buffers: shared hit=127019
               ->  Bitmap Index Scan on idx_planet_osm_point_waygeo  (cost=0.00..9.05 rows=137 width=0) (actual time=0.252..0.252 rows=363 loops=4235)
                     Index Cond: (way_geo && _st_expand(planet_osm_point.way_geo, 500::double precision))
                     Buffers: shared hit=101609
               ->  Bitmap Index Scan on idx_planet_osm_point_amenity  (cost=0.00..18.41 rows=798 width=0) (actual time=0.104..0.104 rows=779 loops=4235)
                     Index Cond: (amenity = 'bar'::text)
                     Buffers: shared hit=25410
 Total runtime: 11238.605 ms

我目前只使用一张桌子1,372,711 行。它有73 列:

       Column       |         Type         |       Modifiers
--------------------+----------------------+---------------------------
 osm_id             | bigint               | 
 access             | text                 | 
 addr:housename     | text                 | 
 addr:housenumber   | text                 | 
 addr:interpolation | text                 | 
 admin_level        | text                 | 
 aerialway          | text                 | 
 aeroway            | text                 | 
 amenity            | text                 | 
 area               | text                 | 
 barrier            | text                 | 
 bicycle            | text                 | 
 brand              | text                 | 
 bridge             | text                 | 
 boundary           | text                 | 
 building           | text                 | 
 capital            | text                 | 
 construction       | text                 | 
 covered            | text                 | 
 culvert            | text                 | 
 cutting            | text                 | 
 denomination       | text                 | 
 disused            | text                 | 
 ele                | text                 | 
 embankment         | text                 | 
 foot               | text                 | 
 generator:source   | text                 | 
 harbour            | text                 | 
 highway            | text                 | 
 historic           | text                 | 
 horse              | text                 | 
 intermittent       | text                 | 
 junction           | text                 | 
 landuse            | text                 | 
 layer              | text                 | 
 leisure            | text                 | 
 lock               | text                 | 
 man_made           | text                 | 
 military           | text                 | 
 motorcar           | text                 | 
 name               | text                 | 
 natural            | text                 | 
 office             | text                 | 
 oneway             | text                 | 
 operator           | text                 | 
 place              | text                 | 
 poi                | text                 | 
 population         | text                 | 
 power              | text                 | 
 power_source       | text                 | 
 public_transport   | text                 | 
 railway            | text                 | 
 ref                | text                 | 
 religion           | text                 | 
 route              | text                 | 
 service            | text                 | 
 shop               | text                 | 
 sport              | text                 | 
 surface            | text                 | 
 toll               | text                 | 
 tourism            | text                 | 
 tower:type         | text                 | 
 tunnel             | text                 | 
 water              | text                 | 
 waterway           | text                 | 
 wetland            | text                 | 
 width              | text                 | 
 wood               | text                 | 
 z_order            | integer              | 
 tags               | hstore               | 
 way                | geometry(Point,4326) | 
 way_geo            | geography            | 
 gid                | integer              | not null default nextval('...
Indexes:
    "planet_osm_point_pkey1" PRIMARY KEY, btree (gid)
    "idx_planet_osm_point_amenity" btree (amenity)
    "idx_planet_osm_point_waygeo" gist (way_geo)
    "planet_osm_point_index" gist (way)
    "planet_osm_point_pkey" btree (osm_id)

便利学校、餐厅和酒吧分别有8811、6291、779排。


这个查询应该有很长的路要走(是much快点):

WITH school AS (
   SELECT s.osm_id AS school_id, text 'school' AS type, s.osm_id, s.name, s.way_geo
   FROM   planet_osm_point s
        , LATERAL (
      SELECT  1 FROM planet_osm_point
      WHERE   ST_DWithin(way_geo, s.way_geo, 500, false)
      AND     amenity = 'bar'
      LIMIT   1  -- bar exists -- most selective first if possible
      ) b
        , LATERAL (
      SELECT  1 FROM planet_osm_point
      WHERE   ST_DWithin(way_geo, s.way_geo, 500, false)
      AND     amenity = 'restaurant'
      LIMIT   1  -- restaurant exists
      ) r
   WHERE  s.amenity = 'school'
   )
SELECT * FROM (
   TABLE school  -- schools

   UNION ALL  -- bars
   SELECT s.school_id, 'bar', x.*
   FROM   school s
        , LATERAL (
      SELECT  osm_id, name, way_geo
      FROM    planet_osm_point
      WHERE   ST_DWithin(way_geo, s.way_geo, 500, false)
      AND     amenity = 'bar'
      ) x

   UNION ALL  -- restaurants
   SELECT s.school_id, 'rest.', x.*
   FROM   school s
        , LATERAL (
      SELECT  osm_id, name, way_geo
      FROM    planet_osm_point
      WHERE   ST_DWithin(way_geo, s.way_geo, 500, false)
      AND     amenity = 'restaurant'
      ) x
   ) sub
ORDER BY school_id, (type <> 'school'), type, osm_id;

This is not与您的原始查询相同,而是您真正想要的,根据评论中的讨论:

我想要一份拥有 500 家以内餐厅和酒吧的学校列表 米,我需要每个学校的坐标及其对应的 餐厅和酒吧。

因此,此查询返回这些学校的列表,然后是附近的酒吧和餐馆。每组行都由osm_id栏中的学校school_id.

现在使用LATERAL连接,以利用空间 GiST 索引。

TABLE school只是简写SELECT * FROM school:

  • psql 中有 SELECT * FROM 的快捷方式吗?

表达方式(type <> 'school')首先对每组中的学校进行排序,因为:

  • SQL select 按日和月查询顺序

子查询sub在决赛中SELECT只需要通过这个表达式来排序。 AUNION查询限制附加的ORDER BY仅列出列,无表达式。

我重点关注您为了回答这个问题而提出的问题 -ignoring过滤其他 70 个文本列中任何一个的扩展要求。这确实是一个设计缺陷。搜索条件应集中于few列。或者您必须对所有 70 列建立索引,而像我要建议的多列索引几乎不是一个选择。仍然possible尽管 ...

Index

除了现有的:

"idx_planet_osm_point_waygeo" gist (way_geo)

如果总是在同一列上进行过滤,您可以创建一个多列索引涵盖您感兴趣的几列,所以仅索引扫描成为可能:

CREATE INDEX planet_osm_point_bar_idx ON planet_osm_point (amenity, name, osm_id)

Postgres 9.5

即将推出的 Postgres9.5介绍主要改进这恰好解决了您的情况:

  • 允许查询使用 GiST 索引对边界框索引对象(多边形、圆形)执行精确的距离过滤 (亚历山大·科罗特科夫、海基·林纳坎加斯)

    以前,需要一个公共表表达式来返回一个大的 按边界框距离排序然后过滤的行数 进一步进行更准确的非边界框距离计算。

  • 允许 GiST 索引执行仅索引扫描 (Anastasia Lubennikova, Heikki Linnakangas, Andreas Karlsson)

您对此特别感兴趣。现在你可以拥有一个single多列(覆盖)GiST 索引:

CREATE INDEX reservations_range_idx ON reservations
USING gist(amenity, way_geo, name, osm_id)

And:

  • 提高位图索引扫描性能(Teodor Sigaev、Tom Lane)

And:

  • 添加GROUP BY分析功能GROUPING SETS, CUBE and ROLLUP(安德鲁·吉尔斯、阿特里·夏尔马)

为什么?因为ROLLUP会简化我建议的查询。相关回答:

  • PostgreSQL 中的 Grouping() 等效项?

第一个 alpha 版本已于 2015 年 7 月 2 日发布。预计发布时间表:

这是9.5版本的alpha版本,表明一些变化 在发布之前功能仍然是可能的。 PostgreSQL 项目 将于8月份发布9.5 beta 1,之后定期发布 测试所需的额外测试版,直到最终版本发布 2015年底。

Basics

当然,一定不要忽视基础知识:

  • PostgreSQL Wiki 上的慢查询问题页面
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

对具有多个自联接的大型表执行缓慢的空间查询 的相关文章

  • 有没有办法阻止 SQL Express 2008 空闲?

    我使用 SQL Express 2008 作为 Web 应用程序的后端 问题是 Web 应用程序是在工作时间使用的 因此有时在午餐或休息时间 如果 20 分钟内没有用户登录 SQL Express 将进入空闲状态模式并释放其缓存 我知道这一
  • SQL:如何在按部分分组的查询中使用子查询?

    如何在按部分分组的查询中使用子查询 我使用 SQL Server 2008 R2 和 Delphi 2010 我收到此错误 Cannot perform an aggregate function on an expression cont
  • 在 postgresql 中查找和汇总具有重叠记录的日期范围

    我有一个大型数据集 我想对记录具有重叠时间的计数进行求和 例如 给定数据 id 1 name A start 2018 12 10 00 00 00 end 2018 12 20 00 00 00 count 34 id 2 name B
  • 加密数据库字段的好方法?

    我被要求加密数据库中的各种数据库字段 问题是这些字段在读取后需要解密 我在用着Django and SQL Server 2005 有什么好主意吗 See 在 SQL Server 2005 数据库中使用对称加密 https web arc
  • 由于键更改而尝试插入时外键约束失败

    我有一个 Content 对象 它引用多对多关系中的一组 Tag 对象 作为持久化新内容对象的一部分 我在 PostgreSQL 中查看标签是否已存在 如果存在 则将对其的引用添加到内容对象并尝试保存内容对象 我遇到的问题是 当我这样做时
  • sql查询获取从一月到当月的所有数据,即使没有记录

    我不擅长 sql 所以任何帮助世界都很棒 我有一个 SQL 查询 可以获取从一月到当月注册的记录 我的代码示例 SELECT DatePart YEAR p createStamp as TheYear DatePart MONTH p c
  • Reporting Services 在哪里存储其日志文件

    最相关的谷歌结果似乎表明 为了访问日志 我们必须将您自己的日志表部署到数据库并制作报告服务写入它 http technet microsoft com en us library ms157403 aspx 简而言之 Reporting S
  • JDBC插入实数数组

    我试图将一个真实的数组插入到 postgresql 数组中 该表的定义是 String sqlTable CREATE TABLE IF NOT EXISTS ccmBlock sampleId INTEGER block REAL 插入内
  • 数据库字段中的逗号分隔值

    我有一个产品表 该表中的每一行对应一个产品 并由唯一的 ID 标识 现在 每个产品都可以有多个与该产品关联的 代码 例如 Id Code 0001 IN ON ME OH 0002 ON VI AC ZO 0003 QA PS OO ME
  • 如何在MYSQL中将整个字符串小写并保持第一个大写[重复]

    这个问题在这里已经有答案了 我的表栏目 我预期的输出会在列中发生变化 Smith Allen Doyle Dennis Baker Waker 这是我尝试过的 但不起作用 UPDATE TABLE employee SET last nam
  • 内置函数将每个单词的第一个字母大写

    如果 SQL Server 中已存在此类函数 我不想为此创建自定义函数 输入字符串 This is my string to convert预期输出 This Is My String To Convert SET ANSI NULLS O
  • 如何通过 SQL 表关联 SQL 中的实体

    我是数据库设计的初学者 我需要为项目创建数据库 我可以用面向对象的术语解释我想要做什么 值得庆幸的是 数据库专家会很友善地向我解释如何在数据库方面处理这个问题 我想创建一个与位置实体 州 城市 有关系的用户 ID 名称 实体 所以在编程语言
  • SQL Server 连接其他表中不存在的位置

    Service Asset AssetService Id Name Id Name AssetId ServiceId
  • 获取带有计数的不同记录

    我有一张桌子personid and msg列 personid msg 1 msg1 2 msg2 2 msg3 3 msg4 1 msg2 我想得到总计msg对于每个personid 我正在尝试这个查询 select distinct
  • SQL 连接两个没有关系的表

    我有具有相同结构的不同表 我想通过其中一列将它们连接起来 问题是他们不共享该专栏中的信息 Table 1 Type A Name Value Table 2 Type B Name Value 结果表 在单列中 nameFromA name
  • 从 Getdate() 获取时间

    我想采取Getdate 结果 例如 2011 10 05 11 26 55 000 into 11 26 55 AM 我看过其他地方并发现 Select RIGHT CONVERT VARCHAR GETDATE 100 7 这给了我 11
  • 我不断收到错误“关系 [TABLE] 不存在”

    我一直在尝试查询数据库中的两个表 在服务器资源管理器中 我可以看到两个表 甚至可以看到其中的列 我们将它们称为 Schema table1 和 Schema table2 其中 Schema 的第一个字母大写 我尝试运行以下查询 selec
  • 快速将列的副本添加到 MySQL 表

    我需要一种快速的方法来复制表中的 DATETIME 列并为其指定一个新名称 我的表中有一个名为 myDate 的列 名为 myResults 我需要一个查询来在名为 newDate 的表中创建一个新列 该列的数据与 myDate 列完全相同
  • 使用加权行概率从 PostgreSQL 表中选择随机行

    输入示例 SELECT FROM test id percent 1 50 2 35 3 15 3 rows 你会如何编写这样的查询 平均 50 的时间我可以获得 id 1 的行 35 的时间 id 2 的行 15 的时间 id 3 的行
  • 如何通过SQL查询检查是否有JSON函数?

    有SQL 2016 中的 JSON 函数 https learn microsoft com en us sql t sql functions json functions transact sql例如 JSON VALUE JSON Q

随机推荐

  • 通过 CURL POST JSON 数据并抓取它

    我正在尝试传递 json 数据作为 cURL POST 的参数 然而 我坚持抓住它并将其保存在数据库上 卷曲文件 data array name gt Hagrid age gt 36 data string json encode dat
  • 在面板上绘制 Html 表格?

    如何生成常规的 html table 在 Sencha Touch 2 的面板上 每行的数据可能来自商店 它不像列表那样非常 移动 但我想在我的平板电脑应用程序上有一些详细面板 其中包含如下几个部分 header 1 table tr td
  • Codeigniter多个文件上传路径

    我正在构建一个应用程序 需要获取上传的文件并将它们放在单独的缩略图和全尺寸图像目录中 但是 config upload path uploads 只允许我选择一个上传路径 如何定义两个或多个上传路径 实际上您需要做的就是 重新初始化 上传类
  • 为什么 Laravel 或 Beanstalkd 会跳槽?

    我正在管理音频转换Laravel 队列 and 豆茎 监测者监督者 当用户上传音频文件时 它会转到AudioController php这会触发一个Queue push AudioProcess 它本身会触发exec sh some scr
  • JavaScript 从嵌套对象获取值[重复]

    这个问题在这里已经有答案了 如果这是我的目标 var obj bakery1 small name Small cookie price 0 75 large name Large cookie price 3 00 bakery2 sma
  • 使用 if 语句查找特定值

    您好 我在 Excel 中有一个很大的产品订单数据集 并且它将不断增长 数据集看起来像这样 Product Date Lsat24 Next24 Summary Buyer day1 AX1 2 1 2019 Checking invent
  • 在主线程上继续执行任务

    如果这是一个简单的问题 请原谅我 我无法用足够通用的方式来表达它来寻找答案 考虑这段代码 var task Task Factory StartNew gt Whatever task ContinueWith Callback TaskS
  • 递归 CTE - 获取后代(多对多关系)

    我拥有的 给定一棵树 或更类似于有向图 描述系统如何由其通用部分组成 现在让这个系统例如人体及其身体部位的节点 例如3可能是有左叶和右叶的肝脏 6 and 9 两者都有静脉 8 也可以在肝脏的任何未指定的位置找到 因此8 gt 3 而且也在
  • 如何快速从照片库中获取图像或电影路径

    我有这段代码 但在尝试获取图像路径时遇到问题 我在 google 和 stack over flow 中搜索 但我找到的解决方案是 Objective C 或在 swift 中不再工作的代码 这是我的代码 IBAction func cho
  • 如何使用 jQuery 为表格制作下拉列表过滤器?

    我想在特殊列中创建下拉表过滤器 例如我想为 已婚 列创建过滤器并从下拉列表中选择是或否 这是我的表 我需要一个 jquery 代码来帮助我制作过滤器 table class table table bordered table stripe
  • SQL分区在mysql中使用“不存在”

    我有下表 committee table commname profname commA bill commA jack commA piper commB bill commB piper 我正试图找到 吹笛者 所在的每个委员会中的教授
  • Chrome 配置文件未加载硒

    这是我的代码 from selenium import webdriver options webdriver ChromeOptions options add argument r user data dir C Users shahr
  • Keras:如何扩展validation_split以生成第三组,即测试集?

    我正在使用带有 TensorFlow 后端的 Keras 我使用带有 valid split 参数的 ImageDataGenerator 将数据拆分为训练集和验证集 因此 我使用 flow from directory 并将子集设置为 训
  • Vulkan 描述符绑定

    在我的 vulkan 应用程序中 当所有网格使用相同的纹理时 我曾经这样绘制网格 Updatedescriptorsets texture Command buffer record For each mesh Bind transform
  • Android - cam.setPreviewDisplay(holder) 遇到 IOError

    我正在尝试使用相机的图像作为动态壁纸的一部分 在我声明的引擎中 我有以下代码 public class Class extends WallpaperService Camera cam Override public void onCre
  • PHP 中提交文本的换行转换

    我设置了一个系统 供用户将他们的文章提交到我的数据库中 因为它只是 HTML 所以我不想指望他们知道如何输入 br 每次有换行符时 所以我使用 PHP 函数nl2br 在输入上 我还提供了一个文章修改工具 它将把他们的文章带回到表单中 但是
  • 如何使用二维数组插入表格行和列

    所以我有 2 个数组 它们是 var num 1 2 3 var cars 萨博 沃尔沃 宝马 我制作了一个按钮 它将添加行和列 并将 2 个数组的值放入表中 但是我不知道如何将其变成二维数组并将其显示在我的桌子上
  • 使用 Selenium Python 发送文本时出现 InvalidElementStateException 无效元素状态:元素必须是用户可编辑的才能清除它”错误

    我在 Django 中有一个像这样的输入 HTML 元素
  • 在 r 中使用 lapply 函数和列表

    d1 lt data frame col one c 1 2 3 col two c 4 5 6 d2 lt data frame col one c 1 1 1 col two c 6 5 4 d3 lt data frame col o
  • 对具有多个自联接的大型表执行缓慢的空间查询

    我正在 Postgres 9 3 9 中的一个大表上进行查询 它是一个空间数据集 并且具有空间索引 比如说 我需要找到 3 种类型的物体 A B 和 C 标准是 B 和 C 都在 A 的一定距离内 比如 500 米 我的查询是这样的 sel