(SQL) SQL (2011 год)

Хранение в MS SQL сетевой топологии и отборы маршрутов рекурсивными CTE-процедурами.

Я довольно на страничках своего сайта рассказываю начинающим программистам, как ПРАВИЛЬНО нормализовать различные данные в реляционную форму и как хранить в реляционной структуре самые различные (встречающиеся на практике) обьекты. Пример такого моего рассказа - Cекционирование графики при SQL-хранении. И я всегда стараюсь из каждого своего реального проекта описать на сайте хотя бы одну SQL-процедурку, хотя бы десяток строчек кода.

Итак, на этой страничке я расскажу как правильно хранить в реляционной структуре сетевую топологию (по которой можно строить маршруты движения). Но сначала давайте разберемся, что я понимаю под этим термином в данной заметке. Ибо термины "сеть", "марштут", "топология" слишком многозначны и могут например в контексте интернета обозначать последовательность IP-адресов, по которым проходит реквест от вашего браузера к удаленному серверу (о маршрутах в этом контексте вы можете например посмотреть мою заметку Xping - утилита контроля качества связи).

Но в контексте этой заметки термин "маршрутная топология" я буду применять как описание маршрутов движения транспорта по улицам, поездов по ЖД-сети, автокара по складу и подобным машрутам на плоскости. Как вы понимаете, на скрине слева могли бы быть указаны не только названия железнодорожных станций, но и названия улиц/перекрестков или улиц/перекрестков склада. Маршруты движения по трехмерному пространству в принципе строятся подобным образом, однако это слишком абстрактная задача и может встречаться только в системах управления воздушным движением и сложных многоуровневых складов, в то время как оптимальная прокладка маршрутов по "улицам" на плоскости - это одна из самых распространенных задач в программировании.

Собственно алгоритмы оптимальных маршрутов я в этой кратенькой заметке описывать не стану, я остановлюсь лишь на одном небольшом аспекте этой проблемы - ХРАНЕНИИ данных о сетевой топологии в реляционной СУБД MS SQL и ОТБОРАМ в базе сетевой топологии с помощью COMMON TABLE EXPRESSION (CTE).

Итак, для построения топологии сети я разбиваю каждый перекресток логически на три-четыре (и более) концевых узлов. То есть в базах, сделанных мною - Куровская будет описана четыре раза. Как конечный/начальный элемент маршрута :

И далее по этим записям, описывающим сетевую топологию - строятся маршруты движения (как переходы в узлах сети с одного сегмента на другой).

При такой архитектуре вся Московская ЖД (из 10-ти областей) описывается в моей базе в 771 запись, состоящую из отдельных 72-х сегментов сети, соединенных узлами/перекрестками. Если бы передо мною стояла такая задача - я мог бы загрузить в базу и все улицы Москвы, например. Эта база была бы вероятно побольше.

Между Люберцами и Куровской есть промежуточные станции (или номера домов на некоторой конкретной улице). Поэтому записи в базе об этом сегменте сети выглядят вот так:




Как вы видите, каждый отдельный сегмент сетевой топологии я описываю в виде последовательности записей, где первая запись начинается в поле PrevID значением NULL (что означает что в этом месте есть узел/перекресток) и каждый сегмент заканчивается в поле NextID значением NULL - то есть сегмент сети завершился перекрестком.

Если вы поняли, как описать сеть в реляционной структуре, то вы можете легко понять как в нижелеследующем скрине я получил все начальные точки сетевой топололии Московкой ЖД.




А вот так в базе описана узловая станция Куровская, которая изображена на титульном рисунке выше. Избавиться от направления начальная->конечная точка сетевого сегмента просто, для этого достаточно в отборах указать условие PrevID is NULL or NextID is NULL.




Дочитав до этого места, вы достаточно подготовлены, чтобы понять изюминку - рекурсивный запрос CTE, которым можно вытащить все промежуточные станции от начальной точки сетевого сегмента до конечной:


   1:  ALTER procedure [dbo].[GetNetSegment]
   2:  @StartNetNode uniqueidentifier
   3:  as
   4:  with rzd_net (PrevID,ID,NextID,Code,Title)
   5:  as
   6:  (
   7:  select PrevID,ID,NextID,Code,Title from Net where ID=@StartNetNode
   8:  union all
   9:  select X.PrevID,X.ID,X.NextID,X.Code,X.Title from Net as X 
  10:  join rzd_net on rzd_net.NextID=X.ID
  11:  )
  12:  select * from rzd_net

Чтобы понять, как работают рекурсивные SQL-запросы, надо понять что выражение внутри WITH выполняется в цикле множество раз. Это чем-то напоминает курсор, но только в первом запросе в rzd_net попадает только стартовый узел (из верхней части UNION), а затем к существующему результату в rzd_net джойнится следующий узел (запросом в нижней части UNION). И блок кода внутри with просчитывается еще и еще раз - пока джойнить к итоговой таблице rzd_net нижним внутренним запростом будет уже нечего.

И вот результат рекурсивных SQL-запросов этой процедурой к узлу, изображенному на титульном рисунке:










Теперь собственно о маршрутах движения по этой, описанной в базе, сетевой топологии.

Маршруты движения по сетевой топологии я описываю точкой входа на перекресток Enter и точкой выхода с перекреска ExitNode. Некоторые маршруты закончились на перекрестках, тогда в точке выхода с перекрестка будет NULL.




Надеюсь, описание моих подходов к хранению сетевой топологии и маршрутной информации в реляционной структуре было полезным. Безусловно, работающая система насчитывает десятки сервисов и сотни процедур, но все они устроены примерно одинаково - так как описанная выше CTE-процедура. Владея описанным здесь походом вам будет совсем несложно решать одну из самых распространенных на практике задач о прокладке оптимальных маршрутов, обходе пробок, добавлению поезда к ЖД-расписанию, прокладки пути движения штабелера, составления сложного пути авиаперелета с промежуточными посадками и так далее.


Если вас интересуют другие специфические технологии микрософтовского SQL-сервера, то вы можете почитать еще несколько моих заметок Реализация таймаута на динамически создаваемых SQL JOB, вызывающих SQL CLR сборку, Как парсить XML SOAP в MS SQL.

Если вас интересуют структуры данных РЖД и работа с расписанием Железной Дороги то вы можете почитать мой топик Скачка, раззиповка, перекодирование, парсинг и укладка в базу ЖД-расписания из АСУ Экспресс-3.



Комментарии к этой страничке ( )
ссылка на эту страничку: http://www.vb-net.ru/SQL_CTE/index.htm
<На главную>  <В раздел ASP>  <В раздел NET>  <В раздел SQL>  <В раздел Разное>  <Написать автору>  < Поблагодарить>