SSMS
 sql >> Baza danych >  >> Database Tools >> SSMS

Czy jest jakiś sposób na programowe wykonanie zapytania z Uwzględnij rzeczywisty plan wykonania i zobacz, czy jest jakaś sugestia indeksu, czy nie

Po pierwsze, zanim przejdę do tego, jak uzyskać rzeczywisty plan wykonania w kodzie i znaleźć te, które zgłaszają potrzebę indeksów, polecam zapoznać się z Doradca dostrajania silnika bazy danych (DTA) , możesz podać mu listę wszystkich zapytań, a on je przetworzy, podając możliwe indeksy, statystyki i wiele innych rzeczy, które mogą pomóc w planowaniu zapytań.

Nawet lepsze niż podanie listy ponad 1 mln zapytań jest to, że możesz uzyskać ślad z serwera z faktycznie uruchomionymi zapytaniami i skupi się on na zapytaniach, które zajmują najwięcej czasu.

Aby odpowiedzieć na swoje pierwotne pytanie, musisz dodać SET STATISTICS XML ON na początku połączenia otrzymasz dane XML, na których oparty jest pokazany GUI. (Więcej informacji na temat uzyskiwania plany ). Gdy to zrobisz, Twoje zapytania zwrócą z dodatkowym zestawem wyników zawierającym xml dla planu w pierwszym wierszu pierwszej kolumny.

Oto szybka i brudna funkcja, która to robi.

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

A oto kod XML, który został zwrócony dla zapytania select TOTAL_SALES from clients where ACTIVE = 0; które uruchomiłem w jednej z moich lokalnych baz danych.

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

Teraz, ponieważ Microsoft jest całkiem miły, jeśli przejdziesz do przestrzeni nazw wymienionej w XML faktycznie możesz uzyskać kopię pliku .xsd dla formatu. Następnie możesz z wiersza poleceń programisty zrobić xsd showplanxml.xsd /classes i da ci showplanxml.cs którego można używać z XmlSerializer .

Oto mały przykładowy program, który przerywa działanie debugera w przypadku braku indeksu.

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

Użyłem XmlSerializer i zdeseralizowałem go do klasy, ale równie łatwo można go załadować do XDocument, a następnie użyć XPath aby znaleźć wszystkie węzły o nazwie MissingIndex .




  1. DBeaver
  2.   
  3. phpMyAdmin
  4.   
  5. Navicat
  6.   
  7. SSMS
  8.   
  9. MySQL Workbench
  10.   
  11. SQLyog
  1. Zapytanie SQL Porównaj wartości w ciągu 15 minut i wyświetl wynik na godzinę

  2. Czy są dostępne bezpłatne dodatki do autouzupełniania programu SQL Server Mgmt Studio?

  3. Sterowanie siecią w SSMS

  4. Jak ustawić właściwość Zezwalaj na wartości null w projektancie tabel programu SSMS, aby była zawsze fałszywa?

  5. Dlaczego nazwy aliasów są takie same jak nazwy kolumn, gdy są przechowywane jako widok?