Problembehandlung bei langsamen Abfragen, die vom Timeout des Abfrageoptimierers betroffen sind

Gilt für: SQL Server

In diesem Artikel wird das Timeout für den Optimierer vorgestellt, wie sich dies auf die Abfrageleistung auswirken kann und wie sie die Leistung optimieren kann.

Was ist Optimierertimeout?

SQL Server verwendet einen kostenbasierten Abfrageoptimierer (QO). Informationen zu QO finden Sie im Handbuch zur Architektur der Abfrageverarbeitung. Ein kostenbasierter Abfrageoptimierer wählt einen Abfrageausführungsplan mit den niedrigsten Kosten aus, nachdem er mehrere Abfragepläne erstellt und bewertet hat. Eines der Ziele von SQL Server Abfrageoptimierer besteht darin, im Vergleich zur Abfrageausführung eine angemessene Zeit mit der Abfrageoptimierung zu verbringen. Das Optimieren einer Abfrage sollte viel schneller sein als die Ausführung. Um dieses Ziel zu erreichen, verfügt QO über einen integrierten Schwellenwert für Aufgaben, die berücksichtigt werden müssen, bevor der Optimierungsprozess beendet wird. Wenn der Schwellenwert erreicht wird, bevor QO alle möglichen Pläne berücksichtigt hat, erreicht er das Timeoutlimit des Optimierer. Ein Optimierer-Timeoutereignis wird im Abfrageplan unter Grund für vorzeitige Beendigung der Anweisungsoptimierung als TimeOut gemeldet. Es ist wichtig zu verstehen, dass dieser Schwellenwert nicht auf der Uhrzeit basiert, sondern auf der Anzahl der vom Optimierer in Betracht gezogenen Möglichkeiten. In aktuellen SQL Server QO-Versionen werden über eine halbe Million Aufgaben berücksichtigt, bevor ein Timeout erreicht wird.

Das Timeout des Optimierers wurde in SQL Server entwickelt und ist in vielen Fällen kein Faktor, der sich auf die Abfrageleistung auswirkt. In einigen Fällen kann sich das Timeout des Optimierer jedoch negativ auf die Sql-Abfrageplanauswahl auswirken, und eine langsamere Abfrageleistung kann dies zur Folge haben. Wenn solche Probleme auftreten, können Sie den Timeoutmechanismus des Optimierer und die Auswirkungen auf komplexe Abfragen bei der Problembehandlung und verbesserung der Abfragegeschwindigkeit unterstützen.

Das Ergebnis des Erreichens des Schwellenwerts für das Optimierertimeout ist, dass SQL Server nicht den gesamten Satz von Optimierungsmöglichkeiten berücksichtigt hat. Das heißt, es kann zu verpassten Plänen gekommen sein, die zu kürzeren Ausführungszeiten führen könnten. QO stoppt am Schwellenwert und berücksichtigt an diesem Punkt den kostengünstigsten Abfrageplan, auch wenn es möglicherweise bessere, unerforschte Optionen gibt. Beachten Sie, dass der nach Erreichen eines Optimierertimeouts ausgewählte Plan eine angemessene Ausführungsdauer für die Abfrage erzeugen kann. In einigen Fällen kann der ausgewählte Plan jedoch zu einer suboptimalen Abfrageausführung führen.

Wie erkennt man ein Optimierertimeout?

Die folgenden Symptome deuten auf ein Timeout des Optimierers hin:

  • Komplexe Abfrage

    Sie verfügen über eine komplexe Abfrage, die viele verknüpfte Tabellen umfasst (z. B. werden acht oder mehr Tabellen verknüpft).

  • Langsame Abfrage

    Die Abfrage wird möglicherweise langsam oder langsamer ausgeführt als auf einer anderen SQL Server Version oder einem anderen System.

  • Abfrageplan zeigt StatementOptmEarlyAbortReason=Timeout an

    • Der Abfrageplan wird im XML-Abfrageplan angezeigt StatementOptmEarlyAbortReason="TimeOut" .

      <?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.518" Build="13.0.5201.2" xmlns="http://schemas.microsoft.com/sqlserver/2004/07/showplan">
      <BatchSequence>
        <Batch>
         <Statements>
          <StmtSimple  ..." StatementOptmLevel="FULL" StatementOptmEarlyAbortReason="TimeOut" ......>
          ...
         <Statements>
        <Batch>
      <BatchSequence>
      
    • Überprüfen Sie die Eigenschaften des Am weitesten links bezogenen Planoperators in Microsoft SQL Server Management Studio. Sie können den Wert von Reason For Early Termination of Statement Optimization (Grund für vorzeitige Beendigung der Anweisungsoptimierung ) auf TimeOut (TimeOut) festlegen.

      Screenshot: Timeout des Optimierers im Abfrageplan in SSMS

