SSMS
 sql >> Datenbank >  >> Database Tools >> SSMS

Gibt es eine Möglichkeit, eine Abfrage mit Include Actual Execution Plan programmgesteuert auszuführen und zu sehen, ob ein Indexvorschlag vorliegt oder nicht

Bevor ich darauf eingehe, wie man den tatsächlichen Ausführungsplan im Code erhält und diejenigen findet, die melden, dass Indizes benötigt werden, würde ich Ihnen empfehlen, sich mit Database Engine Tuning Adviser (DTA) , können Sie es mit einer Liste aller Abfragen füttern und es wird sie verarbeiten und Ihnen mögliche Indizes, Statistiken und viele andere Dinge mitteilen, die Ihnen bei der Planung Ihrer Abfragen helfen können.

Noch besser, als ihm eine Liste mit mehr als einer Million Abfragen zu geben, ist, dass Sie vom Server eine Ablaufverfolgung mit den tatsächlich ausgeführten Abfragen erhalten können, die sich auf die Abfragen konzentriert, die die meiste Zeit in Anspruch nehmen.

Um Ihre ursprüngliche Frage zu beantworten, müssen Sie SET STATISTICS XML ON hinzufügen Beim Start der Verbindung erhalten Sie die XML-Daten, auf denen die von Ihnen gezeigte GUI basiert. (Hier finden Sie weitere Informationen zum Abrufen von Pläne ). Sobald Sie dies getan haben, kehren Ihre Abfragen mit einem zusätzlichen Ergebnissatz zurück, der die XML-Datei für den Plan in der ersten Zeile der ersten Spalte enthält.

Hier ist eine Quick-and-Dirty-Funktion, die das erledigt.

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;
}

Und hier ist das XML, das es für die Abfrage select TOTAL_SALES from clients where ACTIVE = 0; zurückgegeben hat die ich ausgeführt habe, die ich in einer meiner lokalen Datenbanken hatte.

<?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>

Nun, da Microsoft ganz nett ist, wenn Sie zu dem im XML aufgelisteten Namespace navigieren Sie können tatsächlich eine Kopie der .xsd erhalten für das format. Sie können dann an der Eingabeaufforderung des Entwicklers xsd showplanxml.xsd /classes ausführen und es wird Ihnen eine showplanxml.cs geben die Sie mit dem XmlSerializer verwenden können .

Hier ist ein kleines Beispielprogramm, das einen Debugger-Break bei einem fehlenden Index durchführt.

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();
}

Ich habe XmlSerializer verwendet und es in eine Klasse deseralisiert, aber Sie könnten dies genauso einfach in ein XDocument laden und dann XPath verwenden um alle Knoten namens MissingIndex zu finden .