Я получаю удовольствие, пытаясь выбрать подходящую настройку пространственного индекса SQL Server 2008 для набора данных, с которым я имею дело.
Набор данных - это полигоны, представляющие контуры по всему земному шару. В таблице 106 000 строк, многоугольники хранятся в геометрическом поле.
У меня проблема в том, что многие из полигонов покрывают большую часть земного шара. По-видимому, это очень затрудняет получение пространственного индекса, который устранит множество строк в основном фильтре. Например, посмотрите на следующий запрос:
SELECT "ID","CODE","geom".STAsBinary() as "geom" FROM "dbo"."ContA"
WHERE "geom".Filter(
geometry::STGeomFromText('POLYGON ((-142.03193662573682 59.53396984952896,
-142.03193662573682 59.88928136451884,
-141.32743833481925 59.88928136451884,
-141.32743833481925 59.53396984952896,
-142.03193662573682 59.53396984952896))', 4326)
) = 1
Это запрос области, которая пересекается только с двумя полигонами в таблице. Независимо от того, какую комбинацию настроек пространственного индекса я выбрал, функция Filter () всегда возвращает около 60 000 строк.
Замена Filter () на STIntersects (), конечно, возвращает только те два полигона, которые мне нужны, но, конечно, это занимает гораздо больше времени (Filter () - 6 секунд, STIntersects () - 12 секунд).
Может кто-нибудь подсказать, есть ли настройка пространственного индекса, которая может улучшиться на 60000 строк, или мой набор данных просто не подходит для пространственной индексации SQL Server?
Дополнительная информация:
Как и предполагалось, я разделил полигоны, используя сетку 4x4 по всему земному шару. Я не мог найти способ сделать это с QGIS, поэтому я написал свой собственный запрос, чтобы сделать это. Сначала я определил 16 ограничительных рамок, первая выглядела так:
declare @box1 geometry = geometry::STGeomFromText('POLYGON ((
-180 90,
-90 90,
-90 45,
-180 45,
-180 90))', 4326)
Затем я использовал каждую ограничивающую рамку, чтобы выбрать и обрезать полигоны, которые пересекали эту рамку:
insert ContASplit
select CODE, geom.STIntersection(@box1), CODE_DESC from ContA
where geom.STIntersects(@box1) = 1
Я, очевидно, сделал это для всех 16 ограничивающих рамок в сетке 4x4. Конечным результатом является то, что у меня есть новая таблица с ~ 107 000 строк (что подтверждает, что у меня не было много огромных полигонов).
Я добавил пространственный индекс с 1024 ячейками на объект и низко, низко, низко, низко для ячеек на уровень.
Однако, как ни странно, эта новая таблица с разделенными полигонами по-прежнему работает так же, как и старая. Выполнение перечисленного выше .фильтра все же возвращает ~ 60 000 строк. Я действительно совсем не понимаю этого, явно не понимаю, как на самом деле работает пространственный индекс.
Как это ни парадоксально, хотя .Filter () по-прежнему возвращает ~ 60000 строк, он улучшил производительность. .Filter () теперь занимает около 2 секунд, а не 6, а .STIntersects () теперь занимает 6 секунд, а не 12.
В соответствии с запросом приведен пример SQL для индекса:
CREATE SPATIAL INDEX [contasplit_sidx] ON [dbo].[ContASplit]
(
[geom]
)USING GEOMETRY_GRID
WITH (
BOUNDING_BOX =(-90, -180, 90, 180),
GRIDS =(LEVEL_1 = LOW,LEVEL_2 = LOW,LEVEL_3 = LOW,LEVEL_4 = LOW),
CELLS_PER_OBJECT = 1024,
PAD_INDEX = OFF,
SORT_IN_TEMPDB = OFF,
DROP_EXISTING = OFF,
ALLOW_ROW_LOCKS = ON,
ALLOW_PAGE_LOCKS = ON) ON [PRIMARY]
Хотя помните, я пробовал целый ряд различных настроек для сеток и ячеек на объект, каждый раз с одинаковыми результатами.
Вот результаты запуска sp_help_spatial_geometry_index, это в моем наборе разделенных данных, где ни один полигон не занимает более 1/16 земного шара:
Base_Table_Rows 215138
Bounding_Box_xmin -90
Bounding_Box_ymin -180
Bounding_Box_xmax 90
Bounding_Box_ymax 180
Grid_Size_Level_1 64
Grid_Size_Level_2 64
Grid_Size_Level_3 64
Grid_Size_Level_4 64
Cells_Per_Object 16
Total_Primary_Index_Rows 378650
Total_Primary_Index_Pages 1129
Average_Number_Of_Index_Rows_Per_Base_Row 1
Total_Number_Of_ObjectCells_In_Level0_For_QuerySample 1
Total_Number_Of_ObjectCells_In_Level0_In_Index 60956
Total_Number_Of_ObjectCells_In_Level1_In_Index 361
Total_Number_Of_ObjectCells_In_Level2_In_Index 2935
Total_Number_Of_ObjectCells_In_Level3_In_Index 32420
Total_Number_Of_ObjectCells_In_Level4_In_Index 281978
Total_Number_Of_Interior_ObjectCells_In_Level2_In_Index 1
Total_Number_Of_Interior_ObjectCells_In_Level3_In_Index 49
Total_Number_Of_Interior_ObjectCells_In_Level4_In_Index 4236
Total_Number_Of_Intersecting_ObjectCells_In_Level1_In_Index 29
Total_Number_Of_Intersecting_ObjectCells_In_Level2_In_Index 1294
Total_Number_Of_Intersecting_ObjectCells_In_Level3_In_Index 29680
Total_Number_Of_Intersecting_ObjectCells_In_Level4_In_Index 251517
Total_Number_Of_Border_ObjectCells_In_Level0_For_QuerySample 1Total_Number_Of_Border_ObjectCells_In_Level0_In_Index 60956
Total_Number_Of_Border_ObjectCells_In_Level1_In_Index 332
Total_Number_Of_Border_ObjectCells_In_Level2_In_Index 1640
Total_Number_Of_Border_ObjectCells_In_Level3_In_Index 2691
Total_Number_Of_Border_ObjectCells_In_Level4_In_Index 26225
Interior_To_Total_Cells_Normalized_To_Leaf_Grid_Percentage 0.004852925
Пересекающиеся_To_Total_Cells_Normalized_To_Leaf_Grid_Percentage 0.288147586
Border_To_Total_Cells_Normalized_To_Leaf_Grid_Percentage 99.70699949
Average_Cells_Per_Object_Normalized_To_Leaf_Grid 405.7282349
Average_Objects_PerLeaf_GridCell 0.002464704
Number_Of_SRIDs_Found 1
Width_Of_Cell_In_Level1 2.8125
Width_Of_Cell_In_Level2 0,043945313
Width_Of_Cell_In_Level3 0.000686646
Width_Of_Cell_In_Level4 1.07E-05
Height_Of_Cell_In_Level1 5.625
Height_Of_Cell_In_Level2 0.087890625
Height_Of_Cell_In_Level3 0.001373291
Height_Of_Cell_In_Level4 2.15E-05
Area_Of_Cell_In_Level1 1012,5
Area_Of_Cell_In_Level2 15.8203125
Area_Of_Cell_In_Level3 0.247192383
Area_Of_Cell_In_Level4 0,003862381
CellArea_To_BoundingBoxArea_Percentage_In_Level1 1.5625
CellArea_To_BoundingBoxArea_Percentage_In_Level2 0.024414063
CellArea_To_BoundingBoxArea_Percentage_In_Level3 0.00038147
CellArea_To_BoundingBoxArea_Percentage_In_Level4 5.96E-06
Number_Of_Rows_Selected_By_Primary_Filter 60956
Number_Of_Rows_Selected_By_Internal_Filter 0
Number_Of_Times_Secondary_Filter_Is_Called 60956
Number_Of_Rows_Output 2
Percentage_Of_Rows_NotSelected_By_Primary_Filter 71.66655821
Percentage_Of_Primary_Filter_Rows_Selected_By_Internal_Filter 0
Internal_Filter_Efficiency 0
Primary_Filter_Efficiency 0,003281055
"Base_Table_Rows 215138" не имеет для меня особого смысла, в таблице 107 000 строк, а не 215 000
При визуализации набор данных выглядит следующим образом:
альтернативный текст http://norman.cx/photos/links/wms.png
Дальнейшие исследования:
Я по-прежнему озадачен низкой производительностью первичного фильтра с этими данными. Итак, я сделал тест, чтобы увидеть, как именно мои данные распадаются. С моими оригинальными нерасщепленными функциями я добавил в таблицу столбец «ячейки». Затем я выполнил 16 запросов, чтобы подсчитать, сколько ячеек в сетке 4x4 охватывает объект. Поэтому я запустил такой запрос для каждой ячейки:
declare @box1 geometry = geometry::STGeomFromText('POLYGON ((
-180 90,
-90 90,
-90 45,
-180 45,
-180 90))', 4326)
update ContA set cells = cells + 1 where
geom.STIntersects(@box1) = 1
Если я тогда посмотрю на столбец «Ячейки» в таблице, во всем моем наборе данных будет только 672 объекта, которые пересекаются с более чем 1 ячейкой в сетке 4x4. Так как же, буквально, может первичный фильтр возвращать 60 000 объектов для запроса, смотрящего на маленький прямоугольник шириной 200 миль?
На данный момент похоже, что я мог бы написать свою собственную схему индексации, которая работала бы лучше, чем работа SQL Server для этих функций.