Meningkatkan kinerja STIntersects

11

Tabel T_PINmemiliki 300.000 pin dan T_POLYGONmemiliki 36.000 poligon. T_PINmemiliki indeks ini:

CREATE SPATIAL INDEX [T_PIN_COORD] ON [dbo].[T_PIN]
(
[Coord]
)USING  GEOGRAPHY_GRID 
WITH (GRIDS =(LEVEL_1 = HIGH,LEVEL_2 = HIGH,LEVEL_3 = HIGH,LEVEL_4 = HIGH), 
CELLS_PER_OBJECT = 128, PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, 
SORT_IN_TEMPDB = OFF, DROP_EXISTING = OFF, ONLINE = OFF, 
ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON)
ON [PRIMARY];

T_POLYGON memiliki:

CREATE SPATIAL INDEX [T_POLYGON_COORD] ON [dbo].[T_POLYGON]
(
[COORD]
)USING  GEOGRAPHY_GRID 
WITH (GRIDS =(LEVEL_1 = HIGH,LEVEL_2 = HIGH,LEVEL_3 = HIGH,LEVEL_4 = HIGH), 
CELLS_PER_OBJECT = 128, PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF,
SORT_IN_TEMPDB = OFF, DROP_EXISTING = OFF, ONLINE = OFF, 
ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) 
ON [PRIMARY];

Kueri untuk menemukan persimpangan T_PINdan T_POLYGONmembutuhkan lebih dari 45 menit untuk mengeksekusi:

SELECT COUNT(*)
FROM T_PIN 
INNER JOIN T_POLYGON
    ON T_PIN.Coord.STIntersects(T_POLYGON.COORD) = 1;

Hasilnya adalah 4.438.318 baris.

Bagaimana saya bisa mempercepat permintaan ini?

