【问题标题】:SQL Spatial Indexing including category field(s)SQL 空间索引,包括类别字段
【发布时间】:2017-03-23 13:03:51
【问题描述】:

我在存储项目位置的 2012 SQL 数据库中的地理字段上设置了空间索引。大约有 15,000 项。

我需要返回给定 Lat/Lng 半径 N 公里范围内的项目总数。

我能做到,而且速度很快。

DECLARE @radius GEOGRAPHY = GEOGRAPHY::Point(@Lat, @Lng, 4326).STBuffer(@RadiusInMetres*1000)

SELECT
COUNT(*) AS Total
FROM dbo.Items i
WHERE
i.LatLngGeo.STIntersects(@radius) = 1

但是,我现在需要做的是按几个字段过滤,以获取与给定类别和价格匹配的项目。

DECLARE @radius GEOGRAPHY = GEOGRAPHY::Point(@Lat, @Lng, 4326).STBuffer(@RadiusInMetres*1000)

SELECT
COUNT(*) AS Total
FROM dbo.Items i
WHERE
i.LatLngGeo.STIntersects(@radius) = 1 AND
(i.Category = @Category OR @Category is null) AND
(i.Price < @Price OR @Price is null)

这会持续大约 10 多秒,我找不到将 varchar 或数字字段添加到空间索引的方法。

我能做些什么来加快速度?

【问题讨论】:

  • 查询计划是什么样的?是使用空间索引吗?

标签: sql sql-server spatial spatial-query sqlgeography


【解决方案1】:

我会从这样的开始:

--Query 1 - use a CTE to split the two filters
DECLARE @radius GEOGRAPHY = GEOGRAPHY::Point(@Lat, @Lng, 4326).STBuffer(@RadiusInMetres * 1000);

WITH InRadius AS (
    SELECT * FROM dbo.Items WHERE LatLngGeo.STIntersects(@radius) = 1)
SELECT
    COUNT(*)
FROM
    InRadius
WHERE
    ISNULL(@Category, Category) = Category
    AND ISNULL(@Price, Price) = Price;
GO
--Query 2 - use a temp table to *definitely( split the two filters
DECLARE @radius GEOGRAPHY = GEOGRAPHY::Point(@Lat, @Lng, 4326).STBuffer(@RadiusInMetres * 1000);

IF OBJECT_ID('tempdb..#temp') IS NOT NULL
    DROP TABLE #temp;
WITH InRadius AS (
    SELECT * FROM dbo.Items WHERE LatLngGeo.STIntersects(@radius) = 1)
SELECT * INTO #temp FROM InRadius;
SELECT
    COUNT(*)
FROM
    #temp
WHERE
    ISNULL(@Category, Category) = Category
    AND ISNULL(@Price, Price) = Price;

运行这些查询几次,然后将它们与原始脚本进行比较。

另一个技巧是复制您的原始查询,然后查看执行计划。您正在寻找的是每个查询的百分比拆分,理想情况下应该是 98%:1%:1%,即原始查询将占用 98% 的工作,但它可能看起来确实非常不同。

如果这没有帮助,并且您对临时表没问题,请尝试在临时表上添加一个符合您过滤条件的索引。然而,只有 15,00 行,索引的影响几乎是难以察觉的。

最后,您可以将加载到临时表中的数据限制为是您要过滤的项目,因为您似乎想要的只是最后的计数?

让我们快速回顾一下:

  • 提取与您的空间查询匹配的数据(您说这已经很快了);
  • 根据结果丢弃任何 GEOGRAPHY,将它们存储在临时表中;
  • 索引临时表以加速任何过滤器;
  • 现在您的 COUNT(*) 应该只是在数据的子集上(没有空间数据),优化器将无法尝试将其与邻近过滤器结合;
  • 利润!

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2012-06-22
    • 2012-05-10
    • 1970-01-01
    • 2021-03-23
    • 1970-01-01
    • 1970-01-01
    • 2012-11-03
    • 1970-01-01
    相关资源
    最近更新 更多