Bagaimana cara menggunakan indeks dengan inner join di Postgis?

8

Saya mendapat 2 set poin dalam 2 tabel terpisah. Table_a mendapat 100rb poin dan table_b mendapat 300rb poin. Saya mencoba menemukan titik terdekat dalam kaitannya menemukan saya titik dari table_b yang berjarak 50 meter dari tabla_a. Setelah itu hitung kolom jatuh, kelompokkan dengan table_a a_id kolom dan kembalikan nilai tertinggi.

Saya menulis pertanyaan berikut yang memenuhi criteira ini

SELECT DISTINCT ON (a_id) *
FROM (
       SELECT
         table_b.b_id,
         table_b.height - st_3ddistance(table_b.geom, table_a.geom) fall,
         table_b.geom,
         table_a.a_id
       FROM table_a
         INNER JOIN table_b ON _st_3ddwithin(table_a.geom, table_b.geom, 50)) a
WHERE fall >= 0
ORDER BY a_id, fall DESC;

Saya menambahkan indeks geometri 3d:

CREATE INDEX table_a_geom ON table_a USING GIST (geom gist_geometry_ops_nd);
CREATE INDEX table_b_geom ON table_b USING GIST (geom gist_geometry_ops_nd);

Namun masalah saya adalah bahwa saya tidak dapat membuat permintaan untuk menggunakannya. Query planer terus memilih pemindaian urutan yang lambat. Saya menjalankan beberapa pengujian mengubah _st_3ddwithin dengan st_3ddwithin , <<->> <50 , membuat buffer 50 m dan memotong , st_3ddistance <50 tetapi setiap perencana memilih pemindaian urutan. Apakah ada cara untuk menggunakan indeks dengan kinerja lebih tinggi atau mengubah kueri untuk menggunakan indeks?

Paket kueri saya:

Unique  (cost=10462593.70..10473018.43 rows=1 width=144)
  ->  Sort  (cost=10462593.70..10467806.06 rows=2084945 width=144)
        Sort Key: table_a.nmbayuid, ((table_b.height - st_3ddistance(table_b.geomgr, table_a.geom))) DESC
        ->  Nested Loop  (cost=0.00..10243762.28 rows=2084945 width=144)
              Join Filter: (_st_dwithin(table_a.geom, table_b.geomgr, '50'::double precision) AND ((table_b.height - st_3ddistance(table_b.geomgr, table_a.geom)) >= '0'::double precision))
              ->  Seq Scan on table_b  (cost=0.00..1459.47 rows=47147 width=96)
              ->  Materialize  (cost=0.00..10.97 rows=398 width=56)
                    ->  Seq Scan on table_a  (cost=0.00..8.98 rows=398 width=56)
Losbaltica
sumber
1
Apa sebenarnya e_wires_mv12404 yang ada dalam rencana kueri tetapi bukan SQL? Seperti apa tampilan rencana kueri untuk hanya kueri dalam? Saya sarankan tidak menggunakan fungsi yang dimulai dengan _ST. Akhirnya, Anda mungkin bisa mendapatkan kinerja yang lebih baik menggunakan ST_DWithin dalam 2D, menggunakan 35 meter, yang kurang lebih sama dengan 50 meter dari tepi kubus yang berseberangan. Saat Anda mencari titik terdekat terdekat dalam jarak 50 meter, ini mungkin merupakan kandidat yang bagus untuk gabung lateral dan menggunakan ORDER OLEH a.geom <-> b.geom construct.
John Powell
1
Saya memiliki masalah yang sama tahun lalu, saya menggali posting ini untuk Anda , beri tahu saya jika tidak menjawab pertanyaan Anda?
WxGeo
2
Jika Anda melihat definisi fungsi sql Anda melihat bahwa fungsi st_ seperti st_dwithin sebenarnya adalah kotak pembatas cek dan panggilan ke fungsi st . Ini adalah bagian kotak pembatas yang dapat menggunakan indeks ketika Anda memanggil fungsi st secara langsung karena tidak ada cara bagi database untuk menggunakan indeks. Anda memanggil fungsi periksa ulang secara langsung.
Nicklas Avén
1
Apakah Anda ingin saya menuliskan solusi lateral join, saya pikir ini akan bekerja dengan baik untuk apa yang Anda gambarkan
John Powell
1
Fungsi @AndreSilva dimulai dengan _STfungsi internal yang dipanggil oleh PostGIS setelah difilter dengan indeks. Jika Anda memanggil mereka secara langsung, indeks tidak akan digunakan.
dbaston

