我试图生成涉及重叠地理边界的报告。
作为简化示例,邮政编码在DMA(指定的营销区域)内。
我有描述DMA多边形形状的SQL地理数据,以及描述美国邮政编码多边形形状的SQL地理数据。
我想知道:
我能够使用STWithin和STIntersection方法获取此信息,比较这些区域。但是,我遇到了一些严重的性能瓶颈。
我做的第一件事是计算并记录DMA和邮政编码形状的外边界框,将它们保存在表格中并编入索引。这使我可以排除每个不可能与DMA重叠的邮政编码。
示例:
<ShowPlanXML xmlns="http://schemas.microsoft.com/sqlserver/2004/07/showplan" Version="1.5" Build="13.0.4001.0">
<BatchSequence>
<Batch>
<Statements>
<StmtSimple StatementText="SELECT ID
FROM Locations
WHERE LocationType = 5
AND CountryCode = 'US'
AND NOT (
 MaxLat < 32.750
OR MinLat > 34.823
OR MaxLng < -118.951
OR MinLng > -117.646
)" StatementId="1" StatementCompId="1" StatementType="SELECT" RetrievedFromCache="false" StatementSubTreeCost="0.0226731" StatementEstRows="88.5595" SecurityPolicyApplied="false" StatementOptmLevel="TRIVIAL" QueryHash="0xA63252C178F68E1" QueryPlanHash="0x48CBE2D413E8362B" CardinalityEstimationModelVersion="70">
<StatementSetOptions QUOTED_IDENTIFIER="true" ARITHABORT="true" CONCAT_NULL_YIELDS_NULL="true" ANSI_NULLS="true" ANSI_PADDING="true" ANSI_WARNINGS="true" NUMERIC_ROUNDABORT="false"></StatementSetOptions>
<QueryPlan CachedPlanSize="24" CompileTime="3" CompileCPU="3" CompileMemory="344">
<MemoryGrantInfo SerialRequiredMemory="0" SerialDesiredMemory="0"></MemoryGrantInfo>
<OptimizerHardwareDependentProperties EstimatedAvailableMemoryGrant="2949120" EstimatedPagesCached="5898240" EstimatedAvailableDegreeOfParallelism="8" MaxCompileMemory="635169608"></OptimizerHardwareDependentProperties>
<RelOp NodeId="0" PhysicalOp="Nested Loops" LogicalOp="Inner Join" EstimateRows="88.5595" EstimatedRowsRead="1466.67" EstimateIO="0.0209028" EstimateCPU="0.00177034" AvgRowSize="50" EstimatedTotalSubtreeCost="0.0226731" TableCardinality="86546" Parallel="0" EstimateRebinds="0" EstimateRewinds="0" EstimatedExecutionMode="Row">
<OutputList>
<ColumnReference Database="****" Schema="[dbo]" Table="[Locations]" Column="ID"></ColumnReference>
</OutputList>
<NestedLoops Optimized="0">
<OuterReferences>
<ColumnReference Column="Expr1003"></ColumnReference>
<ColumnReference Column="Expr1004"></ColumnReference>
<ColumnReference Column="Expr1002"></ColumnReference>
</OuterReferences>
<RelOp NodeId="1" PhysicalOp="Compute Scalar" LogicalOp="Compute Scalar" EstimateRows="1" EstimateIO="0" EstimateCPU="0" AvgRowSize="37" EstimatedTotalSubtreeCost="0" Parallel="0" EstimateRebinds="0" EstimateRewinds="0" EstimatedExecutionMode="Row">
<OutputList>
<ColumnReference Column="Expr1003"></ColumnReference>
<ColumnReference Column="Expr1004"></ColumnReference>
<ColumnReference Column="Expr1002"></ColumnReference>
</OutputList>
<ComputeScalar>
<DefinedValues>
<DefinedValue>
<ValueVector>
<ColumnReference Column="Expr1003"></ColumnReference>
<ColumnReference Column="Expr1004"></ColumnReference>
<ColumnReference Column="Expr1002"></ColumnReference>
</ValueVector>
<ScalarOperator ScalarString="GetRangeWithMismatchedTypes(NULL,(34.823),(42))">
<Intrinsic FunctionName="GetRangeWithMismatchedTypes">
<ScalarOperator>
<Const ConstValue="NULL"></Const>
</ScalarOperator>
<ScalarOperator>
<Const ConstValue="(34.823)"></Const>
</ScalarOperator>
<ScalarOperator>
<Const ConstValue="(42)"></Const>
</ScalarOperator>
</Intrinsic>
</ScalarOperator>
</DefinedValue>
</DefinedValues>
<RelOp NodeId="2" PhysicalOp="Constant Scan" LogicalOp="Constant Scan" EstimateRows="1" EstimateIO="0" EstimateCPU="0" AvgRowSize="0" EstimatedTotalSubtreeCost="0" Parallel="0" EstimateRebinds="0" EstimateRewinds="0" EstimatedExecutionMode="Row">
<OutputList></OutputList>
<ConstantScan></ConstantScan>
</RelOp>
</ComputeScalar>
</RelOp>
<RelOp NodeId="3" PhysicalOp="Index Seek" LogicalOp="Index Seek" EstimateRows="88.5595" EstimatedRowsRead="1466.67" EstimateIO="0.0209028" EstimateCPU="0.00177034" AvgRowSize="50" EstimatedTotalSubtreeCost="0.0226731" TableCardinality="86546" Parallel="0" EstimateRebinds="0" EstimateRewinds="0" EstimatedExecutionMode="Row">
<OutputList>
<ColumnReference Database="****" Schema="[dbo]" Table="[Locations]" Column="ID"></ColumnReference>
</OutputList>
<IndexScan Ordered="1" ScanDirection="FORWARD" ForcedIndex="0" ForceSeek="0" ForceScan="0" NoExpandHint="0" Storage="RowStore">
<DefinedValues>
<DefinedValue>
<ColumnReference Database="****" Schema="[dbo]" Table="[Locations]" Column="ID"></ColumnReference>
</DefinedValue>
</DefinedValues>
<Object Database="****" Schema="[dbo]" Table="[Locations]" Index="[IX_MinMax]" IndexKind="NonClustered" Storage="RowStore"></Object>
<SeekPredicates>
<SeekPredicateNew>
<SeekKeys>
<Prefix ScanType="EQ">
<RangeColumns>
<ColumnReference Database="****" Schema="[dbo]" Table="[Locations]" Column="LocationType"></ColumnReference>
<ColumnReference Database="****" Schema="[dbo]" Table="[Locations]" Column="CountryCode"></ColumnReference>
</RangeColumns>
<RangeExpressions>
<ScalarOperator ScalarString="(5)">
<Const ConstValue="(5)"></Const>
</ScalarOperator>
<ScalarOperator ScalarString="N'US'">
<Const ConstValue="N'US'"></Const>
</ScalarOperator>
</RangeExpressions>
</Prefix>
<StartRange ScanType="GT">
<RangeColumns>
<ColumnReference Database="****" Schema="[dbo]" Table="[Locations]" Column="MinLat"></ColumnReference>
</RangeColumns>
<RangeExpressions>
<ScalarOperator ScalarString="[Expr1003]">
<Identifier>
<ColumnReference Column="Expr1003"></ColumnReference>
</Identifier>
</ScalarOperator>
</RangeExpressions>
</StartRange>
<EndRange ScanType="LT">
<RangeColumns>
<ColumnReference Database="****" Schema="[dbo]" Table="[Locations]" Column="MinLat"></ColumnReference>
</RangeColumns>
<RangeExpressions>
<ScalarOperator ScalarString="[Expr1004]">
<Identifier>
<ColumnReference Column="Expr1004"></ColumnReference>
</Identifier>
</ScalarOperator>
</RangeExpressions>
</EndRange>
</SeekKeys>
</SeekPredicateNew>
</SeekPredicates>
<Predicate>
<ScalarOperator ScalarString="****.[dbo].[Locations].[MaxLat]>=(32.750) AND ****.[dbo].[Locations].[MaxLng]>=(-118.951) AND ****.[dbo].[Locations].[MinLng]<=(-117.646)">
<Logical Operation="AND">
<ScalarOperator>
<Compare CompareOp="GE">
<ScalarOperator>
<Identifier>
<ColumnReference Database="****" Schema="[dbo]" Table="[Locations]" Column="MaxLat"></ColumnReference>
</Identifier>
</ScalarOperator>
<ScalarOperator>
<Const ConstValue="(32.750)"></Const>
</ScalarOperator>
</Compare>
</ScalarOperator>
<ScalarOperator>
<Compare CompareOp="GE">
<ScalarOperator>
<Identifier>
<ColumnReference Database="****" Schema="[dbo]" Table="[Locations]" Column="MaxLng"></ColumnReference>
</Identifier>
</ScalarOperator>
<ScalarOperator>
<Const ConstValue="(-118.951)"></Const>
</ScalarOperator>
</Compare>
</ScalarOperator>
<ScalarOperator>
<Compare CompareOp="LE">
<ScalarOperator>
<Identifier>
<ColumnReference Database="****" Schema="[dbo]" Table="[Locations]" Column="MinLng"></ColumnReference>
</Identifier>
</ScalarOperator>
<ScalarOperator>
<Const ConstValue="(-117.646)"></Const>
</ScalarOperator>
</Compare>
</ScalarOperator>
</Logical>
</ScalarOperator>
</Predicate>
</IndexScan>
</RelOp>
</NestedLoops>
</RelOp>
</QueryPlan>
</StmtSimple>
<StmtSimple StatementText="

