Voordat ik inga op hoe ik het werkelijke uitvoeringsplan in code kan krijgen en degene vind die aangeven dat ze indexen nodig hebben, raad ik je aan om te kijken naar het gebruik van de Adviseur Database Engine Tuning (DTA) , u kunt het een lijst van alle zoekopdrachten geven en het zal ze verwerken om u mogelijke indexen, statistieken en vele andere dingen te vertellen die kunnen helpen bij het plannen van uw zoekopdrachten.
Nog beter dan het een lijst met meer dan 1 miljoen zoekopdrachten te geven, is dat u een tracering van de server kunt krijgen met de daadwerkelijke zoekopdrachten die worden uitgevoerd en dat deze zich zal concentreren op de zoekopdrachten die de meeste tijd in beslag nemen.
Om uw oorspronkelijke vraag te beantwoorden, moet u SET STATISTICS XML ON
. toevoegen aan het begin van de verbinding geeft dit u de XML-gegevens waarop de GUI die u liet zien, is gebaseerd. (Kijk hier voor meer informatie over het verkrijgen van de plannen
). Zodra u dat doet, komen uw zoekopdrachten terug met een extra resultatenset met de xml voor het plan in de eerste rij van de eerste kolom.
Hier is een snelle en vuile functie die dat doet.
private static string GetXmlPlanForQuery(string queryText)
{
string result = null;
using (var connection = new SqlConnection(connectionString))
using (var command = new SqlCommand())
{
connection.Open();
command.Connection = connection;
//Enable the statistics.
command.CommandText = "SET STATISTICS XML ON";
command.ExecuteNonQuery();
//Run through the query, keeping the first row first column of the last result set.
command.CommandText = queryText;
using (var reader = command.ExecuteReader())
{
object lastValue = null;
do
{
if (reader.Read())
{
lastValue = reader.GetValue(0);
}
} while (reader.NextResult());
if (lastValue != null)
{
result = lastValue as string;
}
}
}
return result;
}
En hier is de XML die is geretourneerd voor de query select TOTAL_SALES from clients where ACTIVE = 0;
die ik heb uitgevoerd die ik had in een van mijn lokale databases.
<?xml version="1.0"?>
<ShowPlanXML xmlns="http://schemas.microsoft.com/sqlserver/2004/07/showplan" Version="1.2" Build="11.0.5058.0">
<BatchSequence>
<Batch>
<Statements>
<StmtSimple StatementText="SELECT [TOTAL_SALES] FROM [clients] WHERE [ACTIVE][email protected]" StatementId="1" StatementCompId="1" StatementType="SELECT" RetrievedFromCache="false" StatementSubTreeCost="0.0767454" StatementEstRows="315" StatementOptmLevel="FULL" QueryHash="0x708AE72DD31A316" QueryPlanHash="0x214EA79FF76E6771" StatementOptmEarlyAbortReason="GoodEnoughPlanFound">
<StatementSetOptions QUOTED_IDENTIFIER="true" ARITHABORT="false" CONCAT_NULL_YIELDS_NULL="true" ANSI_NULLS="true" ANSI_PADDING="true" ANSI_WARNINGS="true" NUMERIC_ROUNDABORT="false"/>
<QueryPlan DegreeOfParallelism="1" CachedPlanSize="16" CompileTime="1" CompileCPU="1" CompileMemory="192">
<MissingIndexes>
<MissingIndexGroup Impact="94.0522">
<MissingIndex Database="[exampleDb]" Schema="[dbo]" Table="[CLIENTS]">
<ColumnGroup Usage="EQUALITY">
<Column Name="[ACTIVE]" ColumnId="15"/>
</ColumnGroup>
<ColumnGroup Usage="INCLUDE">
<Column Name="[TOTAL_SALES]" ColumnId="18"/>
</ColumnGroup>
</MissingIndex>
</MissingIndexGroup>
</MissingIndexes>
<MemoryGrantInfo SerialRequiredMemory="0" SerialDesiredMemory="0"/>
<OptimizerHardwareDependentProperties EstimatedAvailableMemoryGrant="830838" EstimatedPagesCached="207709" EstimatedAvailableDegreeOfParallelism="2"/>
<RelOp NodeId="0" PhysicalOp="Clustered Index Scan" LogicalOp="Clustered Index Scan" EstimateRows="315" EstimateIO="0.0749769" EstimateCPU="0.0017685" AvgRowSize="16" EstimatedTotalSubtreeCost="0.0767454" TableCardinality="1465" Parallel="0" EstimateRebinds="0" EstimateRewinds="0" EstimatedExecutionMode="Row">
<OutputList>
<ColumnReference Database="[exampleDb]" Schema="[dbo]" Table="[CLIENTS]" Column="TOTAL_SALES"/>
</OutputList>
<RunTimeInformation>
<RunTimeCountersPerThread Thread="0" ActualRows="315" ActualEndOfScans="1" ActualExecutions="1"/>
</RunTimeInformation>
<IndexScan Ordered="0" ForcedIndex="0" ForceScan="0" NoExpandHint="0">
<DefinedValues>
<DefinedValue>
<ColumnReference Database="[exampleDb]" Schema="[dbo]" Table="[CLIENTS]" Column="TOTAL_SALES"/>
</DefinedValue>
</DefinedValues>
<Object Database="[exampleDb]" Schema="[dbo]" Table="[CLIENTS]" Index="[imp_clpk_CLIENTS]" IndexKind="Clustered"/>
<Predicate>
<ScalarOperator ScalarString="[exampleDb].[dbo].[CLIENTS].[ACTIVE]=(0)">
<Compare CompareOp="EQ">
<ScalarOperator>
<Identifier>
<ColumnReference Database="[exampleDb]" Schema="[dbo]" Table="[CLIENTS]" Column="ACTIVE"/>
</Identifier>
</ScalarOperator>
<ScalarOperator>
<Const ConstValue="(0)"/>
</ScalarOperator>
</Compare>
</ScalarOperator>
</Predicate>
</IndexScan>
</RelOp>
<ParameterList>
<ColumnReference Column="@1" ParameterCompiledValue="(0)" ParameterRuntimeValue="(0)"/>
</ParameterList>
</QueryPlan>
</StmtSimple>
</Statements>
</Batch>
</BatchSequence>
</ShowPlanXML>
Omdat Microsoft best aardig is, als je navigeert naar de naamruimte die in de XML
je kunt daadwerkelijk een kopie krijgen van de .xsd
voor het formaat. U kunt dan vanaf de opdrachtprompt van de ontwikkelaar xsd showplanxml.xsd /classes
doen en het geeft je een showplanxml.cs
die u kunt gebruiken met de XmlSerializer
.
Hier is een klein voorbeeldprogramma dat een debugger breekt op een ontbrekende index.
static void Main(string[] args)
{
string result = GetXmlPlanForQuery("select TOTAL_SALES from clients where ACTIVE = 0;");
XmlSerializer ser = new XmlSerializer(typeof(ShowPlanXML));
var plan = (ShowPlanXML)ser.Deserialize(new StringReader(result));
var missingIndexes =
plan.BatchSequence.SelectMany(x => x)
.SelectMany(x => x.Items)
.OfType<StmtSimpleType>()
.Select(x => x.QueryPlan)
.Where(x => x.MissingIndexes != null && x.MissingIndexes.Any());
foreach (var queryPlan in missingIndexes)
{
//This will hit for each statement in the query that was missing a index, check queryPlan.MissingIndexes to see the indexes that are missing.
Debugger.Break();
}
Console.WriteLine("Done");
Console.ReadLine();
}
Ik heb XmlSerializer gebruikt en het gedeseraliseerd naar een klas, maar je kunt dit net zo gemakkelijk in een XDocument laden en vervolgens XPath gebruiken
om alle knooppunten te vinden met de naam MissingIndex
.