Was verursacht ein Optimierertimeout?

Es gibt keine einfache Möglichkeit, zu bestimmen, welche Bedingungen dazu führen würden, dass der Schwellenwert für den Optimierer erreicht oder überschritten wird. Die folgenden Abschnitte sind einige Faktoren, die beeinflussen, wie viele Pläne von QO bei der Suche nach dem besten Plan untersucht werden.

  • In welcher Reihenfolge sollten Tabellen verknüpft werden?

    Hier sehen Sie ein Beispiel für die Ausführungsoptionen von Joins mit drei Tabellen (Table1, Table2, Table3):

    • Join Table1 mit Table2 und das Ergebnis mit Table3
    • Join Table1 mit Table3 und das Ergebnis mit Table2
    • Join Table2 mit Table3 und das Ergebnis mit Table1

    Hinweis: Je größer die Anzahl der Tabellen ist, desto größer sind die Möglichkeiten.

  • Welche Heap- oder Binärstruktur (HoBT)-Zugriffsstruktur soll verwendet werden, um die Zeilen aus einer Tabelle abzurufen?

    • Gruppierter Index
    • Nicht gruppierter Index1
    • Nicht gruppierter Index2
    • Tabellenheap
  • Welche physische Zugriffsmethode soll verwendet werden?

    • Indexsuche
    • Indexüberprüfung
    • Tabellenscan
  • Welcher physische Joinoperator soll verwendet werden?

    • Join geschachtelter Schleifen (NJ)
    • Hashjoin (HJ)
    • Merge join (MJ)
    • Adaptiver Join (ab SQL Server 2017 (14.x))

    Weitere Informationen finden Sie unter Joins.

  • Teile der Abfrage parallel oder seriell ausführen?

    Weitere Informationen finden Sie unter Parallele Abfrageverarbeitung.

Während die folgenden Faktoren die Anzahl der berücksichtigten Zugriffsmethoden und damit die in Betracht gezogenen Möglichkeiten verringern:

  • Abfrage-Prädikate (Filter in der WHERE -Klausel)
  • Existenz von Einschränkungen
  • Kombinationen aus gut durchdachten und aktuellen Statistiken

Hinweis: Die Tatsache, dass QO den Schwellenwert erreicht, bedeutet nicht, dass es zu einer langsameren Abfrage kommt. In den meisten Fällen funktioniert die Abfrage gut, aber in einigen Fällen kann es zu einer langsameren Abfrageausführung kommen.

Beispiel für die Berücksichtigung der Faktoren

Zur Veranschaulichung sehen wir uns ein Beispiel für einen Join zwischen drei Tabellen (t1, und ) an, t2und t3jede Tabelle verfügt über einen gruppierten Index und einen nicht gruppierten Index.

Betrachten Sie zunächst die physischen Jointypen. Hier sind zwei Joins beteiligt. Und da es drei physische Verknüpfungsmöglichkeiten (NJ, HJ und MJ) gibt, kann die Abfrage auf 32 = 9 Arten ausgeführt werden.

  1. NJ – NJ
  2. NJ – HJ
  3. NJ – MJ
  4. HJ – NJ
  5. HJ – HJ
  6. HJ – MJ
  7. MJ – NJ
  8. MJ – HJ
  9. MJ – MJ

Betrachten Sie dann die Joinreihenfolge, die mit Permutationen berechnet wird: P (n, r). Die Reihenfolge der ersten beiden Tabellen spielt keine Rolle, daher kann es P(3,1) = 3 Möglichkeiten geben:

  • Beitreten t1 mit t2 und dann mit t3
  • Beitreten t1 mit t3 und dann mit t2
  • Beitreten t2 mit t3 und dann mit t1