Jawaban:

6

Pertama, seperti yang telah dicatat dalam komentar, garis bawah yang memimpin sebelum fungsi ST, yaitu, _ST_3DWithin akan menyebabkan indeks tidak digunakan. Saya tidak dapat menemukan penyebutan baru-baru ini, tetapi dalam dokumen lama jika Anda mencari, misalnya, _ST_Intersects menyatakan:

Untuk menghindari penggunaan indeks, gunakan fungsi _ST_Intersects.

EDIT: Sebagaimana diklarifikasi oleh @dbaston dalam komentar, fungsi dengan garis bawah utama adalah fungsi internal yang tidak menggunakan indeks saat dipanggil dan ini terus menjadi kasus (meskipun sulit ditemukan di dokumen).

Kueri Anda mungkin dapat memanfaatkan sintaks LATERAL JOIN, yang cocok untuk masalah k tetangga terdekat (kNN) seperti ini.

SELECT 
   a.a_id, 
   b.b_id
   b.height - ST_3Ddistance(b.geom, a.geom) AS fall,
  FROM table_a a
     LEFT JOIN LATERAL
       (SELECT
            b_id,         
            geom,
            height        
          FROM table_b
          WHERE ST_3Ddwithin(a.geom, geom, 50)
          AND height - ST_3Ddistance(geom, a.geom) > 0
          ORDER BY height - ST_3Ddistance(b.geom, a.geom) DESC 
          LIMIT 1
        ) b ON TRUE;

Ini memungkinkan Anda untuk menemukan geometri k terdekat dari tabel a (dalam hal ini 1, karena LIMIT 1) ke tabel b, diurutkan berdasarkan jarak 3D di antara mereka. Ini ditulis menggunakan LEFT JOIN, karena dapat dibayangkan bahwa mungkin ada beberapa geometri dalam tabel a yang tidak berada dalam jarak 50 meter dari tabel b.

Kueri lateral memungkinkan Anda untuk merujuk kolom dari klausa FROM sebelumnya, yang membuatnya lebih kuat daripada sub kueri standar, lihat dokumen .

Saya tidak dapat menguji ini terhadap data Anda, tetapi ketika saya telah menjalankan pertanyaan serupa, pernyataan EXPLAIN menunjukkan penggunaan indeks yang tepat.

John Powell
sumber
Komentar Anda sangat masuk akal, tetapi saya tidak dapat menerima jawabannya karena kueri yang Anda berikan berbeda menurut kueri asli. Seperti rumah saya sebelumnya "saya tidak mencari satu titik terdekat tetapi sekelompok titik dalam jarak 50 meter dan kemudian saya memilih satu dengan nilai pengurangan tertinggi (tinggi - ST_3Ddistance (geom, a.geom)) dikelompokkan oleh a_id
Losbaltica
Saya memodifikasi permintaan Anda, silakan lihat dan tambahkan perbaikan jika diperlukan :)
Losbaltica
1
Saya memodifikasi kueri, satu-satunya hal yang hilang adalah "tinggi -" dalam urutan oleh. Ini sekarang akan menemukan semua poin dalam 50 dan mengembalikan yang dengan nilai tertinggi - ST_3Ddistance (b.geom, a.geom). Tidak perlu berbeda pada, karena ini semua ditangani oleh setiap permintaan lateral dan LIMIT 1, yaitu, Anda hanya akan mendapatkan nilai jatuh terbesar untuk setiap a_id.
John Powell
Apakah ini sekarang berfungsi seperti yang Anda harapkan semula. Apakah EXPLAIN terlihat masuk akal?
John Powell
Bekerja seperti yang diharapkan. Performa kueri hampir sama tetapi biaya kueri jauh lebih kecil. EXPLAIN Baru: jelaskan.depesz.com/s/Js5G Saya pikir saya mencapai batas optimasi kueri dan hanya berpikir saya bisa lakukan sekarang adalah untuk menyempurnakan server atau memperbaiki tabel / logika. Jadi itu menjawab pertanyaan awal saya
Losbaltica
2

