Я получаю удовольствие, пытаясь выбрать подходящую настройку пространственного индекса SQL Server 2008 для набора данных, с которым я имею дело.
Набор данных - это полигоны, представляющие контуры по всему земному шару. В таблице 106 000 строк, многоangularьники хранятся в геометрическом поле.
У меня проблема в том, что многие полигоны покрывают большую часть земного шара. По-видимому, это очень затрудняет получение пространственного индекса, который устранит множество строк в основном фильтре. например, посмотрите на следующий запрос:
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 ограничительных рамок в сетке 4х4 Конечным результатом является то, что у меня есть новая таблица с ~ 107 000 строк (что подтверждает, что у меня на самом деле не было много огромных полигонов).
Я добавил пространственный индекс с 1024 ячейками на объект и низкими, низкими, низкими, низкими для ячеек на уровень.
Однако, как ни странно, эта новая таблица с разделенными полигонами по-прежнему работает так же, как и старая. Выполнение указанного выше фильтра. По-прежнему возвращает ~ 60000 строк. Я действительно не понимаю этого вообще, ясно, что я не понимаю, как на самом деле работает пространственный индекс.
Как это ни парадоксально, хотя .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, это в моем наборе разделенных данных, где ни один многоangularьник не занимает более 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 1 Total_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
При рендеринге набор данных выглядит так:
(источник: norman.cx)
Дальнейшие исследования:
Я по-прежнему озадачен низкой производительностью первичного фильтра с этими данными. Итак, я сделал тест, чтобы увидеть, как именно мои данные распадаются. С моими оригинальными нерасщепленными функциями я добавил в таблицу столбец "ячейки". Затем я выполнил 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 объектов для запроса, рассматривающего небольшой прямоangularьник шириной 200 миль?
На данный момент похоже, что я мог бы написать свою собственную схему индексации, которая работала бы лучше, чем SQL Server для этих функций.