seb49
sumber
Sudahkah Anda mencoba menggunakan `T_POLYGON.Coord.STIntersects (T_PIN.COORD) = 1 '?
travis
Saya akan tertarik untuk melihat rencana permintaan Anda. Saya bekerja pada Postgres, tetapi harus menjalankan query yang serupa, tetapi pada set data yang jauh lebih besar. Saya telah datang dengan teknik yang membuat yang terburuk menjadi sekitar 2 hari (yang sayangnya melibatkan skrip), tetapi saya akan tertarik untuk melihat rencana permintaan Anda terlebih dahulu.
John Powell
Mengalikan poligon dalam dua tabel saya bersama-sama, saya memiliki angka yang 7000 kali lebih banyak, dalam hal jumlah persimpangan potensial, daripada yang Anda miliki dalam kombinasi Anda, jadi saya pikir dalam cahaya itu, dua hari saya terlihat cukup bagus. Namun, tanpa melihat rencana kueri dan mengetahui sesuatu tentang jumlah rata-rata poin per poligon, akan sulit untuk menghasilkan solusi konkret.
John Powell

Jawaban:

7

Pertama, periksa apakah indeks spasial sedang digunakan dengan melihat rencana eksekusi permintaan dan melihat apakah ada item Indeks Pencarian Clustered (Spasial).

Dengan asumsi itu sedang digunakan, Anda bisa mencoba menambahkan filter sekunder / sederhana berdasarkan kotak pembatas dengan poligon disederhanakan untuk memeriksa terlebih dahulu. Kecocokan dengan poligon yang disederhanakan ini kemudian dapat dijalankan melalui filter utama untuk mendapatkan hasil akhir.

1) Tambahkan kolom geografi dan geometri baru ke tabel [dbo]. [T_POLYGON]:

ALTER TABLE [dbo].[T_POLYGON] ADD SimplePolysGeom geometry;
ALTER TABLE [dbo].[T_POLYGON] ADD SimplePolysGeog geography;

2) Buat poligon kotak pembatas (ini melibatkan konversi awal ke geometri untuk mengambil keuntungan dari STEnvelope ()):

UPDATE [dbo].[T_POLYGON] SET SimplePolysGeom = geometry::STGeomFromWKB(
    COORD.STAsBinary(), COORD.STSrid).STEnvelope();

UPDATE [dbo].[T_POLYGON] SET SimplePolysGeog = geography::STGeomFromWKB(
    SimplePolysGeom.STAsBinary(), SimplePolysGeom.STSrid);

3) Buat indeks spasial pada kolom geografi yang disederhanakan

4) Dapatkan persimpangan terhadap kolom geografi sederhana ini, lalu filter lagi pada tipe data geografi yang cocok. Kira-kira, seperti ini:

;WITH cte AS
(
   SELECT pinID, polygonID FROM T_PIN INNER JOIN T_POLYGON
    ON T_PIN.Coord.STIntersects(T_POLYGON.SimplePolysGeog ) = 1
)
SELECT COUNT(*)
FROM T_PIN 
INNER JOIN T_POLYGON
    ON T_PIN.Coord.STIntersects(T_POLYGON.COORD) = 1
    AND T_PIN.pinID IN (SELECT pinID FROM cte)
    AND T_POLYGON.polygonID IN (SELECT polygonID FROM cte)

EDIT : Anda dapat mengganti (1) dan (2) dengan kolom yang dikomputasi dan bertahan ini. memuji Paul White atas sarannya.

ALTER TABLE [dbo].[T_POLYGON] ADD SimplePolysGeog AS  ([geography]::STGeomFromWKB([geometry]::STGeomFromWKB([COORD].[STAsBinary](),[COORD].[STSrid]).STEnvelope().STAsBinary(),(4326))) PERSISTED
server g2
sumber
Ya, kurang lebih itulah yang saya maksudkan. Masalah yang saya temukan ketika secara spasial "bergabung" dengan dua set tabel dengan area cakupan luas adalah bahwa pengoptimal sering digunakan untuk dua pemindaian tabel penuh dan banyak poin dalam tes poligon.
John Powell
2

Pertanyaan seperti ini sering memakan waktu lama karena kerumitan poligon. Saya telah melihat garis pantai yang kompleks (misalnya) membutuhkan waktu lama untuk menguji titik yang berada di dekat batas mereka, harus memperbesar banyak level untuk menemukan apakah suatu titik ada di dalam atau di luar.

... sehingga Anda dapat mencoba .Reduce()poligon, untuk melihat apakah itu membantu.

Dan untuk lebih lanjut tentang fungsi itu, lihat http://msdn.microsoft.com/en-us/library/cc627410.aspx

Rob Farley
sumber
1

Menurut Microsoft docs, indeks spasial akan digunakan dengan tipe geografi pada metode berikut ketika mereka muncul di awal perbandingan predikat dengan WHEREklausa:

  • STIntersects
  • STDistance
  • STEquals

Hanya metode tipe geometri (daftar terbatas) yang akan memicu penggunaan indeks spasial JOIN ... ON, jadi ubah kode Anda untuk digunakan WHERE geog1.STIntersects(geog2) = 1dan itu akan meningkatkan kecepatan.

Saya juga merekomendasikan untuk mengambil saran dalam jawaban g2server dan menambahkan yang berikut untuk memfilter dan menambahkan indeks spasial di atasnya

ALTER TABLE [dbo].[T_POLYGON] ADD SimplePolysGeog AS
     ([geography]::STGeomFromWKB([geometry]::STGeomFromWKB([COORD].[STAsBinary](),
                                                           [COORD].[STSrid])
                 .STEnvelope().STAsBinary(),(4326))) PERSISTED

Anda kemudian dapat memiliki pertanyaan seperti berikut (saya menulis posting ini dengan cepat dan belum diuji, ini hanya sesuatu untuk dicoba karena saya melihat bahwa pertanyaan Anda dan jawaban yang diposting tertinggi menggunakan JOIN ON spatial op = 1 yang tidak akan menggunakan indeks spasial):

SELECT   
     (SELECT p2.polygon_id
      FROM   T_Polygon p2
      WHERE  p2.coords.STIntersects(t.coords) = 1),
     t.pin_id
FROM     T_PIN t
WHERE    
     (SELECT t.coords.STIntersects(p.coords)
      FROM   T_POLYGON p
      WHERE  t.coords.STIntersects(p.SimplePolysGeog) = 1) = 1

FYI: Hal di atas tidak berfungsi jika SimplePolysGeogberakhir tumpang tindih (seperti dalam pin bisa dalam dua geog yang disederhanakan, jalankan saja ini pada orang-orang di daerah sekitar dalam keadaan dan karena batas normal polys berbagi, kotak terikat tumpang tindih), jadi dalam kebanyakan penggunaan kasus, itu akan menimbulkan kesalahan bahwa subquery mengembalikan lebih dari satu hasil.

Dari Ikhtisar Indeks Spasial MS Documents :

Metode Geografi Didukung oleh Indeks Spasial

Dalam kondisi tertentu, indeks spasial mendukung metode geografi set-berorientasi berikut: STIntersects (), STEquals (), dan STDistance (). Untuk didukung oleh indeks spasial, metode ini harus digunakan dalam klausa WHERE kueri, dan mereka harus terjadi dalam predikat bentuk umum berikut:

geography1.method_name (geography2) comparison_operatorvalid_number

Untuk mengembalikan hasil yang tidak nol, geografi1 dan geografi2 harus memiliki Pengidentifikasi Referensi Spasial (SRID) yang sama . Jika tidak, metode mengembalikan NULL.

Indeks spasial mendukung bentuk predikat berikut:


Kueri yang menggunakan Indeks Spasial

Indeks spasial hanya didukung dalam kueri yang menyertakan operator spasial yang diindeks dalam klausa WHERE. Misalnya sintaksis seperti:

[spatial object].SpatialMethod([reference spatial object]) [ = | < ] [const literal or variable]

Pengoptimal kueri memahami komutatif operasi spasial (itu @a.STIntersects(@b) = @b.STInterestcs(@a)). Namun, indeks spasial tidak akan digunakan jika awal perbandingan tidak mengandung operator spasial (misalnya WHERE 1 = spatial optidak akan menggunakan indeks spasial). Untuk menggunakan indeks spasial, tulis ulang perbandingannya (misalnya WHERE spatial op = 1).

...

Kueri berikut akan berfungsi jika SimplePolysGeogstumpang tindih:

;WITH cte AS
(
   SELECT T_PIN.PIN_ID, 
          T_POLYGON.POLYGON_ID, 
          T_POLYGON.COORD 
   FROM T_PIN 
   INNER JOIN T_POLYGON
   ON T_PIN.COORD.STIntersects(T_POLYGON.SimplePolysGeog) = 1
)

SELECT COUNT(*)
FROM T_PIN 
INNER JOIN cte
ON T_PIN_PIN_ID = cte.PIN_ID
where cte.[COORD].STIntersects(T_PIN.COORD) = 1
pbordeaux
sumber