Betrachten Sie als Nächstes die gruppierten und nicht gruppierten Indizes, die für den Datenabruf verwendet werden können. Außerdem verfügen wir für jeden Index über zwei Zugriffsmethoden: Suchen oder Scannen. Das bedeutet, dass für jede Tabelle 22 = 4 Optionen verfügbar sind. Wir haben drei Tabellen, sodass es 43 = 64 Auswahlmöglichkeiten geben kann.

Schließlich kann es unter Berücksichtigung all dieser Bedingungen 9 * 3 * 64 = 1728 mögliche Pläne geben.

Angenommen, es gibt n Tabellen, die in der Abfrage verknüpft sind, und jede Tabelle verfügt über einen gruppierten Index und einen nicht gruppierten Index. Berücksichtigen Sie hierbei die folgenden Faktoren:

  • Joinaufträge: P(n,n-2) = n!/2
  • Jointypen: 3n-1
  • Verschiedene Indextypen mit Such- und Scanmethoden: 4n

Multiplizieren Sie alle oben genannten, und wir können die Anzahl der möglichen Pläne abrufen: 2* n!*12n-1. Wenn n = 4 ist, ist die Zahl 82.944. Wenn n = 6 ist, lautet die Zahl 358.318.080. Mit dem Anstieg der Anzahl von Tabellen, die an einer Abfrage beteiligt sind, erhöht sich also die Anzahl möglicher Pläne geometrisch. Wenn Sie die Möglichkeit von Parallelität und anderen Faktoren einbeziehen, können Sie sich vorstellen, wie viele mögliche Pläne in Betracht gezogen werden. Daher ist es wahrscheinlicher, dass eine Abfrage mit vielen Joins den Timeoutschwellenwert des Optimierers erreicht als eine Abfrage mit weniger Joins.

Beachten Sie, dass die obigen Berechnungen das Worst-Case-Szenario veranschaulichen. Wie bereits erwähnt, gibt es Faktoren, die die Anzahl der Möglichkeiten verringern, z. B. Filter-Prädikate, Statistiken und Einschränkungen. Beispielsweise verringern ein Filter-Prädikat und aktualisierte Statistiken die Anzahl der physischen Zugriffsmethoden, da es effizienter sein kann, eine Indexsuche zu verwenden als eine Überprüfung. Dies führt auch zu einer kleineren Auswahl von Verknüpfungen usw.

Warum wird bei einer einfachen Abfrage ein Optimierertimeout angezeigt?

Nichts mit dem Abfrageoptimierer ist einfach. Es gibt viele mögliche Szenarien, und der Grad der Komplexität ist so hoch, dass es schwer ist, alle Möglichkeiten zu erfassen. Der Abfrageoptimierer kann den Timeoutschwellenwert dynamisch basierend auf den Kosten des Plans festlegen, der in einer bestimmten Phase gefunden wurde. Wenn beispielsweise ein Plan gefunden wird, der relativ effizient erscheint, kann das Aufgabenlimit für die Suche nach einem besseren Plan reduziert werden. Daher kann die unterschätzte Kardinalitätsschätzung (CE) ein Szenario für das frühzeitige Erreichen eines Optimierertimeouts sein. In diesem Fall liegt der Schwerpunkt der Untersuchung auf ce. Dies ist ein seltenerer Fall im Vergleich zum Szenario zum Ausführen einer komplexen Abfrage, das im vorherigen Abschnitt erläutert wurde, aber es ist möglich.

Lösungen

Ein Optimierertimeout, das in einem Abfrageplan angezeigt wird, bedeutet nicht unbedingt, dass dies die Ursache für die schlechte Abfrageleistung ist. In den meisten Fällen müssen Sie möglicherweise nichts gegen diese Situation tun. Der Abfrageplan, mit dem SQL Server endet, kann sinnvoll sein, und die ausgeführte Abfrage kann gut funktionieren. Möglicherweise wissen Sie nie, dass ein Optimierertimeout aufgetreten ist.

Führen Sie die folgenden Schritte aus, wenn Sie die Notwendigkeit zum Optimieren und Optimieren feststellen.

Schritt 1: Einrichten einer Baseline

Überprüfen Sie, ob Sie dieselbe Abfrage mit demselben Dataset auf einem anderen Build von SQL Server, mit einer anderen CE-Konfiguration oder auf einem anderen System (Hardwarespezifikationen) ausführen können. Ein Leitprinzip bei der Leistungsoptimierung ist "Ohne Baseline gibt es kein Leistungsproblem". Daher wäre es wichtig, eine Baseline für dieselbe Abfrage einzurichten.

