Cross Join in einer Zahlentabelle, um Linienscheitelpunkte zu erhalten. Gibt es einen besseren Weg?

8

Die Frage:

Ich habe eine räumliche Tabelle (Straßenlinien), die unter Verwendung des SDE.ST_GEOMETRYbenutzerdefinierten ESRI -Datentyps in einer Oracle 12c- Geodatabase gespeichert ist . Ich möchte die Linienscheitelpunkte auflisten, damit ich letztendlich auf ihre Koordinaten zugreifen und diese aktualisieren kann. Wenn ich SDO_GEOMETRY / Oracle Locator verwenden würde, würde ich die SDO_UTIL.GETVERTICESFunktion verwenden. Ich verwende jedoch nicht SDO_GEOMETRY / Oracle Locator und es gibt keine entsprechende Funktion in SDE.ST_GEOMETRY. Die einzigen SDE.ST_GEOMETRY Funktionen, die ich finden kann, die sich auf Eckpunkte beziehen, sind ST_PointNund ST_NumPoints.

Ich habe eine Abfrage erstellt, die all dies erfolgreich ausführt - die Linienscheitelpunkte werden als Zeilen abgerufen (inspiriert von dieser Seite ):

1    SELECT   a.ROAD_ID
2             ,b.NUMBERS VERTEX_INDEX
3             ,a.SDE.ST_X(SDE.ST_PointN(a.SHAPE, b.NUMBERS)) AS X
4             ,a.SDE.ST_Y(SDE.ST_PointN(a.SHAPE, b.NUMBERS)) AS Y
5    FROM     ENG.ROADS a
6             CROSS JOIN ENG.NUMBERS b
7    WHERE    b.NUMBERS <= SDE.ST_NumPoints(a.SHAPE)
8    --removed to do explain plan: ORDER BY ROAD_ID, b.NUMBERS

----------------------------------------------------------------------------------------------------
| Id  | Operation           | Name                 | Rows  | Bytes |TempSpc| Cost (%CPU)| Time     |
----------------------------------------------------------------------------------------------------
|   0 | SELECT STATEMENT    |                      |  5996 |  1545K|       |   262   (1)| 00:00:01 |
|   1 |  MERGE JOIN         |                      |  5996 |  1545K|       |   262   (1)| 00:00:01 |
|   2 |   INDEX FULL SCAN   | R23715_SDE_ROWID_UK  |    30 |    90 |       |     1   (0)| 00:00:01 |
|*  3 |   SORT JOIN         |                      |  3997 |  1018K|  2392K|   261   (1)| 00:00:01 |
|   4 |    TABLE ACCESS FULL| ROAD                 |  3997 |  1018K|       |    34   (0)| 00:00:01 |
----------------------------------------------------------------------------------------------------

Predicate Information (identified by operation id):
---------------------------------------------------
"   3 - access(""B"".""NUMBERS""<=""SDE"".""ST_NumPoints""(""A"".""SHAPE""))"
"       filter(""B"".""NUMBERS""<=""SDE"".""ST_NumPoints""(""A"".""SHAPE""))"

Es werden CROSS JOINSdie Linien in der ROADSTabelle zu einer NUMBERSTabelle (und die Ergebnisse werden auf die Anzahl der Eckpunkte in jeder Linie begrenzt).

Statistik: (aktualisiert)

  • Jede Linie hat maximal 30 Eckpunkte (durchschnittlich 4,38 Eckpunkte pro Linie)
  • ROADS hat 3.997 Linien
  • NUMBERS hat 30 Zeilen (fortlaufende Nummern ab 1)
  • Die Ergebnismenge enthält 17.536 Zeilen

Die Leistung ist jedoch schlecht (40 Sekunden), und ich kann nicht anders als zu denken - gibt es einen eleganteren Weg, dies zu tun? Die Verwendung einer Zahlentabelle und eines Cross-Joins scheint mir ein schlampiger Ansatz zu sein. Gibt es einen besseren Weg?

Laienbedingungen würden geschätzt; Ich bin ein Typ für öffentliche Arbeiten, kein DBA.


Update Nr. 1:

Wenn ich die Zeilen 3 und 4 (Zeichenfolge von X & Y-bezogenen Funktionen) aus der Abfrage entferne, wird sie sofort ausgeführt. Aber natürlich kann ich diese Zeilen nicht einfach entfernen, ich brauche die X & Y-Spalten. Das lässt mich glauben, dass die langsame Leistung etwas mit den X & Y-Funktionen zu tun hat.

