質問:
空間テーブル (道路線)があり、ESRIの SDE.ST_GEOMETRY
Oracle 12cのユーザー定義データ型 geodatabase を使用して保存されています。最終的にそれらの座標にアクセスして更新できるように、線の頂点をリストしたいと思います。 SDO_GEOMETRY/Oracle Locatorを使用している場合は、 SDO_UTIL.GETVERTICES
関数を使用します。しかし、私はSDO_GEOMETRY/Oracle Locatorを使用しておらず、SDE.ST_GEOMETRY
に同等の関数はありません。唯一のSDE.ST_GEOMETRY
functions 頂点に関連するのは ST_PointN
および ST_NumPoints
であることがわかります。
これをすべて正常に実行するクエリを考え出しました-行の頂点を行として取得します( このページ に触発されます):
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""))"
これは、ROADS
テーブルの行をNUMBERS
テーブルにCROSS JOINS
します(結果を各行の頂点の数に制限します)。
統計:(更新済み)
ただし、パフォーマンスが悪い(40秒)ので、どうしても考えなければなりません。これを行うためのよりエレガントな方法はありますか?私にとって、数値テーブルとクロス結合を使用することは、ずさんなアプローチのように思えます。もっと良い方法はありますか?
レイマンの用語をいただければ幸いです。私はDBAではなく、Public Worksの人です。
更新#1:
クエリから行3&4(X&Y関連関数の文字列)を削除すると、即座に実行されます。しかし、もちろん、私はこれらの行を単に削除することはできません。need XおよびY列です。したがって、これは、パフォーマンスの低下がXおよびY関数に関係していると私に信じさせます。
ただし、ポイントを静的テーブルにエクスポートしてからX&Y関数を実行すると、これも即座に実行されます。
それでは、パフォーマンスの低下はXとYの機能が原因で発生しているのでしょうか。よくわかりません。
更新#2:
クエリからXとYを取り出し、それらを外部クエリに入れ、ROWNUMを内部クエリに追加すると、はるかに速くなります(16秒-更新)。
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が、ここでROWNUMがパフォーマンスに役立つ理由を説明します。 ROWNUMをクエリに追加するとパフォーマンスが向上する理由
このパフォーマンスの改善は良好ですが、まだ十分ではありません。そして、私は仕方がありませんが、クエリがどのように機能するか、なぜクエリが遅いのか、まだ完全には理解していません。
問題はまだ残っています:より良い方法はありますか?
Oracleのパフォーマンスについては少し知っており、カスタムデータ型についてはほとんど何も知りませんが、パフォーマンスを向上させるための計画を提示します。
1)Explain Planを取得できないことを確認します。
洗練されたデータベースソフトウェアを持っていない場合でも、説明計画を取得することは可能です。 _set autotrace on explain
_を実行するとどうなりますか?
DBMS_XPLAN を試すこともできます。まず、クエリをいくつかの追加のキーワードでラップして、計画を保存します。
_explain plan for (SELECT... your query goes here);
_
次に、これを実行します。
SELECT PLAN_TABLE_OUTPUT FROM TABLE(DBMS_XPLAN.DISPLAY());
これらのいずれも機能せず、説明計画を本当に取得できない可能性があります。説明プランを使用すると、コミュニティがあなたを助けるのがはるかに簡単になるので、私はそれを確認したかっただけです。
2)要件を検討します。
あなたは20秒では十分ではないと言っていました。あなたや他の誰かが十分に良いものを正確に定義しましたか?交渉の余地はありますか?クエリは厳密に1つのSELECTクエリである必要がありますか? グローバル一時テーブル を1つのステップで入力し、次のステップで必要な結果を選択できますか? 結果セットを返すストアドプロシージャ を作成してそれを呼び出すことができますか?
3)クエリの完了に必要な時間の下限を設定します。
よく最適化されたクエリがどのように見えるかを理解するために「チート」する単純なクエリを実行することをお勧めします。たとえば、最初の頂点のみを取得するこのクエリにはどのくらい時間がかかりますか?
_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;
_
私はあなたに4000行を与えると思います。そのクエリの応答時間を17.5/4で乗算すると、合計実行時間の適切な下限が得られます。
合計実行時間の下限がステップ2で設定した値よりも長い場合は、事前に結果を計算してテーブルに格納することにより、データモデルを工夫するか、必要な応答時間を再交渉する必要があります。
4)実行時間に最も貢献している関数を特定するためのベンチマーク
あなたはアップデート#1で正しい軌道に乗っていましたが、行われている作業の量を制御しようとする必要があります。たとえば、各関数を正確に10000回実行する比較的単純なクエリのグループを作成することは可能ですか?応答時間はどのように比較されますか?
5)仕事に行きます
手順2で確立した要件と手順4で見つけた要件に応じて、クエリの実行時間を短縮するために考えられるトリックを試してください。結果を事前に計算して節約できますか?問題が関数の実行回数に関連している場合、 ドキュメント化されていないマテリアライズヒント が役立つことがあります。これにより、Oracleは結果を格納するために、背後に隠れた一時テーブルを作成する必要があります。使用している特殊なデータ型と互換性があるかどうかはわかりません。
たとえば、おそらくこのようなものはより良いパフォーマンスをしますか?コンパイルできない場合はお詫びしますが、テストする方法がありません。
_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;
_
これらすべてを試しても問題が解決しない場合は、質問に編集できる追加情報が少なくとも表示されると思います。幸運を!
Joe Obbishの回答に対する結果と応答:
注:ここからは、Update#2のクエリを「クエリ」と呼びます。元の質問のクエリについては触れません。
1)Explain Planを取得できないことを確認します。
_set autotrace on explain
_を実行できません。私はこのエラーを受け取ります:ORA-00922: missing or invalid option (#922)
しかし、私はamが_DBMS_XPLAN
_を実行できます。私はこれができないだろうと思っていました。幸いにも、私は間違っていました。現在、説明プランを実行しています。
2)要件を検討します。
クエリは厳密に1つのSELECTクエリである必要がありますか?
クエリdoesは正確に1つのクエリである必要があると思います。私が使用しているソフトウェアは非常に制限されており、複数のselectステートメントを使用できません。
要件を正確に定義しましたか?
3)クエリの完了に必要な時間の下限を設定します。
最初の頂点クエリは3.75秒で実行されます(予想どおり3805行を返します)。
3.75 sec * (16495 total / 3805 lines) = 16.25 sec
結果:合計実行時間の下限は、手順2で設定したもの(5秒)よりも長くなります。したがって、解決策は「...事前に結果を計算してテーブルに保存することにより、データモデルで創造的になる」ことです(必要な応答時間は交渉不可能です)。つまり、マテリアライズドビューを作成します。
さらに、16.25秒の下限は、更新#2のクエリの合計実行時間(16秒)と一致します。これは、操作する必要のある関数とデータを考えると、クエリが完全に最適化されていることを証明していると思います。
4)実行時間に最も貢献している関数を特定するためのベンチマーク
2つのテーブル(どちらも10,000行を含む)を作成しました:_ROADS_BM
_と_ROADS_STARTPOINT_BM
_。関連する各関数を使用して、テーブルに対して単純なクエリを実行しました。結果は次のとおりです。
_ +-----------+------------------+---------------------------------------------------------------------------+
| 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 |
+--------------+-----------+------------------+---------------------------------------------------------------------------+
_
関数のドキュメント: ST_X 、 ST_Y 、 ST_NumPoints 、 ST_PointN
結果? _ST_PointN
_が問題です。それは他の機能と比較して9.5秒の応答時間はひどいです。私はこれが少し意味を作ると思います。 _ST_PointN
_は _ST_POINT
_ ジオメトリデータタイプを返します。これは、単純な数値を返す他の関数と比べてかなり複雑である必要があります。
注:_
ST_PointN
_はトリッキーです。その戻り値の型は _ST_POINT
_ であり、これは私のソフトウェアが結果セットでの処理方法を認識していません:_ORA-24359: OCIDefineObject not invoked for a Object type or Reference
_。これを回避するために、インラインクエリに配置して、列が結果セットに返されないようにします。しかし、その場合、クエリは実際には列を処理しないため、テストの目的に反します。したがって、外部クエリでそれがnullかどうかを確認します:_
WHERE ST_POINT IS NOT NULL ORDER BY RDSEC
_。これを行うことにより、_ST_PointN
_関数が結果セットに返されることなく、実際に使用されていることを確認します。そしてもちろん、私はりんご同士のテストをしたいので、他の関数に対しても同様のインラインクエリを実行します(技術的には必要ありませんが)。
5)仕事に行きます
手順2、3、4に基づいて、ここに私の調査結果があります。
ST_PointN
_関数です。 それは遅いです。しかし、これについて私ができることは多くないと思います。関数を完全に再プログラム/再作成する以外は、それを作成したスペシャリストよりも上手くできることを期待しています。 正確には実用的ではありません。ST_PointN
_もそのせいだと思います。 CTEクエリは無駄ではありませんでした。使うだけでたくさんのことを学びました。6)結論
クエリを可能な限り最適化したことに満足しています。事前計算を設定し、次のステップに進みます。 Joe Obbishに感謝します。私は彼が提供したステップからトンを学びました。
CONNECT BY(およびDUAL)を使用してより速くなるかどうかを確認しようとしましたが、そうではありません(ほぼ同じです)。
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""))"
私はこの投稿からアイデアを得ました: Oracleで範囲を計算する方法