Schritt 2: Suchen nach "ausgeblendeten" Bedingungen, die zum Timeout des Optimierer führen

Untersuchen Sie Ihre Abfrage im Detail, um deren Komplexität zu ermitteln. Bei der ersten Prüfung ist es möglicherweise nicht offensichtlich, dass die Abfrage komplex ist und viele Verknüpfungen umfasst. Ein häufiges Szenario ist hier, dass Ansichten oder Tabellenwertfunktionen beteiligt sind. Auf der Oberfläche kann die Abfrage beispielsweise einfach erscheinen, da sie zwei Ansichten verknüpft. Wenn Sie jedoch die Abfragen in den Ansichten untersuchen, stellen Sie möglicherweise fest, dass jede Ansicht sieben Tabellen verknüpft. Wenn die beiden Ansichten verknüpft sind, erhalten Sie daher einen Join mit 14 Tabellen. Wenn Ihre Abfrage die folgenden Objekte verwendet, führen Sie einen Drilldown in die einzelnen Objekte aus, um zu sehen, wie die zugrunde liegenden Abfragen darin aussehen:

Für all diese Szenarien besteht die häufigste Lösung darin, die Abfrage neu zu schreiben und in mehrere Abfragen aufzuteilen. Weitere Informationen finden Sie unter Schritt 7: Verfeinern der Abfrage .

Unterabfragen oder abgeleitete Tabellen

Die folgende Abfrage ist ein Beispiel, das zwei separate Gruppen von Abfragen (abgeleitete Tabellen) mit jeweils 4-5 Joins verknüpft. Nach der Analyse durch SQL Server wird sie jedoch in eine einzelne Abfrage mit acht verknüpften Tabellen kompiliert.

SELECT ...
  FROM 
    ( SELECT ...
        FROM t1 
        JOIN t2 ON ...
        JOIN t3 ON ...
        JOIN t4 ON ...
        WHERE ...
    ) AS derived_table1
INNER JOIN
  ( SELECT ...
      FROM t5 
      JOIN t6 ON ...
      JOIN t7 ON ...
      JOIN t8 ON ...
      WHERE ...
  ) AS derived_table2 
ON derived_table1.Co1 = derived_table2.Co10 
AND derived_table1.Co2 = derived_table2.Co20

Allgemeine Tabellenausdrücke (Common Table Expressions, CTEs)

Die Verwendung mehrerer allgemeiner Tabellenausdrücke (CTEs) ist keine geeignete Lösung, um eine Abfrage zu vereinfachen und Optimierertimeouts zu vermeiden. Mehrere CTEs erhöhen nur die Komplexität der Abfrage. Daher ist es kontraproduktiv, CTEs beim Lösen von Optimierertimeouts zu verwenden. CTEs sehen so aus, als ob sie eine Abfrage logisch unterbrechen, aber sie werden in einer einzelnen Abfrage kombiniert und als einen einzelnen großen Join von Tabellen optimiert.

Hier sehen Sie ein Beispiel für einen CTE, der als einzelne Abfrage mit vielen Joins kompiliert wird. Es kann den Anschein haben, dass die Abfrage für den my_cte ein einfacher Join mit zwei Objekten ist, aber tatsächlich gibt es sieben weitere Tabellen, die im CTE verknüpft sind.

WITH my_cte AS (
  SELECT ...
    FROM t1 
    JOIN t2 ON ...
    JOIN t3 ON ...
    JOIN t4 ON ...
    JOIN t5 ON ...
    JOIN t6 ON ...
    JOIN t7 ON ...
    WHERE ... )

SELECT ...
  FROM my_cte 
  JOIN t8 ON ...

Ansichten

Stellen Sie sicher, dass Sie die Ansichtsdefinitionen überprüft und alle beteiligten Tabellen abgerufen haben. Ähnlich wie CTEs und abgeleitete Tabellen können Joins in Ansichten ausgeblendet werden. Beispielsweise kann eine Verknüpfung zwischen zwei Sichten letztlich eine einzelne Abfrage mit acht beteiligten Tabellen sein:

CREATE VIEW V1 AS 
  SELECT ...
    FROM t1 
    JOIN t2 ON ...
    JOIN t3 ON ...
    JOIN t4 ON ...
    WHERE ...
GO

CREATE VIEW V2 AS 
  SELECT ...
    FROM t5 
    JOIN t6 ON ...
    JOIN t7 ON ...
    JOIN t8 ON ...
    WHERE ...
GO

SELECT ...
  FROM V1 
  JOIN V2 ON ...

Tabellenwertfunktionen (Table-Valued Functions, TVFs)

Einige Joins sind möglicherweise in TFVs ausgeblendet. Das folgende Beispiel zeigt, was als Join zwischen zwei TFVs angezeigt wird, und eine Tabelle kann ein Join mit neun Tabellen sein.

CREATE FUNCTION tvf1() RETURNS TABLE
AS RETURN
  SELECT ...
    FROM t1 
    JOIN t2 ON ...
    JOIN t3 ON ...
    JOIN t4 ON ...
    WHERE ...
GO 

CREATE FUNCTION tvf2() RETURNS TABLE
AS RETURN
  SELECT ...
    FROM t5
    JOIN t6 ON ...
    JOIN t7 ON ...
    JOIN t8 ON ...
    WHERE ...
GO

SELECT ...
  FROM tvf1() 
  JOIN tvf2() ON ...
  JOIN t9 ON ...

Union

Union-Operatoren kombinieren die Ergebnisse mehrerer Abfragen in einem einzelnen Resultset. Sie kombinieren auch mehrere Abfragen in einer einzelnen Abfrage. Dann erhalten Sie möglicherweise eine einzelne, komplexe Abfrage. Im folgenden Beispiel wird ein einzelner Abfrageplan mit 12 Tabellen angezeigt.

SELECT ...
  FROM t1 
  JOIN t2 ON ...
  JOIN t3 ON ...
  JOIN t4 ON ...

UNION ALL

SELECT ...
  FROM t5 
  JOIN t6 ON ...
  JOIN t7 ON ...
  JOIN t8 ON ...

UNION ALL

SELECT ...
  FROM t9
  JOIN t10 ON ...
  JOIN t11 ON ...
  JOIN t12 ON ...

Schritt 3: Wenn Sie über eine Baselineabfrage verfügen, die schneller ausgeführt wird, verwenden Sie deren Abfrageplan.

Wenn Sie feststellen, dass ein bestimmter Baselineplan, den Sie aus Schritt 1 erhalten, für Ihre Abfrage durch Tests besser geeignet ist, verwenden Sie eine der folgenden Optionen, um QO zur Auswahl dieses Plans zu erzwingen:

Schritt 4: Reduzieren der Planauswahl

Um die Wahrscheinlichkeit eines Optimierertimeouts zu verringern, versuchen Sie, die Möglichkeiten zu reduzieren, die QO bei der Auswahl eines Plans berücksichtigen muss. Dieser Prozess umfasst das Testen der Abfrage mit verschiedenen Hinweisoptionen. Wie bei den meisten Entscheidungen bei QO sind die Auswahlmöglichkeiten nicht immer deterministisch, da es eine Vielzahl von Faktoren gibt, die berücksichtigt werden müssen. Daher gibt es keine einzige garantierte erfolgreiche Strategie, und der ausgewählte Plan kann die Leistung der ausgewählten Abfrage verbessern oder verringern.

Erzwingen einer JOIN-Reihenfolge

Verwenden Sie OPTION (FORCE ORDER) , um die Reihenfolgenpermutationen zu beseitigen:

SELECT ...
  FROM t1
  JOIN t2 ON ...
  JOIN t3 ON ...
  JOIN t4 ON ...
OPTION (FORCE ORDER)

Reduzieren sie die JOIN-Möglichkeiten

Wenn andere Alternativen nicht geholfen haben, versuchen Sie, die Abfrageplankombinationen zu reduzieren, indem Sie die Auswahl physischer Joinoperatoren mit Joinhinweisen einschränken. Beispiel: OPTION (HASH JOIN, MERGE JOIN), OPTION (HASH JOIN, LOOP JOIN) oder OPTION (MERGE JOIN).

Hinweis: Bei der Verwendung dieser Hinweise sollten Sie vorsichtig sein.