Wenn ich die Punkte jedoch in eine statische Tabelle exportiere und dann die X & Y-Funktionen darauf ausführe, wird dies ebenfalls sofort ausgeführt.

Bedeutet dies also, dass die langsame Leistung durch die X & Y-Funktionen verursacht wird, außer, nein, ist dies nicht der Fall? Ich bin verwirrt.


Update Nr. 2:

Wenn ich das X und Y aus der Abfrage herausbringe, sie in eine äußere Abfrage einfüge und der inneren Abfrage ROWNUM hinzufüge, geht es viel schneller (16 Sekunden - aktualisiert):

    SELECT
        ROWNUM
        ,ROAD_ID
        ,VERTEX_INDEX
        ,SDE.ST_X(ST_POINT) AS X
        ,SDE.ST_Y(ST_POINT) AS Y
    FROM
    (
        SELECT  
              ROWNUM
              ,a.ROAD_ID
              ,b.NUMBERS VERTEX_INDEX
              ,SDE.ST_PointN(a.SHAPE, b.NUMBERS) AS ST_POINT
        FROM  ENG.ROAD a
              CROSS JOIN ENG.NUMBERS b
        WHERE b.NUMBERS <= SDE.ST_NumPoints(a.SHAPE)
    )
    --removed to do explain plan: ORDER BY ROAD_ID, VERTEX_INDEX

-------------------------------------------------------------------------------------------------------
| Id  | Operation              | Name                 | Rows  | Bytes |TempSpc| Cost (%CPU)| Time     |
-------------------------------------------------------------------------------------------------------
|   0 | SELECT STATEMENT       |                      |  5996 |   322K|       |   262   (1)| 00:00:01 |
|   1 |  COUNT                 |                      |       |       |       |            |          |
|   2 |   VIEW                 |                      |  5996 |   322K|       |   262   (1)| 00:00:01 |
|   3 |    COUNT               |                      |       |       |       |            |          |
|   4 |     MERGE JOIN         |                      |  5996 |  1545K|       |   262   (1)| 00:00:01 |
|   5 |      INDEX FULL SCAN   | R23715_SDE_ROWID_UK  |    30 |    90 |       |     1   (0)| 00:00:01 |
|*  6 |      SORT JOIN         |                      |  3997 |  1018K|  2392K|   261   (1)| 00:00:01 |
|   7 |       TABLE ACCESS FULL| ROAD                 |  3997 |  1018K|       |    34   (0)| 00:00:01 |
-------------------------------------------------------------------------------------------------------

Predicate Information (identified by operation id):
---------------------------------------------------
"   6 - access(""B"".""NUMBERS""<=""SDE"".""ST_NumPoints""(""A"".""SHAPE""))"
"       filter(""B"".""NUMBERS""<=""SDE"".""ST_NumPoints""(""A"".""SHAPE""))"

Justin Cave erklärt hier, warum ROWNUM die Leistung verbessert : Warum verbessert das Hinzufügen von ROWNUM zu einer Abfrage die Leistung?

Diese Leistungsverbesserung ist zwar gut, aber noch nicht gut genug. Und ich kann nicht anders, als zu glauben, dass ich immer noch nicht ganz verstehe, wie die Abfrage funktioniert oder warum sie so langsam ist wie sie ist.

Die Frage bleibt noch: Gibt es einen besseren Weg?

Wilson
quelle
Kommentare sind nicht für eine ausführliche Diskussion gedacht. Dieses Gespräch wurde in den Chat verschoben .
Paul White 9

Antworten:

7

Ich weiß ein wenig über die Leistung von Oracle und so ziemlich nichts über benutzerdefinierte Datentypen, aber ich werde versuchen, Ihnen einen Plan zur Verbesserung der Leistung zu geben.

1) Stellen Sie sicher, dass Sie keinen Erklärungsplan erhalten.

Es ist möglich, Erklärungspläne zu erhalten, auch wenn Sie keine ausgefeilte Datenbanksoftware haben. Was passiert, wenn Sie ausführen set autotrace on explain?

Sie können auch DBMS_XPLAN ausprobieren . Speichern Sie zunächst den Plan, indem Sie Ihre Anfrage mit ein paar zusätzlichen Schlüsselwörtern abschließen:

explain plan for (SELECT... your query goes here); 

Führen Sie dann Folgendes aus:

SELECT PLAN_TABLE_OUTPUT FROM TABLE(DBMS_XPLAN.DISPLAY());

Es ist möglich, dass keines davon funktioniert und Sie wirklich keinen Erklärungsplan erhalten können. Ich wollte nur überprüfen, ob es mit einem Erklärungsplan für die Community viel einfacher ist, Ihnen zu helfen.

2) Anforderungen berücksichtigen.

Sie sagten, dass 20 Sekunden nicht gut genug sind. Haben Sie oder jemand anderes genau definiert, was gut genug ist? Gibt es Raum für Verhandlungen? Muss Ihre Abfrage genau eine SELECT-Abfrage sein? Könnten Sie in einem Schritt eine globale temporäre Tabelle füllen und die gewünschten Ergebnisse im nächsten auswählen? Könnten Sie eine gespeicherte Prozedur erstellen , die eine Ergebnismenge zurückgibt, und diese aufrufen?

3) Legen Sie eine Untergrenze für die Zeit fest, die zum Abschließen der Abfrage erforderlich ist.

Ich schlage vor, eine einfache Abfrage auszuführen, die "betrügt", um herauszufinden, wie eine gut optimierte Abfrage aussehen würde. Wie lange dauert diese Abfrage, bei der nur die ersten Scheitelpunkte abgerufen werden, beispielsweise?

SELECT
    ROWNUM
    ,ROAD_ID
    ,VERTEX_INDEX
    ,SDE.ST_X(ST_POINT) AS X
    ,SDE.ST_Y(ST_POINT) AS Y
FROM
(
    SELECT  
          ROWNUM
          ,a.ROAD_ID
          ,1 VERTEX_INDEX
          ,SDE.ST_PointN(a.SHAPE, 1) AS ST_POINT
    FROM  ENG.ROAD a
)
ORDER BY ROAD_ID, VERTEX_INDEX;

Ich vermute, das gibt Ihnen 4000 Zeilen. Wenn Sie die Antwortzeit dieser Abfrage mit 17,5 / 4 multiplizieren, erhalten Sie möglicherweise eine gute Untergrenze für die Gesamtausführungszeit.

Wenn Ihre Untergrenze für die Gesamtausführungszeit länger ist als in Schritt 2 festgelegt, müssen Sie entweder mit Ihrem Datenmodell kreativ werden, indem Sie die Ergebnisse im Voraus berechnen und in Tabellen speichern, oder Sie müssen die erforderliche Antwortzeit neu aushandeln.

4) Benchmark, um herauszufinden, welche Funktionen am meisten zu Ihrer Ausführungszeit beitragen.

Sie waren mit Update Nr. 1 auf dem richtigen Weg, müssen jedoch versuchen, den Arbeitsaufwand zu kontrollieren. Ist es beispielsweise möglich, eine Gruppe relativ einfacher Abfragen zu schreiben, die jede Funktion genau 10000 Mal ausführen? Wie vergleichen sich die Antwortzeiten?

5) Geh zur Arbeit.

Abhängig von den in Schritt 2 festgelegten Anforderungen und den in Schritt 4 gefundenen Anforderungen versuchen Sie einen beliebigen Trick, um die Laufzeit der Abfrage zu verringern. Können Sie Ergebnisse vorberechnen und speichern? Wenn sich das Problem darauf bezieht, wie oft die Funktionen ausgeführt werden, kann der undokumentierte Materialisierungshinweis hilfreich sein. Dies zwingt Oracle, hinter den Kulissen eine versteckte temporäre Tabelle zu erstellen, um die Ergebnisse zu speichern. Ich weiß nicht, ob es mit den von Ihnen verwendeten speziellen Datentypen kompatibel ist.

Zum Beispiel funktioniert so etwas vielleicht besser? Entschuldigung, wenn es nicht kompiliert wird, aber ich habe keine Möglichkeit zu testen.