Tautan ini ke dokumentasi PostGIS merekomendasikan langkah-langkah berikut untuk memastikan indeks dan perencana kueri dioptimalkan:

  1. Pastikan statistik dikumpulkan tentang jumlah dan distribusi nilai dalam tabel, untuk memberikan informasi yang lebih baik kepada perencana kueri untuk membuat keputusan seputar penggunaan indeks. VACUUM ANALYZE akan menghitung keduanya.

  2. Jika menyedot debu tidak membantu, Anda dapat sementara memaksa perencana untuk menggunakan informasi indeks dengan menggunakan set enable_seqscan untuk dimatikan; perintah. Dengan cara ini Anda dapat memeriksa apakah perencana sama sekali mampu menghasilkan rencana kueri dipercepat indeks untuk permintaan Anda. Anda hanya harus menggunakan perintah ini hanya untuk debug: secara umum, perencana tahu lebih baik daripada yang Anda lakukan saat menggunakan indeks. Setelah menjalankan kueri, jangan lupa untuk mengaktifkan ENABLE_SEQSCAN kembali, sehingga kueri lain akan menggunakan perencana seperti biasa.

  3. Jika atur enable_seqscan ke off; membantu permintaan Anda untuk menjalankan, Postgres Anda kemungkinan tidak disesuaikan untuk perangkat keras Anda. Jika Anda menemukan perencana yang salah tentang biaya scan indeks berurutan vs coba kurangi nilai random_page_cost di postgresql.conf atau gunakan set random_page_cost ke 1.1 ;. Nilai default untuk parameter adalah 4, coba atur ke 1 (pada SSD) atau 2 (pada disk magnetik cepat). Penurunan nilai membuat perencana lebih cenderung menggunakan pemindaian Indeks.

  4. Jika atur enable_seqscan ke off; tidak membantu permintaan Anda, mungkin saja Anda menggunakan konstruksi Postgres belum dapat diurai. Subquery dengan inline select adalah salah satu contoh - Anda perlu menulis ulangnya sehingga form planner dapat mengoptimalkan, katakanlah, LATERAL JOIN.

Jadi, pertama-tama coba langkah 1-3 sebelum menulis ulang kueri Anda untuk menggunakan indeks. Jika itu tidak berhasil, Anda bisa mencoba memodifikasi kueri.

Saya percaya (dengan kemampuan terbaik saya untuk menyiapkan SQL tanpa menjalankan kode) bahwa kueri di bawah ini akan mengembalikan hasil yang identik dengan Anda, tetapi tidak tahu apakah itu akan lebih efisien.

SELECT DISTINCT on (a_id),
    table_b.b_id as b_id,
    table_b.height - st_3ddistance(table_b.geom, table_a.geom) as fall,
    table_b.geom as b_geom,
    table_a.a_id as a_id
    FROM table_a
         INNER JOIN table_b ON _st_3ddwithin(table_a.geom, table_b.geom, 50)) a
WHERE fall >= 0
ORDER BY a_id, fall DESC;
jgm_GIS
sumber
Sangat menarik setelah mengubah _st_3ddwithin menjadi st_dwithin seperti komentar lain yang disarankan dan menjalankan VACUUM ANALYZE setelah, perencana akhirnya mulai mengejar indeks!
Losbaltica
0

Jika Anda menggunakan Postgres 10 (atau lebih baru), saya akan sangat menyarankan memuat data Anda dalam tabel Paralel.

Anda mungkin perlu menghabiskan waktu menyetelnya (partisi data, dan jumlah pekerja), tapi saya pikir sepadan dengan usaha. Secara teoritis, KNN sangat paralel, mencapai kompleksitas waktu yang konstan, bahkan O (1) jika jumlah pekerja sama dengan jumlah elemen di mana operasi KNN akan dihitung.

Beberapa referensi praktis tentang memuat data dan melakukan kueri disediakan di sini . Dia memberikan beberapa detail tentang penyetelan rencana (untuk memaksa lebih banyak pekerja untuk ditindaklanjuti) di sini . Penting untuk dicatat bahwa skrip paralel melibatkan banyak tugas koordinasi, sehingga batas teoretis ekstrim untuk memberikan paralelisasi paling ekstrem tidak berlaku dalam praktik, karena jaringan, dan karakteristik desain sistem lainnya.

Ricardo Barros Lourenço
sumber