SELECT ID
FROM Locations
WHERE LocationType = 5
AND CountryCode = 'US'
AND MaxLat >= 32.750
AND MinLat <= 34.823
AND MaxLng >= -118.951
AND MinLng <= -117.646" StatementId="2" StatementCompId="2" StatementType="SELECT" RetrievedFromCache="false" StatementSubTreeCost="0.0226731" StatementEstRows="88.5595" SecurityPolicyApplied="false" StatementOptmLevel="TRIVIAL" QueryHash="0xA63252C178F68E1" QueryPlanHash="0x48CBE2D413E8362B" CardinalityEstimationModelVersion="70" ParameterizedText="(@1 tinyint,@2 varchar(8000),@3 numeric(5,3),@4 numeric(5,3),@5 numeric(6,3),@6 numeric(6,3))SELECT [ID] FROM [Locations] WHERE [LocationType]=@1 AND [CountryCode]=@2 AND [MaxLat]>=@3 AND [MinLat]<=@4 AND [MaxLng]>=@5 AND [MinLng]<=@6">
<StatementSetOptions QUOTED_IDENTIFIER="true" ARITHABORT="true" CONCAT_NULL_YIELDS_NULL="true" ANSI_NULLS="true" ANSI_PADDING="true" ANSI_WARNINGS="true" NUMERIC_ROUNDABORT="false"></StatementSetOptions>
<QueryPlan CachedPlanSize="32" CompileTime="2" CompileCPU="2" CompileMemory="336">
<MemoryGrantInfo SerialRequiredMemory="0" SerialDesiredMemory="0"></MemoryGrantInfo>
<OptimizerHardwareDependentProperties EstimatedAvailableMemoryGrant="2949120" EstimatedPagesCached="5898240" EstimatedAvailableDegreeOfParallelism="8" MaxCompileMemory="635178856"></OptimizerHardwareDependentProperties>
<RelOp NodeId="0" PhysicalOp="Nested Loops" LogicalOp="Inner Join" EstimateRows="88.5595" EstimatedRowsRead="1466.67" EstimateIO="0.0209028" EstimateCPU="0.00177034" AvgRowSize="50" EstimatedTotalSubtreeCost="0.0226731" TableCardinality="86546" Parallel="0" EstimateRebinds="0" EstimateRewinds="0" EstimatedExecutionMode="Row">
<OutputList>
<ColumnReference Database="****" Schema="[dbo]" Table="[Locations]" Column="ID"></ColumnReference>
</OutputList>
<NestedLoops Optimized="0">
<OuterReferences>
<ColumnReference Column="Expr1005"></ColumnReference>
<ColumnReference Column="Expr1006"></ColumnReference>
<ColumnReference Column="Expr1004"></ColumnReference>
</OuterReferences>
<RelOp NodeId="1" PhysicalOp="Compute Scalar" LogicalOp="Compute Scalar" EstimateRows="1" EstimateIO="0" EstimateCPU="0" AvgRowSize="37" EstimatedTotalSubtreeCost="0" Parallel="0" EstimateRebinds="0" EstimateRewinds="0" EstimatedExecutionMode="Row">
<OutputList>
<ColumnReference Column="Expr1005"></ColumnReference>
<ColumnReference Column="Expr1006"></ColumnReference>
<ColumnReference Column="Expr1004"></ColumnReference>
</OutputList>
<ComputeScalar>
<DefinedValues>
<DefinedValue>
<ValueVector>
<ColumnReference Column="Expr1005"></ColumnReference>
<ColumnReference Column="Expr1006"></ColumnReference>
<ColumnReference Column="Expr1004"></ColumnReference>
</ValueVector>
<ScalarOperator ScalarString="GetRangeWithMismatchedTypes(NULL,[@4],(42))">
<Intrinsic FunctionName="GetRangeWithMismatchedTypes">
<ScalarOperator>
<Const ConstValue="NULL"></Const>
</ScalarOperator>
<ScalarOperator>
<Identifier>
<ColumnReference Column="@4"></ColumnReference>
</Identifier>
</ScalarOperator>
<ScalarOperator>
<Const ConstValue="(42)"></Const>
</ScalarOperator>
</Intrinsic>
</ScalarOperator>
</DefinedValue>
</DefinedValues>
<RelOp NodeId="2" PhysicalOp="Constant Scan" LogicalOp="Constant Scan" EstimateRows="1" EstimateIO="0" EstimateCPU="0" AvgRowSize="0" EstimatedTotalSubtreeCost="0" Parallel="0" EstimateRebinds="0" EstimateRewinds="0" EstimatedExecutionMode="Row">
<OutputList></OutputList>
<ConstantScan></ConstantScan>
</RelOp>
</ComputeScalar>
</RelOp>
<RelOp NodeId="3" PhysicalOp="Index Seek" LogicalOp="Index Seek" EstimateRows="88.5595" EstimatedRowsRead="1466.67" EstimateIO="0.0209028" EstimateCPU="0.00177034" AvgRowSize="50" EstimatedTotalSubtreeCost="0.0226731" TableCardinality="86546" Parallel="0" EstimateRebinds="0" EstimateRewinds="0" EstimatedExecutionMode="Row">
<OutputList>
<ColumnReference Database="****" Schema="[dbo]" Table="[Locations]" Column="ID"></ColumnReference>
</OutputList>
<IndexScan Ordered="1" ScanDirection="FORWARD" ForcedIndex="0" ForceSeek="0" ForceScan="0" NoExpandHint="0" Storage="RowStore">
<DefinedValues>
<DefinedValue>
<ColumnReference Database="****" Schema="[dbo]" Table="[Locations]" Column="ID"></ColumnReference>
</DefinedValue>
</DefinedValues>
<Object Database="****" Schema="[dbo]" Table="[Locations]" Index="[IX_MinMax]" IndexKind="NonClustered" Storage="RowStore"></Object>
<SeekPredicates>
<SeekPredicateNew>
<SeekKeys>
<Prefix ScanType="EQ">
<RangeColumns>
<ColumnReference Database="****" Schema="[dbo]" Table="[Locations]" Column="LocationType"></ColumnReference>
<ColumnReference Database="****" Schema="[dbo]" Table="[Locations]" Column="CountryCode"></ColumnReference>
</RangeColumns>
<RangeExpressions>
<ScalarOperator ScalarString="CONVERT_IMPLICIT(int,[@1],0)">
<Identifier>
<ColumnReference Column="ConstExpr1002">
<ScalarOperator>
<Convert DataType="int" Style="0" Implicit="1">
<ScalarOperator>
<Identifier>
<ColumnReference Column="@1"></ColumnReference>
</Identifier>
</ScalarOperator>
</Convert>
</ScalarOperator>
</ColumnReference>
</Identifier>
</ScalarOperator>
<ScalarOperator ScalarString="CONVERT_IMPLICIT(nvarchar(4000),[@2],0)">
<Identifier>
<ColumnReference Column="ConstExpr1003">
<ScalarOperator>
<Convert DataType="nvarchar" Length="8000" Style="0" Implicit="1">
<ScalarOperator>
<Identifier>
<ColumnReference Column="@2"></ColumnReference>
</Identifier>
</ScalarOperator>
</Convert>
</ScalarOperator>
</ColumnReference>
</Identifier>
</ScalarOperator>
</RangeExpressions>
</Prefix>
<StartRange ScanType="GT">
<RangeColumns>
<ColumnReference Database="****" Schema="[dbo]" Table="[Locations]" Column="MinLat"></ColumnReference>
</RangeColumns>
<RangeExpressions>
<ScalarOperator ScalarString="[Expr1005]">
<Identifier>
<ColumnReference Column="Expr1005"></ColumnReference>
</Identifier>
</ScalarOperator>
</RangeExpressions>
</StartRange>
<EndRange ScanType="LT">
<RangeColumns>
<ColumnReference Database="****" Schema="[dbo]" Table="[Locations]" Column="MinLat"></ColumnReference>
</RangeColumns>
<RangeExpressions>
<ScalarOperator ScalarString="[Expr1006]">
<Identifier>
<ColumnReference Column="Expr1006"></ColumnReference>
</Identifier>
</ScalarOperator>
</RangeExpressions>
</EndRange>
</SeekKeys>
</SeekPredicateNew>
</SeekPredicates>
<Predicate>
<ScalarOperator ScalarString="****.[dbo].[Locations].[MaxLat]>=[@3] AND ****.[dbo].[Locations].[MaxLng]>=[@5] AND ****.[dbo].[Locations].[MinLng]<=[@6]">
<Logical Operation="AND">
<ScalarOperator>
<Compare CompareOp="GE">
<ScalarOperator>
<Identifier>
<ColumnReference Database="****" Schema="[dbo]" Table="[Locations]" Column="MaxLat"></ColumnReference>
</Identifier>
</ScalarOperator>
<ScalarOperator>
<Identifier>
<ColumnReference Column="@3"></ColumnReference>
</Identifier>
</ScalarOperator>
</Compare>
</ScalarOperator>
<ScalarOperator>
<Compare CompareOp="GE">
<ScalarOperator>
<Identifier>
<ColumnReference Database="****" Schema="[dbo]" Table="[Locations]" Column="MaxLng"></ColumnReference>
</Identifier>
</ScalarOperator>
<ScalarOperator>
<Identifier>
<ColumnReference Column="@5"></ColumnReference>
</Identifier>
</ScalarOperator>
</Compare>
</ScalarOperator>
<ScalarOperator>
<Compare CompareOp="LE">
<ScalarOperator>
<Identifier>
<ColumnReference Database="****" Schema="[dbo]" Table="[Locations]" Column="MinLng"></ColumnReference>
</Identifier>
</ScalarOperator>
<ScalarOperator>
<Identifier>
<ColumnReference Column="@6"></ColumnReference>
</Identifier>
</ScalarOperator>
</Compare>
</ScalarOperator>
</Logical>
</ScalarOperator>
</Predicate>
</IndexScan>
</RelOp>
</NestedLoops>
</RelOp>
<ParameterList>
<ColumnReference Column="@6" ParameterDataType="numeric(6,3)" ParameterCompiledValue="(-117.646)"></ColumnReference>
<ColumnReference Column="@5" ParameterDataType="numeric(6,3)" ParameterCompiledValue="(-118.951)"></ColumnReference>
<ColumnReference Column="@4" ParameterDataType="numeric(5,3)" ParameterCompiledValue="(34.823)"></ColumnReference>
<ColumnReference Column="@3" ParameterDataType="numeric(5,3)" ParameterCompiledValue="(32.750)"></ColumnReference>
<ColumnReference Column="@2" ParameterDataType="varchar(8000)" ParameterCompiledValue="'US'"></ColumnReference>
<ColumnReference Column="@1" ParameterDataType="tinyint" ParameterCompiledValue="(5)"></ColumnReference>
</ParameterList>
</QueryPlan>
</StmtSimple>
</Statements>
</Batch>
</BatchSequence>
</ShowPlanXML>
但是,根据要比较的结果邮政编码的数量,查询仍然需要太长时间才能在生产环境中使用。
我希望可能有某种方法来计算内部边界框&#34; (因为没有更好的词)类似于STEnvelope如何计算&#34;外边界框&#34;
这个想法是什么,最大的矩形适合所提供的形状?这将允许快速查询完全适合其内部的邮政编码边界框,留下(希望)更小的邮政编码集,这将需要更精确的STIntersection。
编辑:已添加5/25/2018
在下面与Rick讨论之后,我将包括两个查询的执行计划详细信息,一个使用AND NOT(&gt; OR模式,另一个使用AND反转)(&lt; = AND。
using( MyDBContext db = new MyDBContext()){
var activeEmployees = (from em in db.Employees
join s in db.Store on em.StoreId == s.Id
where em.IsActive == true
select em).ToList();
}
以下是执行计划的XML视图:
{{1}}
答案 0 :(得分:0)
(这些评论适用于MySQL;它们可能适用于或不适用于其他供应商。答案是在知道MSSql是目标之前编写的。)
使用De Morgan定律避免NOT
和OR
:
AND z.MaxLat >= d.MinLat
AND z.MinLat <= d.MaxLat
AND z.MaxLng >= d.MinLng
AND z.MinLng <= d.MaxLng
在确定所需的拉链和/或dmas之前,请勿执行整个SELECT
。这个可能允许子查询在索引中有效运行。
SELECT ...
FROM ( SELECT z.id
FROM zips z JOIN dmas d
WHERE d.ID = @DMAID
AND z.Type = 5
AND z.MaxLat >= d.MinLat ...
) x
JOIN zips z2 ON x.id = z2.id
此指数位于zips
:
INDEX(Type, -- first
MinLat, MaxLat, MinLng, MaxLng, -- any order
id) -- last
On OR,NOT
BTree索引......
a > 5
,a BETWEEN 5 AND 10
,`c LIKE'foo%'等。NOT
通常涉及两个范围,通常涵盖大部分索引:“X之前的所有内容和X之后的所有内容”。OR
(涉及一个专栏)涉及跳过桌子。当涉及多个表(或多个列)时,情况会更复杂。
a = 5 AND ...
可能会受益于复合索引以a
开始,因为a
已使用{{进行了测试1}}。我推荐的6列索引是'复合'和'覆盖':
=
开始,使用'='进行测试,以便更多列可以发挥作用。Type
),因此其余列不会用于过滤或排序。MinLat
)列(“派生”表)都包含在索引中。这使得操作仅在索引的BTree中发生,而不触及“数据”BTree。