У меня есть следующая схема базы данных.
Профиль [Таблица]
pKey (auto)
pgKey ('Group' foreign key to separate tenants)
pProfile (the string data to return)
HistoryData [Таблица]
hisKey (auto)
hispKey (foreign key to Profile)
hisgKey (foreign key to 'Group')
hisData (the string data to return)
Количество профилей для одного арендатора составляет 24 874
SELECT COUNT(p.pKey) FROM Profile p WHERE pgKey = 318
Количество строк HistoryData, которые меня интересуют (SeveranceFinal), составляет 16 591
( 10 профилей имеют 2 строки, остальные строки - это отношение 1 к 1 )
SELECT COUNT(h.hisKey) FROM HistoryData h WHERE h.hisgKey = 318 AND h.hisType = 'SeveranceFinal'
Если я напишу запрос, подобный этому:
SELECT COUNT(p.pKey)
FROM Profile p INNER JOIN HistoryData h ON p.pKey = h.hispKey
WHERE p.pgKey = 318 AND h.hisType = 'SeveranceFinal'
Значение счетчика равно снова 16,591.
Если я напишу этот запрос на внешнее объединение:
SELECT COUNT(p.pKey)
FROM Profile p LEFT OUTER JOIN (
SELECT * FROM HistoryData WHERE hisType = 'SeveranceFinal'
) h ON p.pKey = h.hispKey
WHERE p.pgKey = 318
Ожидаемое число будет 24,884 ( 10 больше, чем количество записей профиля из-за 10 человек с двумя строками данных в HistoryData ).
Проблема в том, что мне нужно вернуть всех профилей вместе с любыми соответствующими строками SeveranceFinal, , но только последний SeveranceFinal строка для всех, у кого есть. Количество должно соответствовать исходному количеству профилей, так как у меня всегда было бы 0 или 1 рядов SeveranceFinal, соединенных со строками профиля. Я пробовал этот запрос, но он никогда не завершается, он всегда истекает. Мне кажется, что я что-то не так делаю ( помните, что база данных мультитенантна и в таблице профиля есть миллионы строк, но с разными pgKey
значениями )
SELECT COUNT(p.pKey)
FROM Profile p LEFT OUTER JOIN
(
SELECT hispKey, hisData FROM (
SELECT hispKey, hisData, ROW_NUMBER() OVER (PARTITION BY hispKey ORDER BY hisIndex DESC) RowID
FROM HistoryData WHERE hisgKey = 318 AND hisType = 'SeveranceFinal'
) t WHERE RowID = 1
) h ON p.pKey = h.hispKey
WHERE p.pgKey = 318
Что не так с запросом?
Примечания:
1. Если я просто выполню часть h
этого запроса SELECT hispKey, hisData FROM ( ... ) t WHERE RowID = 1
, которая будет возвращена «немедленно».
2. Если я изменю LEFT OUTER JOIN на INNER JOIN и INNER JOIN, он немедленно завершится и вернёт правильное значение 16 581 ( 10 меньше, чем количество строк SeveranceFinal из-за 10 профилей, которые имеют 2 строки )
Вот предлагаемый индекс из плана выполнения:
/*
USE [RBL_Profile]
GO
CREATE NONCLUSTERED INDEX [<Name of Missing Index, sysname,>]
ON [dbo].[HistoryData] ([hisType],[hisgKey])
INCLUDE ([hispKey],[hisIndex])
GO
*/
Вот отображаемый план выполнения:
![enter image description here](https://i.stack.imgur.com/bSBVu.png)
Но вот уже мои индексы в таблицах:
** Таблица профиля **
IX_Profile - nonclustered, unique located on PRIMARY: pAuthID, pgKey
IX_Profile_Group - nonclustered located on PRIMARY: pgKey
nc_Profile_GetProfiles - nonclustered located on PRIMARY: pgKey, pAuthID
** Таблица истории **
IX_HistoryData - nonclustered, unique located on PRIMARY: hisType, hispKey, hisIndex
IX_HistoryData_Profile - nonclustered located on PRIMARY: hispKey
PK_HistoryData - nonclustered, unique, primary key located on PRIMARY: hisKey
Следует Я следую совету SQL?
Обновление : я применил предложенный индекс без у пользы кажется. Запрос все еще истекает (или я убил его через 30 секунд)
Вот новый план:
![enter image description here](https://i.stack.imgur.com/6HtxC.png)
Вот план в Xml:
<?xml version="1.0" encoding="utf-16"?>
<ShowPlanXML xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema" Version="1.1" Build="10.50.4000.0" xmlns="http://schemas.microsoft.com/sqlserver/2004/07/showplan">
<BatchSequence>
<Batch>
<Statements>
<StmtSimple StatementCompId="1" StatementEstRows="1" StatementId="1" StatementOptmLevel="FULL" StatementOptmEarlyAbortReason="GoodEnoughPlanFound" StatementSubTreeCost="0.0179336" StatementText="SELECT COUNT(p.pKey) 
FROM Profile p LEFT OUTER JOIN 
 ( 
 SELECT hispKey, hisData FROM (
 SELECT hispKey, hisData, ROW_NUMBER() OVER (PARTITION BY hispKey ORDER BY hisIndex DESC) RowID 
 FROM HistoryData WHERE hisgKey = 318 AND hisType = 'SeveranceFinal' 
 ) t WHERE RowID = 1
 ) h ON p.pKey = h.hispKey
WHERE p.pgKey = 318
" StatementType="SELECT" QueryHash="0xFF065B887470862B" QueryPlanHash="0xC85BCB9BD79F6735">
<StatementSetOptions ANSI_NULLS="true" ANSI_PADDING="true" ANSI_WARNINGS="true" ARITHABORT="true" CONCAT_NULL_YIELDS_NULL="true" NUMERIC_ROUNDABORT="false" QUOTED_IDENTIFIER="true" />
<QueryPlan CachedPlanSize="32" CompileTime="5" CompileCPU="5" CompileMemory="536">
<RelOp AvgRowSize="11" EstimateCPU="1.1E-06" EstimateIO="0" EstimateRebinds="0" EstimateRewinds="0" EstimateRows="1" LogicalOp="Compute Scalar" NodeId="0" Parallel="false" PhysicalOp="Compute Scalar" EstimatedTotalSubtreeCost="0.0179336">
<OutputList>
<ColumnReference Column="Expr1009" />
</OutputList>
<ComputeScalar>
<DefinedValues>
<DefinedValue>
<ColumnReference Column="Expr1009" />
<ScalarOperator ScalarString="CONVERT_IMPLICIT(int,[Expr1013],0)">
<Convert DataType="int" Style="0" Implicit="true">
<ScalarOperator>
<Identifier>
<ColumnReference Column="Expr1013" />
</Identifier>
</ScalarOperator>
</Convert>
</ScalarOperator>
</DefinedValue>
</DefinedValues>
<RelOp AvgRowSize="11" EstimateCPU="1.1E-06" EstimateIO="0" EstimateRebinds="0" EstimateRewinds="0" EstimateRows="1" LogicalOp="Aggregate" NodeId="1" Parallel="false" PhysicalOp="Stream Aggregate" EstimatedTotalSubtreeCost="0.0179336">
<OutputList>
<ColumnReference Column="Expr1013" />
</OutputList>
<StreamAggregate>
<DefinedValues>
<DefinedValue>
<ColumnReference Column="Expr1013" />
<ScalarOperator ScalarString="Count(*)">
<Aggregate AggType="countstar" Distinct="false" />
</ScalarOperator>
</DefinedValue>
</DefinedValues>
<RelOp AvgRowSize="9" EstimateCPU="4.18E-06" EstimateIO="0" EstimateRebinds="0" EstimateRewinds="0" EstimateRows="1" LogicalOp="Left Outer Join" NodeId="2" Parallel="false" PhysicalOp="Nested Loops" EstimatedTotalSubtreeCost="0.0179325">
<OutputList />
<NestedLoops Optimized="false">
<Predicate>
<ScalarOperator ScalarString="[RBL_Profile].[dbo].[Profile].[pKey] as [p].[pKey]=[RBL_Profile].[dbo].[HistoryData].[hispKey]">
<Compare CompareOp="EQ">
<ScalarOperator>
<Identifier>
<ColumnReference Database="[RBL_Profile]" Schema="[dbo]" Table="[Profile]" Alias="[p]" Column="pKey" />
</Identifier>
</ScalarOperator>
<ScalarOperator>
<Identifier>
<ColumnReference Database="[RBL_Profile]" Schema="[dbo]" Table="[HistoryData]" Column="hispKey" />
</Identifier>
</ScalarOperator>
</Compare>
</ScalarOperator>
</Predicate>
<RelOp AvgRowSize="11" EstimateCPU="0.0001581" EstimateIO="0.003125" EstimateRebinds="0" EstimateRewinds="0" EstimateRows="1" LogicalOp="Index Seek" NodeId="3" Parallel="false" PhysicalOp="Index Seek" EstimatedTotalSubtreeCost="0.0032831" TableCardinality="1371980">
<OutputList>
<ColumnReference Database="[RBL_Profile]" Schema="[dbo]" Table="[Profile]" Alias="[p]" Column="pKey" />
</OutputList>
<IndexScan Ordered="true" ScanDirection="FORWARD" ForcedIndex="false" ForceSeek="false" ForceScan="false" NoExpandHint="false">
<DefinedValues>
<DefinedValue>
<ColumnReference Database="[RBL_Profile]" Schema="[dbo]" Table="[Profile]" Alias="[p]" Column="pKey" />
</DefinedValue>
</DefinedValues>
<Object Database="[RBL_Profile]" Schema="[dbo]" Table="[Profile]" Index="[nc_Profile_GetProfiles]" Alias="[p]" IndexKind="NonClustered" />
<SeekPredicates>
<SeekPredicateNew>
<SeekKeys>
<Prefix ScanType="EQ">
<RangeColumns>
<ColumnReference Database="[RBL_Profile]" Schema="[dbo]" Table="[Profile]" Alias="[p]" Column="pgKey" />
</RangeColumns>
<RangeExpressions>
<ScalarOperator ScalarString="(318)">
<Const ConstValue="(318)" />
</ScalarOperator>
</RangeExpressions>
</Prefix>
</SeekKeys>
</SeekPredicateNew>
</SeekPredicates>
</IndexScan>
</RelOp>
<RelOp AvgRowSize="11" EstimateCPU="4.8E-07" EstimateIO="0" EstimateRebinds="0" EstimateRewinds="0" EstimateRows="1" LogicalOp="Filter" NodeId="4" Parallel="false" PhysicalOp="Filter" EstimatedTotalSubtreeCost="0.014645">
<OutputList>
<ColumnReference Database="[RBL_Profile]" Schema="[dbo]" Table="[HistoryData]" Column="hispKey" />
</OutputList>
<Filter StartupExpression="false">
<RelOp AvgRowSize="19" EstimateCPU="8E-08" EstimateIO="0" EstimateRebinds="0" EstimateRewinds="0" EstimateRows="1" LogicalOp="Compute Scalar" NodeId="5" Parallel="false" PhysicalOp="Sequence Project" EstimatedTotalSubtreeCost="0.0146445">
<OutputList>
<ColumnReference Database="[RBL_Profile]" Schema="[dbo]" Table="[HistoryData]" Column="hispKey" />
<ColumnReference Column="Expr1007" />
</OutputList>
<SequenceProject>
<DefinedValues>
<DefinedValue>
<ColumnReference Column="Expr1007" />
<ScalarOperator ScalarString="row_number">
<Sequence FunctionName="row_number" />
</ScalarOperator>
</DefinedValue>
</DefinedValues>
<RelOp AvgRowSize="19" EstimateCPU="2E-08" EstimateIO="0" EstimateRebinds="0" EstimateRewinds="0" EstimateRows="1" LogicalOp="Segment" NodeId="6" Parallel="false" PhysicalOp="Segment" EstimatedTotalSubtreeCost="0.0146444">
<OutputList>
<ColumnReference Database="[RBL_Profile]" Schema="[dbo]" Table="[HistoryData]" Column="hispKey" />
<ColumnReference Database="[RBL_Profile]" Schema="[dbo]" Table="[HistoryData]" Column="hisIndex" />
<ColumnReference Column="Segment1012" />
</OutputList>
<Segment>
<GroupBy>
<ColumnReference Database="[RBL_Profile]" Schema="[dbo]" Table="[HistoryData]" Column="hispKey" />
</GroupBy>
<SegmentColumn>
<ColumnReference Column="Segment1012" />
</SegmentColumn>
<RelOp AvgRowSize="22" EstimateCPU="0.000100022" EstimateIO="0.0112613" EstimateRebinds="0" EstimateRewinds="0" EstimateRows="1" LogicalOp="Sort" NodeId="7" Parallel="false" PhysicalOp="Sort" EstimatedTotalSubtreeCost="0.0146444">
<OutputList>
<ColumnReference Database="[RBL_Profile]" Schema="[dbo]" Table="[HistoryData]" Column="hispKey" />
<ColumnReference Database="[RBL_Profile]" Schema="[dbo]" Table="[HistoryData]" Column="hisIndex" />
</OutputList>
<MemoryFractions Input="1" Output="1" />
<Sort Distinct="false">
<OrderBy>
<OrderByColumn Ascending="true">
<ColumnReference Database="[RBL_Profile]" Schema="[dbo]" Table="[HistoryData]" Column="hispKey" />
</OrderByColumn>
<OrderByColumn Ascending="false">
<ColumnReference Database="[RBL_Profile]" Schema="[dbo]" Table="[HistoryData]" Column="hisIndex" />
</OrderByColumn>
</OrderBy>
<RelOp AvgRowSize="22" EstimateCPU="0.0001581" EstimateIO="0.003125" EstimateRebinds="0" EstimateRewinds="0" EstimateRows="1" LogicalOp="Index Seek" NodeId="8" Parallel="false" PhysicalOp="Index Seek" EstimatedTotalSubtreeCost="0.0032831" TableCardinality="29897300">
<OutputList>
<ColumnReference Database="[RBL_Profile]" Schema="[dbo]" Table="[HistoryData]" Column="hispKey" />
<ColumnReference Database="[RBL_Profile]" Schema="[dbo]" Table="[HistoryData]" Column="hisIndex" />
</OutputList>
<IndexScan Ordered="true" ScanDirection="FORWARD" ForcedIndex="false" ForceSeek="false" ForceScan="false" NoExpandHint="false">
<DefinedValues>
<DefinedValue>
<ColumnReference Database="[RBL_Profile]" Schema="[dbo]" Table="[HistoryData]" Column="hispKey" />
</DefinedValue>
<DefinedValue>
<ColumnReference Database="[RBL_Profile]" Schema="[dbo]" Table="[HistoryData]" Column="hisIndex" />
</DefinedValue>
</DefinedValues>
<Object Database="[RBL_Profile]" Schema="[dbo]" Table="[HistoryData]" Index="[IX_HistoryData_Group]" IndexKind="NonClustered" />
<SeekPredicates>
<SeekPredicateNew>
<SeekKeys>
<Prefix ScanType="EQ">
<RangeColumns>
<ColumnReference Database="[RBL_Profile]" Schema="[dbo]" Table="[HistoryData]" Column="hisType" />
<ColumnReference Database="[RBL_Profile]" Schema="[dbo]" Table="[HistoryData]" Column="hisgKey" />
</RangeColumns>
<RangeExpressions>
<ScalarOperator ScalarString="'SeveranceFinal'">
<Const ConstValue="'SeveranceFinal'" />
</ScalarOperator>
<ScalarOperator ScalarString="(318)">
<Const ConstValue="(318)" />
</ScalarOperator>
</RangeExpressions>
</Prefix>
</SeekKeys>
</SeekPredicateNew>
</SeekPredicates>
</IndexScan>
</RelOp>
</Sort>
</RelOp>
</Segment>
</RelOp>
</SequenceProject>
</RelOp>
<Predicate>
<ScalarOperator ScalarString="[Expr1007]=(1)">
<Compare CompareOp="EQ">
<ScalarOperator>
<Identifier>
<ColumnReference Column="Expr1007" />
</Identifier>
</ScalarOperator>
<ScalarOperator>
<Const ConstValue="(1)" />
</ScalarOperator>
</Compare>
</ScalarOperator>
</Predicate>
</Filter>
</RelOp>
</NestedLoops>
</RelOp>
</StreamAggregate>
</RelOp>
</ComputeScalar>
</RelOp>
</QueryPlan>
</StmtSimple>
</Statements>
</Batch>
</BatchSequence>
</ShowPlanXML>