Поліпшення продуктивності STIntersects


11

Стіл T_PINмає 300 000 шпильок і T_POLYGONмає 36 000 полігонів. T_PINмає цей показник:

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 має:

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];

Запит на пошук перехрестя T_PINта на його виконання T_POLYGONпотрібно більше 45 хвилин:

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

Результат - 4,438,318 рядків.

Як я можу пришвидшити цей запит?


Ви намагалися використовувати `T_POLYGON.Coord.STIntersects (T_PIN.COORD) = 1 '?
travis

Мені буде цікаво переглянути ваш план запитів. Я працюю над Postgres, але доводиться виконувати подібні запити, але на значно більших наборах даних. Я придумав методику, яка змушує моїх найгірших приблизно за 2 дні (що, на жаль, включає сценарії), але мені було б цікаво спочатку переглянути ваш план запитів.
Джон Пауелл

Помножуючи багатокутники в двох моїх таблицях разом, у мене є число, яке в 7000 разів більше за кількістю потенційних перетинів, ніж у вас у вашому поєднанні, тому я думаю, що в такому світлі мої два дні виглядають досить добре. Однак, не бачачи плану запитів і не знаючи чогось про середню кількість балів за багатокутник, буде важко придумати конкретні рішення.
Джон Пауелл

Відповіді:


7

По-перше, перевірте, чи використовується просторовий індекс, переглянувши план виконання запитів і перевірте, чи є елемент пошуку кластерного індексу (просторовий).

Припускаючи, що він використовується, ви можете спробувати додати вторинний / спрощений фільтр на основі обмежувального поля зі спрощеними багатокутниками, щоб перевірити спочатку. Збіги проти цих спрощених багатокутників потім можна пропустити через первинний фільтр, щоб отримати кінцеві результати.

1) Додати нову колонку з географії та геометрії до таблиці [dbo]. [T_POLYGON]:

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

2) Створіть полігони обмежувального поля (це передбачає початкове перетворення в геометрію, щоб скористатися 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) Створіть просторовий індекс на стовпчику спрощеної географії

4) Доберіть перехрестя до цієї спрощеної географічної колонки, після чого ще раз фільтруйте відповідні типи даних географії. Приблизно щось подібне:

;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 : ви можете замінити (1) та (2) цим обчисленим персистуючим стовпцем. Подяка Полу Уайту за пропозицію.

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

Так, це більш-менш те, що я отримував. Проблема, яку я виявив, коли просторово "з'єднував" два набори таблиць із широкою зоною покриття, полягає в тому, що оптимізатор часто працює на два сканування повних таблиць і навантаження точок у тестах на полігонах.
Джон Пауелл

2

Такі запити часто займають тривалий час через складність полігонів. Я бачив, як складні берегові лінії (наприклад) проходять віки для тестування точок, що знаходяться біля їхніх меж, що потребують масштабування багатьох рівнів, щоб виявити, чи є точка всередині чи зовні.

... так що ви можете спробувати .Reduce()багатокутники, щоб побачити, чи це допомагає.

Більше про цю функцію дивіться на http://msdn.microsoft.com/en-us/library/cc627410.aspx


1

Згідно з документами Microsoft, просторові індекси будуть використовуватися для типів географії за такими методами, коли вони з’являються на початку предиката порівняння з WHEREпунктом:

  • STIntersects
  • STDistance
  • STEquals

Лише методи типів геометрії (обмежений список) спричинить використання просторового індексу JOIN ... ON, тому змініть свій код на використання WHERE geog1.STIntersects(geog2) = 1і це повинно підвищити швидкість.

Я також рекомендую порадитись у відповідь g2server та додати наступне для фільтрації та додавання на неї просторового індексу

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

ви могли б мати запит , як таке (я писав цей пост швидко і ще не перевірили, це просто що - то спробувати , тому що я бачив , що ваш запит і наівисочайшіе Відправлені відповіді використовувати JOIN ON просторових оп = 1 , який не використовуватиме просторовий індекс):

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: Вищезгадане не працює, якщо в SimplePolysGeogкінцевому підсумку перекриваються (як у штифті може бути в двох спрощених географах, просто запустили це на людей по дільницях у штаті, і оскільки нормальні поліси діляться кордоном, обмежувальні поля перекриваються), тому в більшості використовуйте випадків, це призведе до помилки, що підзапит повернув більше одного результату.

З огляду просторових індексів MS Docs :

Методи географії, що підтримуються просторовими індексами

За певних умов просторові індекси підтримують такі методи, орієнтовані на задані географії: STIntersects (), STEquals () та STDistance (). Щоб підтримуватися просторовим індексом, ці методи повинні використовуватися в пункті WHERE запиту, і вони повинні виникати в межах предиката такої загальної форми:

geography1.method_name (geography2) порівняння_operatorvalid_number

Для повернення ненульового результату географія1 та географія2 повинні мати однаковий ідентифікатор просторового довідника (SRID) . В іншому випадку метод повертає NULL.

Просторові індекси підтримують такі форми предикатів:


Запити, що використовують просторові індекси

Просторові індекси підтримуються лише у запитах, які включають індексований просторовий оператор у пункті WHERE. Наприклад синтаксис, такий як:

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

Оптимізатор запитів розуміє комунікативність просторових операцій (що @a.STIntersects(@b) = @b.STInterestcs(@a)). Однак просторовий індекс не буде використовуватися, якщо початок порівняння не містить просторового оператора (наприклад WHERE 1 = spatial op, просторовий індекс не буде використовувати). Щоб використовувати просторовий індекс, перепишіть порівняння (наприклад WHERE spatial op = 1).

...

Наступний запит буде працювати, якщо вони SimplePolysGeogsперетинаються:

;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
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.