In einigen Fällen kann das Einschränken des Optimierrs mit weniger Joinoptionen dazu führen, dass die beste Joinoption nicht verfügbar ist und die Abfrage tatsächlich verlangsamt wird. Außerdem ist in einigen Fällen eine bestimmte Verknüpfung für einen Optimierer erforderlich (z. B. Zeilenziel), und die Abfrage kann einen Plan nicht generieren, wenn dieser Join keine Option ist. Überprüfen Sie daher, nachdem Sie die Joinhinweise auf eine bestimmte Abfrage ausgerichtet haben, ob Sie eine Kombination gefunden haben, die eine bessere Leistung bietet und das Timeout des Optimierers eliminiert.

Im Folgenden finden Sie zwei Beispiele für die Verwendung solcher Hinweise:

  • Verwenden Sie OPTION (HASH JOIN, LOOP JOIN) , um nur Hash- und Schleifenjoins zuzulassen und Mergejoins in der Abfrage zu vermeiden:

    SELECT ...
      FROM t1 
      JOIN t2 ON ...
      JOIN t3 ON ...
      JOIN t4 ON ...
      JOIN t5 ON ...
    OPTION (HASH JOIN, LOOP JOIN)
    
  • Erzwingen sie einen bestimmten Join zwischen zwei Tabellen:

    SELECT ...
      FROM t1 
      INNER MERGE JOIN t2 ON ...
      JOIN t3 ON ...
      JOIN t4 ON ...
      JOIN t5 ON ...
    

Schritt 5: Ändern der CE-Konfiguration

Versuchen Sie, die CE-Konfiguration zu ändern, indem Sie zwischen Legacy CE und New CE wechseln. Das Ändern der CE-Konfiguration kann dazu führen, dass die QO einen anderen Pfad auswählt, wenn SQL Server Abfragepläne auswertet und erstellt. Selbst wenn ein Timeoutproblem des Optimierers auftritt, ist es also möglich, dass Sie einen Plan haben, der optimaler ist als der, der mit der alternativen CE-Konfiguration ausgewählt wurde. Weitere Informationen finden Sie unter Aktivieren des besten Abfrageplans (Kardinalitätsschätzung).

Schritt 6: Aktivieren von Optimiererkorrekturen

Wenn Sie keine Korrekturen des Abfrageoptimierers aktiviert haben, sollten Sie sie mit einer der folgenden beiden Methoden aktivieren:

  • Serverebene: Verwenden Sie das Ablaufverfolgungsflag T4199.
  • Datenbankebene: Verwenden ALTER DATABASE SCOPED CONFIGURATION ..QUERY_OPTIMIZER_HOTFIXES = ON oder Ändern von Datenbank-Kompatibilitätsgraden für SQL Server 2016 und höhere Versionen.

Die QO-Korrekturen können dazu führen, dass der Optimierer bei der Planeruntersuchung einen anderen Pfad einnimmt. Daher kann ein besserer Abfrageplan ausgewählt werden. Weitere Informationen finden Sie unter SQL Server Ablaufverfolgungsflag 4199 des Abfrageoptimierer-Hotfixes.

Schritt 7: Optimieren der Abfrage

Erwägen Sie, die einzelne Abfrage mit mehreren Tabellen mithilfe temporärer Tabellen in mehrere separate Abfragen aufzuteilen. Das Aufteilen der Abfrage ist nur eine der Möglichkeiten, die Aufgabe für den Optimierer zu vereinfachen. Sehen Sie sich folgendes Beispiel an:

SELECT ...
  FROM t1
  JOIN t2 ON ...
  JOIN t3 ON ...
  JOIN t4 ON ...
  JOIN t5 ON ...
  JOIN t6 ON ...
  JOIN t7 ON ...
  JOIN t8 ON ...

Um die Abfrage zu optimieren, versuchen Sie, die einzelne Abfrage in zwei Abfragen aufzuteilen, indem Sie einen Teil der Joinergebnisse in eine temporäre Tabelle einfügen:

SELECT ...
  INTO #temp1
  FROM t1 
  JOIN t2 ON ...
  JOIN t3 ON ...
  JOIN t4 ON ...

GO

SELECT ...
  FROM #temp1
  JOIN t5 ON ...
  JOIN t6 ON ...
  JOIN t7 ON ...
  JOIN t8 ON ...