Die Frage:
Ich habe eine räumliche Tabelle (Straßenlinien), die unter Verwendung des SDE.ST_GEOMETRY
benutzerdefinierten 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.GETVERTICES
Funktion 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_PointN
und 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 JOINS
die Linien in der ROADS
Tabelle zu einer NUMBERS
Tabelle (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?
quelle
Antworten:
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:
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?
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.
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!
quelle
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).
Ich habe die Idee aus diesem Beitrag: Wie berechnet man Bereiche in Oracle?
quelle
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?
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_BM
undROADS_STARTPOINT_BM
. Ich habe einfache Abfragen für die Tabellen mit jeder der beteiligten Funktionen ausgeführt. Hier sind die Ergebnisse:Funktionsdokumentation: ST_X , ST_Y , ST_NumPoints , ST_PointN
Das Ergebnis?
ST_PointN
ist 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_PointN
Gibt einenST_POINT
Geometriedatentyp zurück, der im Vergleich zu den anderen Funktionen, die eine einfache Zahl zurückgeben, ziemlich komplex sein muss.5) Geh zur Arbeit.
Basierend auf den Schritten 2, 3 und 4 sind hier meine Ergebnisse:
ST_PointN
Funktion. 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.ST_PointN
ist 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.
quelle