SQL Spatial索引包括类别字段

时间:2017-03-23 13:03:52

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

我在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或数字字段添加到空间索引的方法。

我该怎么做才能加快速度?

1 个答案:

答案 0 :(得分:0)

我会从这样的事情开始:

--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行,索引的效果几乎是不可察觉的。

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

让我们快速回顾一下:

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