WITH ROAD_CTE (ROAD_ID, VERTEX_INDEX, SHAPE) AS
(
    SELECT /*+ materalize */
      a.ROAD_ID
    , b.NUMBERS VERTEX_INDEX
    , a.SHAPE
    FROM ENG.ROAD a
    CROSS JOIN ENG.NUMBERS b
    WHERE b.NUMBERS <= SDE.ST_NUMPOINTS(a.SHAPE)
)
, CTE_WITH_ST_POINT (ROAD_ID, VERTEX_INDEX, ST_POINT) AS
(
    SELECT /*+ materalize */
      rcte.ROAD_ID
    , rcte.VERTEX_INDEX
    , SDE.ST_PointN(rcte.SHAPE, rcte.VERTEX_INDEX) ST_POINT
    FROM ROAD_CTE rcte
)
SELECT 
      ROAD_ID
    , VERTEX_INDEX
    , SDE.ST_X(ST_POINT) AS X
    , SDE.ST_Y(ST_POINT) AS Y
FROM CTE_WITH_ST_POINT
ORDER BY ROAD_ID, VERTEX_INDEX;

Wenn Sie nach all dem immer noch nicht weiterkommen, vermute ich, dass es Ihnen zumindest zusätzliche Informationen gibt, die Sie in die Frage einarbeiten können. Viel Glück!

Joe Obbish
quelle
2

Ich habe versucht, mit CONNECT BY (und DUAL) zu prüfen, ob es schneller geht, aber es ist nicht so (es ist ungefähr gleich).

SELECT  ROAD_ID
        ,T.VERTEX_INDEX
        ,SDE.ST_X(SDE.ST_PointN(SHAPE, T.VERTEX_INDEX)) AS X
        ,SDE.ST_Y(SDE.ST_PointN(SHAPE, T.VERTEX_INDEX)) AS Y
FROM    ENG.ROADS 
        CROSS JOIN
            (
            SELECT LEVEL AS VERTEX_INDEX 
            FROM DUAL CONNECT BY LEVEL <= 
                (
                SELECT MAX(SDE.ST_NUMPOINTS(SHAPE)) 
                FROM ENG.ROADS 
                )
            ) T
WHERE    T.VERTEX_INDEX <= SDE.ST_NUMPOINTS(SHAPE)
--removed to do explain plan: ORDER BY ROAD_ID, VERTEX_INDEX

-------------------------------------------------------------------------------------------------------
| Id  | Operation                      | Name                 | Rows  | Bytes | Cost (%CPU)| Time     |
-------------------------------------------------------------------------------------------------------
|   0 | SELECT STATEMENT               |                      |   200 | 54800 |    36   (0)| 00:00:01 |
|   1 |  NESTED LOOPS                  |                      |   200 | 54800 |    36   (0)| 00:00:01 |
|   2 |   VIEW                         |                      |     1 |    13 |     2   (0)| 00:00:01 |
|*  3 |    CONNECT BY WITHOUT FILTERING|                      |       |       |            |          |
|   4 |     FAST DUAL                  |                      |     1 |       |     2   (0)| 00:00:01 |
|   5 |     SORT AGGREGATE             |                      |     1 |   261 |            |          |
|   6 |      TABLE ACCESS FULL         | ROAD                 |  3997 |  1018K|    34   (0)| 00:00:01 |
|*  7 |   TABLE ACCESS FULL            | ROAD                 |   200 | 52200 |    34   (0)| 00:00:01 |
-------------------------------------------------------------------------------------------------------

Predicate Information (identified by operation id):
---------------------------------------------------
"   3 - filter(LEVEL<= (SELECT MAX(""SDE"".""ST_NUMPOINTS""(""SHAPE"")) FROM "
"              ""ENG"".""ROAD"" ""ROAD""))"
"   7 - filter(""T"".""VERTEX_INDEX""<=""SDE"".""ST_NUMPOINTS""(""ROAD"".""SHAPE""))"

Ich habe die Idee aus diesem Beitrag: Wie berechnet man Bereiche in Oracle?

Wilson
quelle
2

Ergebnisse und Antwort auf die Antwort von Joe Obbish :

Hinweis: Von hier an werde ich die Abfrage in Update 2 als "die Abfrage" bezeichnen. Ich werde mich nicht auf die Anfrage in der ursprünglichen Frage beziehen.

1) Stellen Sie sicher, dass Sie keinen Erklärungsplan erhalten.

Ich kann nicht ausführen set autotrace on explain. Ich erhalte diesen Fehler:ORA-00922: missing or invalid option (#922)

Aber ich bin der Lage , auszuführen DBMS_XPLAN. Ich hatte angenommen, dass ich dazu nicht in der Lage sein würde. Zum Glück habe ich mich geirrt. Ich führe jetzt Erklärungspläne aus.

2) Anforderungen berücksichtigen.

