Pertanyaan:
Saya memiliki tabel spasial (garis jalan), disimpan menggunakan SDE.ST_GEOMETRY
tipe data ESRI yang ditentukan pengguna dalam geodatabase Oracle 12c . Saya ingin membuat daftar garis simpul sehingga saya akhirnya dapat mengakses & memperbarui koordinat mereka. Jika saya menggunakan SDO_GEOMETRY / Oracle Locator, maka saya akan menggunakan
SDO_UTIL.GETVERTICES
fungsinya. Tapi saya tidak menggunakan SDO_GEOMETRY / Oracle Locator, dan tidak ada fungsi yang setara di SDE.ST_GEOMETRY
. Satu-satunya SDE.ST_GEOMETRY
fungsi yang saya temukan yang berhubungan dengan simpul adalah ST_PointN
dan ST_NumPoints
.
Saya telah datang dengan kueri yang berhasil melakukan semua ini - dapatkan simpul garis sebagai baris (terinspirasi oleh halaman ini ):
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""))"
Ini CROSS JOINS
garis dalam ROADS
tabel ke NUMBERS
tabel (dan membatasi hasil ke jumlah simpul di setiap baris).
Statistik: (diperbarui)
- Setiap baris memiliki maksimum 30 simpul (rata-rata 4,38 simpul per baris)
- ROADS memiliki 3.997 jalur
- NUMBERS memiliki 30 baris (angka berurutan mulai dari 1)
- Set hasil memiliki 17.536 baris
Namun, kinerjanya buruk (40 detik), dan saya berpikir - apakah ada cara yang lebih elegan untuk melakukan ini? Bagi saya, menggunakan tabel angka dan gabungan silang sepertinya pendekatan yang ceroboh. Apakah ada cara yang lebih baik?
Ketentuan awam akan dihargai; Saya seorang pria Pekerjaan Umum, bukan DBA.
Perbarui # 1:
Jika saya menghapus baris 3 & 4 (serangkaian fungsi terkait X & Y) dari kueri, itu dieksekusi langsung. Tapi tentu saja, saya tidak bisa hanya menghapus garis-garis ini, saya perlu kolom X & Y. Jadi ini membuat saya percaya bahwa kinerja lambat ada hubungannya dengan fungsi X & Y.
Namun, jika saya mengekspor poin ke tabel statis, dan kemudian menjalankan fungsi X & Y di atasnya, ini langsung dieksekusi juga.
Jadi, apakah ini berarti bahwa kinerja yang lambat disebabkan oleh fungsi X & Y, kecuali, yah, bukan? Saya bingung.
Perbarui # 2:
Jika saya mengeluarkan X dan Y dari kueri, memasukkannya ke dalam kueri luar, dan menambahkan ROWNUM ke kueri dalam, maka itu jauh lebih cepat (16 detik - diperbarui):
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 menjelaskan mengapa ROWNUM membantu kinerja di sini: Mengapa menambahkan ROWNUM ke permintaan meningkatkan kinerja?
Meskipun peningkatan kinerja ini baik, itu belum cukup baik. Dan saya tidak dapat membantu tetapi berpikir bahwa saya masih belum sepenuhnya memahami cara kerja kueri atau mengapa permintaannya lambat.
Pertanyaannya masih ada: apakah ada cara yang lebih baik?
sumber
Jawaban:
Saya tahu sedikit tentang kinerja Oracle dan tidak banyak tentang tipe data khusus, tetapi saya akan mencoba memberi Anda rencana untuk meningkatkan kinerja.
1) Pastikan Anda tidak dapat menjelaskan rencana.
Dimungkinkan untuk menjelaskan rencana walaupun Anda tidak memiliki perangkat lunak basis data yang canggih. Apa yang terjadi jika Anda mengeksekusi
set autotrace on explain
?Anda juga dapat mencoba DBMS_XPLAN . Pertama-tama, hemat paket dengan membungkus kueri Anda dengan beberapa kata kunci tambahan:
Kemudian jalankan ini:
SELECT PLAN_TABLE_OUTPUT FROM TABLE(DBMS_XPLAN.DISPLAY());
Ada kemungkinan bahwa keduanya tidak akan bekerja dan Anda benar-benar tidak bisa mendapatkan rencana yang jelas. Saya hanya ingin memverifikasi itu karena dengan menjelaskan rencana itu akan jauh lebih mudah bagi masyarakat untuk membantu Anda.
2) Pertimbangkan persyaratan.
Anda mengatakan bahwa 20 detik tidak cukup baik. Sudahkah Anda atau orang lain mendefinisikan apa yang cukup baik? Apakah ada ruang untuk negosiasi? Apakah permintaan Anda harus tepat satu permintaan SELECT? Bisakah Anda mengisi tabel sementara global dalam satu langkah dan memilih hasil yang Anda inginkan di langkah berikutnya? Bisakah Anda membuat prosedur tersimpan yang mengembalikan set hasil dan memanggilnya?
3) Menetapkan batas bawah untuk waktu yang dibutuhkan untuk menyelesaikan kueri.
Saya sarankan menjalankan kueri sederhana yang "menipu" untuk mencari tahu seperti apa query yang dioptimalkan dengan baik. Misalnya, berapa lama kueri ini yang hanya memerlukan simpul pertama berlangsung?
Saya menduga itu akan memberi Anda 4000 baris. Jika Anda melipatgandakan waktu respons kueri tersebut dengan 17,5 / 4, itu bisa memberi Anda batas bawah yang baik untuk total waktu eksekusi.
Jika batas bawah Anda untuk total waktu eksekusi lebih lama dari yang Anda buat pada langkah 2, maka Anda harus berkreasi dengan model data dengan menghitung hasil sebelumnya dan menyimpannya dalam tabel atau Anda perlu menegosiasikan ulang waktu respons yang diperlukan.
4) Tolok Ukur untuk mengetahui fungsi mana yang paling berkontribusi terhadap waktu eksekusi Anda.
Anda berada di jalur yang benar dengan Pembaruan # 1 tetapi Anda harus mencoba mengontrol jumlah pekerjaan yang dilakukan. Misalnya, apakah mungkin untuk menulis grup pertanyaan yang relatif sederhana yang menjalankan setiap fungsi tepat 10.000 kali? Bagaimana perbandingan waktu tanggapan?
5) Pergi bekerja.
Bergantung pada persyaratan yang ditetapkan pada langkah 2 dan apa yang Anda temukan di langkah 4 coba trik apa pun yang dapat Anda pikirkan untuk mengurangi runtime kueri. Apakah Anda dapat melakukan pra-perhitungan hasil dan menyimpannya? Jika masalah terkait dengan berapa kali fungsi dieksekusi maka petunjuk materialized tidak berdokumen dapat membantu. Itu memaksa Oracle untuk membuat tabel temp tersembunyi di balik layar untuk menyimpan hasilnya. Saya tidak tahu apakah itu kompatibel dengan tipe data khusus yang Anda gunakan.
Misalnya, mungkin sesuatu seperti ini berkinerja lebih baik? Maaf jika tidak dikompilasi tapi saya tidak punya cara untuk menguji.
Jika Anda masih terjebak setelah semua ini, saya curiga setidaknya akan memberi Anda informasi tambahan yang dapat Anda edit ke dalam pertanyaan. Semoga berhasil!
sumber
Saya mencoba menggunakan CONNECT BY (dan DUAL) untuk melihat apakah itu akan lebih cepat, tetapi tidak (ini hampir sama).
Saya mendapat ide dari posting ini: Bagaimana cara menghitung rentang di Oracle?
sumber
Hasil dan respons terhadap jawaban Joe Obbish :
Catatan: Mulai sekarang, saya akan merujuk ke kueri di Pembaruan # 2 sebagai 'kueri'; Saya tidak akan merujuk ke kueri di pertanyaan awal.
1) Pastikan Anda tidak dapat menjelaskan rencana.
Saya tidak bisa mengeksekusi
set autotrace on explain
. Saya mendapatkan kesalahan ini:ORA-00922: missing or invalid option (#922)
Tapi saya saya bisa mengeksekusi
DBMS_XPLAN
. Saya berasumsi bahwa saya tidak akan dapat melakukan ini. Untungnya saya salah. Saya sekarang menjalankan menjelaskan rencana.2) Pertimbangkan persyaratan.
Apakah permintaan Anda harus tepat satu permintaan SELECT?
Saya pikir query tidak perlu menjadi tepat satu query. Perangkat lunak yang saya gunakan sangat terbatas, dan tidak memungkinkan untuk beberapa pernyataan pilih.
Sudahkah Anda mendefinisikan dengan tepat apa persyaratan Anda?
3) Menetapkan batas bawah untuk waktu yang dibutuhkan untuk menyelesaikan kueri.
Query vertex pertama dijalankan dalam 3,75 detik (menghasilkan 3805 baris, seperti yang diharapkan).
3.75 sec * (16495 total / 3805 lines) = 16.25 sec
Hasilnya: batas bawah untuk total waktu eksekusi lebih lama daripada yang saya buat pada langkah 2 (5 detik). Oleh karena itu, saya pikir solusinya adalah '... menjadi kreatif dengan model data saya dengan menghitung hasil sebelumnya dan menyimpannya dalam sebuah tabel' (waktu respons yang diperlukan tidak dapat dinegosiasikan). Dengan kata lain, buatlah pandangan yang terwujud.
Selain itu, batas bawah 16,25 detik cocok dengan total waktu pelaksanaan kueri dalam Pembaruan # 2 (16 detik). Saya pikir ini membuktikan bahwa permintaan saya sepenuhnya dioptimalkan, mengingat fungsi dan data yang harus saya kerjakan.
4) Tolok Ukur untuk mengetahui fungsi mana yang paling berkontribusi terhadap waktu eksekusi Anda.
Saya telah membuat dua tabel (keduanya berisi 10.000 baris):
ROADS_BM
danROADS_STARTPOINT_BM
. Saya telah menjalankan kueri sederhana di atas meja menggunakan masing-masing fungsi yang terlibat. Inilah hasilnya:Dokumentasi fungsi: ST_X , ST_Y , ST_NumPoints , ST_PointN
Hasil?
ST_PointN
adalah masalahnya. 9,5 detik waktu responnya sangat buruk dibandingkan dengan fungsi lainnya. Saya kira ini masuk akal .ST_PointN
mengembalikanST_POINT
tipe data geometri, yang harus cukup kompleks dibandingkan fungsi lain yang mengembalikan angka sederhana.5) Pergi bekerja.
Berdasarkan langkah 2, 3 & 4, berikut ini adalah temuan saya:
ST_PointN
fungsinya. Itu lambat. Saya pikir tidak banyak yang bisa saya lakukan tentang ini. Selain mencoba memprogram ulang sepenuhnya / membuat ulang fungsi dengan harapan saya bisa melakukan lebih baik daripada spesialis yang membuatnya. Tidak praktis.ST_PointN
yang harus disalahkan untuk itu juga. Namun, permintaan CTE tidak sia-sia; Saya belajar banyak hanya dengan menggunakannya.6. Kesimpulan.
Saya puas bahwa saya telah mengoptimalkan kueri sebanyak mungkin. Saya akan menyiapkan pra-perhitungan, dan beralih ke hal berikutnya. Terima kasih banyak untuk Joe Obbish; Saya telah belajar banyak dari langkah-langkah yang diberikannya.
sumber