Sql 地理定位自连接太慢

Sql 地理定位自连接太慢,sql,geolocation,sas,self-join,Sql,Geolocation,Sas,Self Join,我正在尝试使用如下所示的自连接,在一个巨大的表(1m+记录)中获取每个记录50英里内的所有记录数: proc sql; create table lab as select distinct a.id, sum(case when b.value="New York" then 1 else 0 end) from latlon a, latlon b where a.id <> b.id and geodist(a.lat,a.lon,b

我正在尝试使用如下所示的自连接,在一个巨大的表(1m+记录)中获取每个记录50英里内的所有记录数:

proc sql;
    create table lab as
    select distinct a.id, sum(case when b.value="New York" then 1 else 0 end) 
    from latlon a, latlon b
    where a.id <> b.id 
    and geodist(a.lat,a.lon,b.lat,b.lon,"M") <= 50 
    and a.state = b.state;
proc-sql;
将表格实验室创建为
选择不同的a.id、sum(b.value=“New York”时为1,否则为0结束)
来自拉特隆a,拉特隆b
其中a.id b.id

而地球学家(a.lat,a.lon,b.lat,b.lon,“M”)你的问题措辞含糊不清——我把它解释为“给我所有(a,b)彼此相距50英里以内的城市对。”纽约市的特例似乎是一次性测试——问题不是(很简单,在O(n)时间内)找到纽约市50英里以内的所有城市

与其计算大圆距离,不如使用简单的加法和简单的边界框来计算曼哈顿距离。给定小于50英里的(A,B)城市元组,很容易删掉大圆(或欧几里德)距离小于50英里的少数(在对角线上)

您没有向我们显示描述后端优化器计划的
EXPLAIN
输出

您没有告诉我们关于
latlon
表上的索引

我不熟悉SAS RDBMS。Oracle、MySQL和其他数据库必须支持多维索引。本质上,它们将高阶坐标位合并到低阶坐标位,以构建四叉树索引。这项技术可能会对您的查询有所帮助

您的
DISTINCT
关键字将对查询计划产生重大影响。通常,它会强制执行表扫描和文件排序。考虑删除它。

上的等分法似乎是错误的,但也许你不在乎三州的大都市区和州边界附近类似的人口密集地区

您肯定希望
WHERE
子句删除距离当前
a
行超过50英里的
b
行:

  • 太北了,或者
  • 太南了,或者
  • 太偏西了,或者
  • 太远的东方
  • 这些条件中的每一个都归结为一个简单的范围查询,RDBMS后端可以根据索引进行评估和优化。不幸的是,如果它选择纬度索引,磁盘上的任何经度索引都将被忽略,反之亦然。这促使您使用供应商的地理空间支持。

    您使用的
    geodist()
    功能没有机会利用任何索引。所以,你有一个充其量是O(n**2)的算法。那会很慢的

    不过,您可以利用球面几何体的一个简单事实来访问可索引查询。一个纬度(南北)相当于60海里、69法定英里或111.111公里。英国对海里的定义最初等于一分钟。最初的拿破仑米被定义为从赤道到极点距离的万分之一,也被定义为90度

    (这些定义取决于地球是球形的假设。事实并非如此。如果你是一名土木工程师,这些定义就不成立了。如果你用它们来设计停车场,下雨时会有一些肮脏的水坑,并会侵蚀邻居的财产。)

    因此,您需要使用一个边界范围。假设你的纬度值a.lat和b.lat是以度为单位的,那么其中两个肯定相距超过50法定英里,除非

         a.lat BETWEEN b.lat - 50.0/69.0 AND b.lat + 50.0/69.0
    
    让我们重构您的查询。(我不了解纽约的情况,所以我忽略了它。你可以把它加回去。)这将给出相距50英里以内的所有地方的ID。(我在这里使用的是21世纪的连接语法)

    选择不同的a.id、b.id
    来自拉特隆a
    在a.idb.id上连接latlon b
    a.lat介于b.lat-50.0/69.0和b.lat+50.0/69.0之间
    和a.state=b.state
    
    和geodist(a.lat,a.lon,b.lat,b.lon,“M”),我不明白为什么需要执行连接来选择表示一个选定位置范围内的位置的记录。另一方面,如果你想要,每一条记录,所有其他代表50英里内点的记录,那么我不明白你为什么会感到惊讶,因为这需要永远。我确实想要每一条记录。我想知道是否有一种方法可以做得更好并节省几个小时,因为我可能需要对同样庞大的不同数据集多次运行此代码。该查询将处理表中的每个记录,并且您在其中一个条件下有一个函数,因此,数据库必须逐个处理组合,而不是使用集合。由于要将每个记录与处于相同状态的所有记录进行比较,因此假设位置均匀分布,则查询正在处理(20000x2000)x50=200000000000+组合。另外,
    geodist()
    函数不喜欢一个简单的操作,所以6个小时听起来没那么糟糕。你应该预先计算这些距离并将它们存储在某个地方。子句
    和a.state=b.state
    将删除许多彼此相距50英里但处于不同状态的结果。不确定这是否是有意的,或者是一个优化错误。请编辑您的问题以给出一个清晰的规范。即使您的“更新”也不清楚。(不要“更新”或评论、修改。)你想让纽约人的数量小于50公里,每个人最多12个家乡??看在上帝的份上。不要计算a到b和b到a2。将ny测试放在where 3中。使用计数4。使用地理空间dbms功能5。可能首先选择与ny 6相关的位置和距离。在测地线之前使用近似边界。例如距纽约的距离,例如δx平方+δy平方+δz平方。顺便说一句,这种优化在距离任何一个极点50英里的范围内都不起作用。SAS RDBMS。哈哈。
    select distinct a.id, b.id 
    from latlon a
    JOIN latlon b    ON a.id<>b.id
                    AND a.lat BETWEEN b.lat - 50.0/69.0 AND b.lat + 50.0/69.0
                    AND a.state = b.state
                    AND geodist(a.lat,a.lon,b.lat,b.lon,"M") <= 50