Muss Ihre Abfrage genau eine SELECT-Abfrage sein?

Ich denke, die Abfrage muss genau eine Abfrage sein. Die von mir verwendete Software ist sehr begrenzt und erlaubt keine Mehrfachauswahlanweisungen.

Haben Sie genau definiert, was Ihre Anforderungen sind?

  • Die Abfrage wird verwendet, um die Scheitelpunktkoordinaten zu aktualisieren, nachdem Änderungen an der Liniengeometrie vorgenommen wurden. Dies geschieht normalerweise entweder mit einer einzelnen Zeile oder mit möglicherweise zehn Zeilen, aber wahrscheinlich nicht mit Tausenden von Zeilen. In diesem Szenario ist die aktuelle Leistung der Abfrage ausreichend.
  • Die Abfrage wird auch verwendet, um eine neue Liniengeometrie für alle 3.805 Linien zu erstellen (dies bezieht sich auf das Thema dynamische Segmentierung / lineare Referenzierung ). Dies geschieht in einer Ansicht im laufenden Betrieb, daher ist die Leistung von entscheidender Bedeutung. Die Abfrage müsste wahrscheinlich in weniger als 5 Sekunden ausgeführt werden.

3) Legen Sie eine Untergrenze für die Zeit fest, die zum Abschließen der Abfrage erforderlich ist.

Die Abfrage mit dem ersten Scheitelpunkt wird in 3,75 Sekunden ausgeführt (gibt erwartungsgemäß 3805 Zeilen zurück).

3.75 sec * (16495 total / 3805 lines) = 16.25 sec

Das Ergebnis: Die Untergrenze für die Gesamtausführungszeit ist länger als in Schritt 2 festgelegt (5 Sekunden). Daher denke ich, dass die Lösung darin besteht, "... mit meinem Datenmodell kreativ zu werden, indem die Ergebnisse im Voraus berechnet und in einer Tabelle gespeichert werden" (die erforderliche Antwortzeit ist nicht verhandelbar). Mit anderen Worten, machen Sie eine materialisierte Ansicht.

Darüber hinaus entspricht die Untergrenze von 16,25 Sekunden der Gesamtausführungszeit der Abfrage in Update 2 (16 Sekunden). Ich denke, dies beweist, dass meine Abfrage angesichts der Funktionen und Daten, mit denen ich arbeiten muss, vollständig optimiert ist.

4) Benchmark, um herauszufinden, welche Funktionen am meisten zu Ihrer Ausführungszeit beitragen.

Ich habe zwei Tabellen erstellt (beide enthalten 10.000 Zeilen): ROADS_BMund ROADS_STARTPOINT_BM. Ich habe einfache Abfragen für die Tabellen mit jeder der beteiligten Funktionen ausgeführt. Hier sind die Ergebnisse:

               +-----------+------------------+---------------------------------------------------------------------------+
               | TIME(sec) | RETURN TYPE      | QUERY                                                                     |
+--------------+-----------+------------------+---------------------------------------------------------------------------+
| ST_X         | < 0.5     | Double precision | SELECT ROAD_ID FROM (                                                     |
|              |           | (Number)         | SELECT ROAD_ID, SDE.ST_X(SHAPE) AS X FROM ENG.ROADS_STARTPOINT_BM         |
|              |           |                  | ) WHERE X IS NOT NULL ORDER BY ROAD_ID                                    |
+--------------+-----------+------------------+---------------------------------------------------------------------------+
| ST_Y         | < 0.5     | Double precision | SELECT ROAD_ID FROM (                                                     |
|              |           | (Number)         | SELECT ROAD_ID, SDE.ST_Y(SHAPE) AS Y FROM ENG.ROADS_STARTPOINT_BM         |
|              |           |                  | ) WHERE Y IS NOT NULL ORDER BY ROAD_ID                                    |
+--------------+-----------+------------------+---------------------------------------------------------------------------+
| ST_NumPoints | < 0.5     | Integer          | SELECT ROAD_ID FROM (                                                     |
|              |           |                  | SELECT ROAD_ID, SDE.ST_NumPoints(SHAPE) AS NUM_POINTS FROM ENG.ROADS_BM   |
|              |           |                  | ) WHERE NUM_POINTS IS NOT NULL ORDER BY ROAD_ID                           |
+--------------+-----------+------------------+---------------------------------------------------------------------------+
| ST_PointN*   | **9.5**   | ST_POINT         | SELECT ROAD_ID FROM (                                                     |
|              |           | (ST_GEOMETRY     | SELECT ROAD_ID, SDE.ST_PointN(SHAPE,1) AS ST_POINT FROM ENG.ROADS_BM      |
|              |           | subclass)        | ) WHERE ST_POINT IS NOT NULL ORDER BY ROAD_ID                             |
+--------------+-----------+------------------+---------------------------------------------------------------------------+

