Я пытаюсь создавать отчеты, которые связаны с перекрытием географических границ.
В качестве упрощенного примера, почтовые индексы внутри DMA (назначенная маркетинговая зона).
У меня есть данные географии SQL, описывающие форму многоугольника прямого доступа к памяти, и данные географии SQL, описывающие формы многоугольника почтовых индексов США.
Я бы хотел узнать:
- Какие почтовые индексы полностью содержатся в DMA
- Какие почтовые индексы частично перекрывают DMA.
- Какой процент площади перекрывающегося почтового индекса находится внутри DMA
Я могу получить эту информацию, используя методы STWithin и STIntersection, сравнивая области. Однако я сталкиваюсь с некоторыми узкими местами с высокой производительностью.
Первое, что я сделал, это вычислил и записал внешние ограничительные рамки DMA и форм почтового индекса, сохранил их в таблице и проиндексировал на этих данных. Это позволило мне исключить все почтовые индексы, которые не могут перекрываться с DMA.
Пример: * * один тысяча двадцать-одна
SELECT ...
FROM Locations d
INNER JOIN Locations z ON z.Type = 5 -- Zip Code
WHERE d.ID = @DMAID
AND NOT (
z.MaxLat < d.MinLat
OR z.MinLat > d.MaxLat
OR z.MaxLng < d.MinLng
OR z.MinLng > d.MaxLng
)
Однако, в зависимости от количества получаемых почтовых индексов для сравнения, запрос все равно может занять слишком много времени, чтобы его можно было использовать в производственной среде.
Я надеюсь, что может быть какой-то способ вычислить "внутреннюю ограничивающую рамку" (из-за отсутствия лучшего слова), подобную тому, как STEnvelope вычисляет "внешнюю ограничивающую рамку"
Идея в том, какой самый большой прямоугольник поместится в предоставленной форме? Это позволило бы быстро запросить ограничивающие прямоугольники почтового индекса, которые полностью помещаются внутри него, оставив (надеюсь) гораздо меньший набор почтовых индексов, который потребовал бы более точного STIntersection.
Редактировать: Добавлено 5/25/2018
После обсуждения с Риком, приведенного ниже, я включил детали плана выполнения для двух запросов, один с использованием шаблона AND NOT (> OR, а другой - с помощью AND (<= AND. </p>).
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
)
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
Вот XML-представление планов выполнения:
<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>