空间索引应用

前篇:http://www.cnblogs.com/Gin-23333/p/4995199.html  _(:з」∠)_
空间索引,一个专门用来处理空间数据计算的索引,目前支持 STContains()、STDistance()、STEquals()、STIntersects()、STOverlaps()、STTouches() 和 STWithin()
然而通常我们平常使用的,通常就是 STDistance() 求两点之间的距离。
 
先看下空间索引的创建语法,简单的必填项就是酱~
CREATE SPATIAL INDEX IndexName ON #Tmp(GeographyCoordinate)  
    WITH (BOUNDING_BOX=(XMIN=-180,YMIN=-90,XMAX=180,YMAX=90))   
 
*最好要保证最大最小值要覆盖整个坐标系的值域,否则不能最有效使用索引
 
好,上个不太严谨的Sample
然后我们测试一下。创建了一个模拟酒店表 (数据量大概是45W左右)
    Hotel,只有主键,其他没有任何东西,坐标存在 GeographyCoordinate 的栏位里面
    Tmp_Hotel1 ,结构和Hotel完全一致,并在GeographyCoordinate 里面创建了一个空间索引。
 
DECLARE @DT DATETIME=GETDATE()
DECLARE @GEO GEOMETRY=GEOMETRY::STGeomFromText('POINT(39.8666 20.017)',0)
 
SELECT DATEDIFF(ms,@DT,GETDATE())
SELECT *
    FROM Hotel
        WHERE GeographyCoordinate.STDistance(@GEO) < 0.43       
SELECT DATEDIFF(ms,@DT,GETDATE())
SELECT *
    FROM Tmp_Hotel1
        WHERE GeographyCoordinate.STDistance(@GEO) < 0.43      SELECT DATEDIFF(ms,@DT,GETDATE())

结果如下 ,可见第一句执行了1320ms ,第二句仅仅执行了 50ms,这是一个坐标在45W数据里面的大概分析情况

然后我们把比对的数据扩大10条,比如说我要找这10个酒店5公里之内的其他酒店。首先我会建个临时表存放那10间酒店的位置。

--随机取10条,避免被读取顺序误导
SELECT TOP 10 ID,
              GeographyCoordinate
        INTO #Tmp
    FROM dbo.Hotel 
        WHERE ORDER BY NEWID()

 

顺手开个统计看看

SET STATISTICS IO ON

--不使用索引直接关联
SELECT a.*,b.ID
    FROM dbo.Hotel a
        INNER JOIN #Tmp b ON a.GeographyCoordinate.STDistance(b.GeographyCoordinate) < 0.43  

--使用索引关联
SELECT a.*,b.ID
    FROM dbo.Tmp_Hotel1 a
        INNER JOIN #Tmp b ON a.GeographyCoordinate.STDistance(b.GeographyCoordinate) < 0.43        

结果 28s,还有8次的Hotel物理扫描 ╮(╯_╰)╭

表 '#Tmp'。扫描计数 1,逻辑读取 9 次,物理读取 0 次,预读 0 次,lob 逻辑读取 0 次,lob 物理读取 0 次,lob 预读 0 次。
表 'Hotel'。扫描计数 8,逻辑读取 241960 次,物理读取 0 次,预读 0 次,lob 逻辑读取 0 次,lob 物理读取 0 次,lob 预读 0 次。
表 'Worktable'。扫描计数 0,逻辑读取 0 次,物理读取 0 次,预读 0 次,lob 逻辑读取 0 次,lob 物理读取 0 次,lob 预读 0 次。
表 'Worktable'。扫描计数 0,逻辑读取 0 次,物理读取 0 次,预读 0 次,lob 逻辑读取 0 次,lob 物理读取 0 次,lob 预读 0 次。

 

第二位空间索引选手的表现呢,执行时间18s。看起来比没有索引好不了多少

表 'Tmp_Hotel1'。扫描计数 0,逻辑读取 1332493 次,物理读取 0 次,预读 0 次,lob 逻辑读取 0 次,lob 物理读取 0 次,lob 预读 0 次。
表 'Worktable'。扫描计数 0,逻辑读取 0 次,物理读取 0 次,预读 0 次,lob 逻辑读取 0 次,lob 物理读取 0 次,lob 预读 0 次。
表 'Workfile'。扫描计数 48,逻辑读取 1384 次,物理读取 96 次,预读 1440 次,lob 逻辑读取 0 次,lob 物理读取 0 次,lob 预读 0 次。
表 'extended_index_907150277_384000'。扫描计数 5413,逻辑读取 18390 次,物理读取 0 次,预读 0 次,lob 逻辑读取 0 次,lob 物理读取 0 次,lob 预读 0 次。
表 '#Tmp'。扫描计数 1,逻辑读取 9 次,物理读取 0 次,预读 0 次,lob 逻辑读取 0 次,lob 物理读取 0 次,lob 预读 0 次。

 

然而,根据资料,欲用空间索引,必先在Where 和 OrderBy 使用效果更佳。加上Order By 试下

SELECT a.*,b.ID
    FROM dbo.Tmp_Hotel1 a
        INNER JOIN #Tmp b ON a.GeographyCoordinate.STDistance(b.GeographyCoordinate) < 0.43
        ORDER BY a.GeographyCoordinate.STDistance(b.GeographyCoordinate)

执行时间6S,比没有OrderBy 选手好多了~