Funktionsdokumentation: ST_X , ST_Y , ST_NumPoints , ST_PointN

Das Ergebnis? ST_PointNist das Problem. Die Reaktionszeit von 9,5 Sekunden ist im Vergleich zu den anderen Funktionen miserabel. Ich nehme an, das macht ein bisschen Sinn . ST_PointNGibt einen ST_POINTGeometriedatentyp zurück, der im Vergleich zu den anderen Funktionen, die eine einfache Zahl zurückgeben, ziemlich komplex sein muss.

Hinweis: ST_PointNist schwierig. Der Rückgabetyp ist ST_POINT, mit dem meine Software in einer Ergebnismenge nicht umgehen kann : ORA-24359: OCIDefineObject not invoked for a Object type or Reference.

Um dies zu umgehen, habe ich es in eine Inline-Abfrage eingefügt, um zu verhindern, dass die Spalte an die Ergebnismenge zurückgegeben wird. Aber wenn ich das mache, verarbeitet die Abfrage die Spalte nicht wirklich, was den Zweck des Tests zunichte macht. Also überprüfe ich, ob es in der äußeren Abfrage null ist : WHERE ST_POINT IS NOT NULL ORDER BY RDSEC. Auf diese Weise stelle ich sicher, dass die ST_PointNFunktion tatsächlich verwendet wird, ohne sie an die Ergebnismenge zurückzugeben.

Und natürlich möchte ich einen Apfel-zu-Apfel-Test durchführen, also mache ich die gleiche Art von Inline-Abfrage auch für die anderen Funktionen (obwohl dies technisch nicht notwendig ist).

5) Geh zur Arbeit.

Basierend auf den Schritten 2, 3 und 4 sind hier meine Ergebnisse:

  • Das Problem ist die ST_PointNFunktion. Es ist langsam. Ich glaube nicht, dass ich viel dagegen tun kann. Abgesehen von dem Versuch, die Funktion komplett neu zu programmieren / neu zu erstellen, in der Hoffnung, dass ich es besser machen könnte als die Spezialisten, die sie erstellt haben. Nicht gerade praktisch.
  • Um die von mir gewünschte Leistung zu erzielen, muss ich die Abfrage in einer Tabelle oder einer materialisierten Ansicht vorberechnen.
  • Was "Tricks, an die Sie denken können, um die Abfragelaufzeit zu reduzieren" betrifft, kann ich möglicherweise einige der Scheitelpunkte in den längeren Zeilen entfernen. Dies würde es mir ermöglichen, einige Zeilen aus der NUMBERS-Tabelle zu entfernen (die derzeit 30 Zeilen enthält). Dies würde den Join beschleunigen (obwohl jeder Leistungsgewinn minimal wäre). Ich sollte auch alle Tabellenindizes überprüfen, obwohl meine Leistungsprobleme nicht mit Indizes / Joins zusammenhängen.
  • Aufgrund der Tests glaube ich nicht, dass das Problem "... sich auf die Häufigkeit bezieht, mit der die Funktionen ausgeführt werden".
  • Die CTE-Abfrage, die in # 5 bereitgestellt wurde, wurde einwandfrei kompiliert (ich bin beeindruckt, dass Joe dies durchziehen konnte). Überraschenderweise betrug die Ausführungszeit 30 Sekunden, was keine Verbesserung darstellt. Ich denke, das ST_PointNist auch die Schuld. Die CTE-Abfrage war jedoch keine Verschwendung. Ich habe viel gelernt, indem ich es benutzt habe.

6) Schlussfolgerung.

Ich bin zufrieden, dass ich die Abfrage so weit wie möglich optimiert habe. Ich werde die Vorberechnung einrichten und mit dem nächsten Schritt fortfahren. Ein großes Dankeschön an Joe Obbish; Ich habe eine Menge aus den Schritten gelernt, die er bereitgestellt hat.

Wilson
quelle