Mic*_*l B 6 sql-server spatial sql-server-2016
我有一些表,其中包含存储为 lat long 对的属性的事务。(然后在我的示例架构中有更多的列和数据点)。
一个常见的请求是查找特定点 X 英里范围内发生的交易,并且只检索附近每个房产发生的 5 个最近的交易。
为了完成这项工作,我决定添加一个视图来封装最新的逻辑:
create or alter view dbo.v_example
with schemabinding as
select example_id
,transaction_dt
,latitude
,longitude
,latlong
,most_recent= iif(row_number() over (partition by latitude,longitude order by transaction_dt desc) < 5,1,null)
from dbo.example;
Run Code Online (Sandbox Code Playgroud)
所以一个查询可能看起来像这样:
create or alter view dbo.v_example
with schemabinding as
select example_id
,transaction_dt
,latitude
,longitude
,latlong
,most_recent= iif(row_number() over (partition by latitude,longitude order by transaction_dt desc) < 5,1,null)
from dbo.example;
Run Code Online (Sandbox Code Playgroud)
不幸的是,当我通过视图查询时,SQL Server 不想使用空间索引。如果我删除schemabinding
并尝试在视图上添加提示,我会发现查询处理器无法创建计划。
如何封装逻辑并仍然让它使用我的空间索引?
该表要大得多,扫描它然后进行聚集索引查找然后逐点查找要慢得多。
窗口函数和视图
最近我回答了一个关于视图和窗口函数的不同问题,但并非所有给出的答案都适用于这里。
两个不同之处在于此示例geography
在窗口函数上使用数据类型和过滤器。这里正的部分是,你是不是绑定到窗口功能,可以使用像CROSS APPLY
得到最接近的结果一个latitude
与longitude
组合。
简而言之,窗口函数是在应用过滤之前计算的。
例如
where latlong.STDistance(geography::Point(40,-74,4326)) <=1609.344e1
and most_recent = 1
Run Code Online (Sandbox Code Playgroud)
更多信息可以在Paul White 2013 年关于窗口函数和视图的博客文章中找到。
测试
作为第一个测试,ROW_NUMBER()
从视图中省略时结果很清楚
create or alter view dbo.v_example2
with schemabinding as
select example_id
,transaction_dt
,latitude
,longitude
,latlong
from dbo.example;
set statistics xml on;
select *
from dbo.v_example2
where latlong.STDistance(geography::Point(40,-74,4326)) <=1609.344e1
Run Code Online (Sandbox Code Playgroud)
产生了预期的高性能查询计划。
保持视图+窗口功能
您可以选择在之后添加窗口函数,如下所示:
create or alter view dbo.v_example2
with schemabinding as
select example_id
,transaction_dt
,latitude
,longitude
,latlong
from dbo.example;
Run Code Online (Sandbox Code Playgroud)
&
set statistics xml on;
;WITH CTE AS
(
select most_recent= iif(row_number() over (partition by latitude,longitude order by transaction_dt desc) < 5,1,null)
,*
from dbo.v_example2
where latlong.STDistance(geography::Point(40,-74,4326)) <=1609.344e1
)
SELECT
*
FROM CTE
WHERE most_recent is not null;
Run Code Online (Sandbox Code Playgroud)
再次产生预期的执行计划。
使用内联表值函数+窗口函数
CREATE FUNCTION dbo.F_Example
(
@P1 INT
)
RETURNS TABLE
WITH SCHEMABINDING AS
RETURN
(
SELECT example_id
,transaction_dt
,latitude
,longitude
,latlong
,most_recent FROM
(
select example_id
,transaction_dt
,latitude
,longitude
,latlong
,most_recent= iif(row_number() over (partition by latitude,longitude order by transaction_dt desc) < 5,1,null)
from dbo.example
WHERE latlong.STDistance(geography::Point(40,-74,4326)) <= 1609.344e1
) AS A
WHERE most_recent= @P1
);
EXEC SP_EXECUTESQL
N'SELECT * FROM dbo.F_Example(@P1)',N'@P1 INT',@P1 = 1
Run Code Online (Sandbox Code Playgroud)
产生您预期的查询计划。
使用 CTE + 窗口函数
;WITH CTE AS
(
select example_id
,transaction_dt
,latitude
,longitude
,latlong
,most_recent= row_number() over (partition by latitude,longitude order by transaction_dt desc)
FROM dbo.example
WHERE latlong.STDistance(geography::Point(40,-74,4326)) <=1609.344e1
)
SELECT example_id
,transaction_dt
,latitude
,longitude
,latlong
,most_recent
FROM CTE2
WHERE most_recent < 5;
Run Code Online (Sandbox Code Playgroud)
使用CROSS APPLY
和TOP(4)
不使用窗口函数
SELECT example_id
,a.transaction_dt
,latitude
,longitude
,latlong
from dbo.example e1
CROSS APPLY(
SELECT TOP(4) transaction_dt
FROM dbo.example e2
WHERE e1.latitude = e2.latitude and e1.longitude = e2.longitude
GROUP BY latitude,longitude,transaction_dt
ORDER BY transaction_dt desc
) as a
WHERE latlong.STDistance(geography::Point(40,-74,4326)) <=1609.344e1
AND e1.transaction_dt = a.transaction_dt
ORDER BY transaction_dt desc;
Run Code Online (Sandbox Code Playgroud)
我使用,top(4)
因为结果集是基于< 5
不<= 5
使用该CROSS APPLY
方法时,还需要添加一个索引,以删除索引假脱机和排序:
CREATE INDEX IX_latitude_longitude_transaction_dt
ON dbo.example(latitude,longitude,transaction_dt);
Run Code Online (Sandbox Code Playgroud)
不用说,您可以将CROSS APPLY
解决方案添加到视图中,并以与以前相同的方式对其进行查询,这里是一个示例。一个 DB<>Fiddle here 中的所有上述示例。
MichaelB 的结束解决方案评论
感谢 Randi,我能够使用交叉应用的想法将我的逻辑重写为选择子查询。作为一个额外的好处,如果我不在我选择的列中提及该字段,我不会受到性能影响。
create or alter view dbo.v_example2
with schemabinding as
select example_id
,transaction_dt
,latitude
,longitude
,latlong
,most_recent= (
select a.transaction_dt
intersect
select top 5 b.transaction_dt
from dbo.example b
where a.latitude=b.latitude
and a.longitude=b.longitude
order by transaction_dt desc
)
from dbo.example a;
Run Code Online (Sandbox Code Playgroud)
&
select example_id
,transaction_dt
,latitude
,longitude
,latlong
from dbo.v_example2
where latlong.STDistance(geography::Point(40,-74,4326)) <=1609.344e1
and most_recent is not null
Run Code Online (Sandbox Code Playgroud)
归档时间: |
|
查看次数: |
260 次 |
最近记录: |