表 '#Tmp'。扫描计数 1,逻辑读取 9 次,物理读取 0 次,预读 0 次,lob 逻辑读取 0 次,lob 物理读取 0 次,lob 预读 0 次。
表 'Tmp_Hotel1'。扫描计数 0,逻辑读取 1332549 次,物理读取 0 次,预读 0 次,lob 逻辑读取 0 次,lob 物理读取 0 次,lob 预读 0 次。
表 'Worktable'。扫描计数 0,逻辑读取 0 次,物理读取 0 次,预读 0 次,lob 逻辑读取 0 次,lob 物理读取 0 次,lob 预读 0 次。
表 'Workfile'。扫描计数 48,逻辑读取 1440 次,物理读取 96 次,预读 1440 次,lob 逻辑读取 0 次,lob 物理读取 0 次,lob 预读 0 次。
表 'extended_index_907150277_384000'。扫描计数 5413,逻辑读取 18525 次,物理读取 0 次,预读 0 次,lob 逻辑读取 0 次,lob 物理读取 0 次,lob 预读 0 次。
表 'Worktable'。扫描计数 0,逻辑读取 0 次,物理读取 0 次,预读 0 次,lob 逻辑读取 0 次,lob 物理读取 0 次,lob 预读 0 次。

看IO其实两边差不多,为什么时间差辣么远?我看了一下才发现,加上了OrderBy 之后,执行计划会使用并行,所以速度刷一下上去了

--尝试一下看看帮#Tmp加个空间索引会不会有更大提升
ALTER TABLE #Tmp ADD CONSTRAINT PK_Tmp PRIMARY KEY (ID)
CREATE SPATIAL INDEX #IX_Tmp ON #Tmp(GeographyCoordinate)
    WITH (BOUNDING_BOX=(XMIN=-180,YMIN=-90,XMAX=180,YMAX=90))

再执行一下第二句,执行时间也是差不多,应该是临时表数据量太少,没有走到索引,╮(╯_╰)╭。

表 '#Tmp'。扫描计数 1,逻辑读取 3 次,物理读取 0 次,预读 0 次,lob 逻辑读取 0 次,lob 物理读取 0 次,lob 预读 0 次。
表 'Tmp_Hotel1'。扫描计数 0,逻辑读取 1332553 次,物理读取 0 次,预读 0 次,lob 逻辑读取 0 次,lob 物理读取 0 次,lob 预读 0 次。
表 'Worktable'。扫描计数 0,逻辑读取 0 次,物理读取 0 次,预读 0 次,lob 逻辑读取 0 次,lob 物理读取 0 次,lob 预读 0 次。
表 'Workfile'。扫描计数 48,逻辑读取 1376 次,物理读取 96 次,预读 1440 次,lob 逻辑读取 0 次,lob 物理读取 0 次,lob 预读 0 次。
表 'extended_index_907150277_384000'。扫描计数 5413,逻辑读取 18546 次,物理读取 0 次,预读 0 次,lob 逻辑读取 0 次,lob 物理读取 0 次,lob 预读 0 次。
表 'Worktable'。扫描计数 0,逻辑读取 0 次,物理读取 0 次,预读 0 次,lob 逻辑读取 0 次,lob 物理读取 0 次,lob 预读 0 次。

 

其实到这里,我觉得有个定性的认识就差不多了。

总结:要使用空间索引

1、主要对付查询 STDistance  函数等问题。

2、建索引的时候边界范围要禁可能包含出现的值域

3、在Where条件和 OrderBy 都要使用 STDistance 函数

4、特别注意Null 值会影响空间索引的性能

 

写在最后:

其实我也想过一下其他替代STDistance建空间索引的方式。

1、建一个对照表,预先做个笛卡尔积,这样就可以不用调度函数  (想到此方法先给自己2个耳光,维护成本太高了)

2、可以把XY值抽取出来,用个游标来做数值计算,不使用 STDistance 进行计算。这样我测试过性能还算可以。(但是逼格不够高,而且看起来有点蠢╮(╯_╰)╭)

3、一般来说可以用Top 或者其他条件加强筛选,减少待计算的数据量,也有助于提高性能。

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 
 
 

转载于:https://www.cnblogs.com/Gin-23333/p/5001109.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
提供的源码资源涵盖了安卓应用、小程序、Python应用和Java应用等多个领域,每个领域都包含了丰富的实例和项目。这些源码都是基于各自平台的最新技术和标准编写,确保了在对应环境下能够无缝运行。同时,源码中配备了详细的注释和文档,帮助用户快速理解代码结构和实现逻辑。 适用人群: 这些源码资源特别适合大学生群体。无论你是计算机相关专业的学生,还是对其他领域编程感兴趣的学生,这些资源都能为你提供宝贵的学习和实践机会。通过学习和运行这些源码,你可以掌握各平台开发的基础知识,提升编程能力和项目实战经验。 使用场景及目标: 在学习阶段,你可以利用这些源码资源进行课程实践、课外项目或毕业设计。通过分析和运行源码,你将深入了解各平台开发的技术细节和最佳实践,逐步培养起自己的项目开发和问题解决能力。此外,在求职或创业过程中,具备跨平台开发能力的大学生将更具竞争力。 其他说明: 为了确保源码资源的可运行性和易用性,特别注意了以下几点:首先,每份源码都提供了详细的运行环境和依赖说明,确保用户能够轻松搭建起开发环境;其次,源码中的注释和文档都非常完善,方便用户快速上手和理解代码;最后,我会定期更新这些源码资源,以适应各平台技术的最新发展和市场需求。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值