Связи и отношения между таблицами

. Posted in Fox populi - Утилиты

Как обычно, в FoxPro существует путаница и с этим понятиями. Точнее так, эта путаница возникла при включении в среду FoxPro такого объекта как Контейнер базы данных. Впрочем, по порядку.

Итак, в FoxPro термин "связь" или "отношение" применяется в следующих случаях
Постоянная связь - это некоторый графический объект, визуально отображающий взаимосвязь двух таблиц между собой в контейнере базы данных
Связь - это некоторая настройка, накладываемая на две таблицы и определяющая положение указателя записи в подчиненной таблице при перемещении указателя записи в главной таблице


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

Ну, третий вариант термина "связь" я здесь рассматривать не буду, поскольку это фактически просто визуализация термина "связь" во втором его смысле. Поэтому подробно рассмотрю только первые два определения

Постоянная связь (persistent relationship)

Повторю здесь еще раз свое определение термина "постоянная связь"

Постоянная связь - это некоторый графический объект, визуально отображающий взаимосвязь двух таблиц между собой в контейнере базы данных

Обращаю внимание на то, что это именно "графический объект". Он не несет за собой никакого физического смысла.

Но если "постоянная связь" не имеет физического смысла, то для чего же она используется? Вероятно, точнее, следовало бы определить "постоянную связь", как некоторый шаблон, который используется при создании других объектов. Собственно, я знаю только о трех применениях "постоянной связи"
Для настройки и создания определенного вида триггеров в Referential Integrity
Автоматически предлагает создать обычную связь между таблицами, при включении их в DataEnvironment формы или отчета
Автоматически предлагает взаимосвязь нужного вида JOIN при включении таблиц в дизайнер запросов (Query или Local View)

Обратите внимание, что связь в DataEnvironment (или в дизайнере запросов) уже не имеет никакого отношения к "постоянной связи" в контейнере базы данных.

Да, она может быть создана автоматически на основе существующей "постоянной связи", но это будет исключительно рекомендация, с которой Вы можете согласиться или НЕ согласиться и удалить ее из DataEnvironment (или в дизайнере запросов)

Программно "постоянная связь" настраивается через команду ALTER TABLE, используя ключевое слово REFERENCES. "Постоянная связь" всегда устанавливает связь между тэгами структурных индексных файлов связываемых таблиц. Причем хотя бы в одной из таблиц этот тэг должен иметь тип "Primary". Т.е. связь должна быть либо один-к-одному, либо один-ко-многим. Но никак не много-ко-многим.

Связь вообще и "постоянную связь" в частности нельзя настроить дважды между одними и теми же таблицами. Пусть и по разным критериям. В FoxPro такое недопустимо, даже если связь осуществляется через таблицы посредники.

Т.е. если уж вы настроили связь между двумя таблицами, то другую связь между этими же таблицами Вы можете создать только взамен существующей, но никак не одновременно с ней. Попытка организации такой связи вызовет сообщение об ошибке.

"Постоянная связь" может быть настроена "сама на себя". Т.е. можно установить постоянную связь между двумя индексами одной и той же таблицы. Это имеет смысл, если таблица имеет "древовидную" структуру и Вы хотите установить триггер на удаление по типу "Restrict" (установить триггер на удаление по типу "Cascade" - не получится, точнее он не будет работать, поскольку в FoxPro запрещена рекурсия триггеров).

Конечно, можно использовать "постоянную связь" и в качестве визуализации взаимоотношений таблиц в контейнере базы данных. Я имею в виду, что когда Вы открываете контейнер базы данных на модификацию, то Вы видите некоторую картинку структуры базы данных. Вы можете передвинуть объекты так, чтобы визуально проще было понять структуру базы данных.

Так-то оно так, да вот FoxPro имеет весьма слабый инструментарий для полноценного проектирования базы данных. Вот и эта картинка. Физически она хранится в файле ресурсов FoxUser.dbf (fpt). Т.е. если Вы переносите проект на новое место (в другую папку, на другой компьютер), то чтобы картинка не сбилась, надо будет захватить и файл ресурсов. Подробнее о том, как это сделать читайте в разделе Содержимое главной директории проекта

Проблема в том, что файл ресурсов время от времени приходится удалять или хотя бы чистить. Т.е. есть серьезный риск, что через месяц другой, картинка все-таки собьется. После двух...трех таких восстановлений останется только плюнуть на эту возню и заняться чем-то более полезным.

Для полноценного проектирования базы данных лучше использовать другие продукты, которые имеют общее название: Case - средства для проектирования базы данных. Из наиболее известных это:
Power Designer - считается самым лучшим
ERWin - пожалуй, самый известный в паре с BPWin
Visio - знаменит тем, что это продукт MicroSoft, который был русифицирован, и можно найти бесплатную версию в Internet

В принципе была попытка создать полноценное средство для проектирования базы данных в FoxPro. Этот продукт получил название FoxCase. Но ни широкого распространения, ни полноценного развития он так и не получил.

Связь (обычная)

В принципе, в противовес "постоянной связи" этот вид связи следовало бы назвать "временная связь". В том смысле, что данный тип связи устанавливается "по требованию". Т.е. вот надо мне в этом месте организовать связь, я тут же ее и организовываю, используя команду SET RELATION. Отпала в ней надобность - тут же и удаляю. Или она сама удалится при закрытии одной из связанных таблиц.

Это так надо бы было назвать, но дело в том, что данный вид связи исторически появился раньше, чем "постоянная связь". Еще в версии FoxPro for DOS. Когда "постоянной связи" еще в проекте не было. А поскольку не было необходимости этот вид связи от чего-то отличать, то его так и назвали "связь". Без каких-либо уточняющих прилагательных.

Поэтому, когда в FoxPro используют термин "связь", то, скорее всего, речь идет именно об этой связи. Если же говорят о "постоянной связи", то обязательно добавляют уточняющее прилагательное "постоянная".

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

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

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

Между парой таблиц нельзя установить несколько связей одновременно. Неважно, сколько таблиц посредников было использовано. Попытка установить дублирующую связь вызовет сообщение об ошибке. Ну, действительно, если бы было настроено сразу две связи, то на какую запись в подчиненной таблице надо было бы установить указатель записей при перемещении в главной таблице? Вот возникновение данной неопределенности и является причиной подобного запрета.

В отличие от "постоянной связи", обычная связь не требует наличия индекса в главной таблице, но вот в подчиненной таблице индекс должен быть и более того, этот индекс должен быть главным. Одна из распространенных ошибок - это переключение главного индекса в подчиненной таблице. При этом настроенная связь не разрывается, но вот результат ее действия становится весьма неопределенным.

Еще одна тонкость заключается в том, что связь можно настроить не по полному, а по частичному (по первым символам) совпадению ключа. Разумеется, если используется настройка SET EXACT OFF (это настройка по умолчанию).

Например, если подчиненная таблица имеет 2 поля ParentID и NickName и требуется упорядочить записи в пределах каждого значения ParentID в алфавитном порядке NickName, то в подчиненной таблице строится примерно такой индекс:INDEX ON ParentID+NickName TAG SortOrd


Здесь я предполагаю, что ParentID - это поле символьного типа. Тогда в главной таблице настраивается связь по выражению только ParentIDSET RELATION TO ParentID INTO ChildTab


В результате, связь между таблицами будет настроена по значению поля ParentID, но в пределах одного значения ParentID в подчиненной таблице записи буду упорядочены в алфавитном порядке значений поля NickName

Строго говоря, команда SET RELATION TO устанавливает связь по типу один-к-одному. Т.е. если реально связь имеет вид один-ко-многим, то Вы не увидите этих "многих". Отображаться будет только первая попавшаяся запись дочерней таблицы. Для установки связи один-ко-многим после команды SET RELATION TO следует дать команду SET SKIP TO. В подавляющем большинстве случаев использовать команду SET SKIP TO нет необходимости. Надобность в ней возникает только в случае визуального отображения связи вида один-ко-многим. Если речь идет о программировании, то стоит ограничиться только командой SET RELATION TO

Если Вы работаете с DataEnvironment формы или отчета, то установка связи между таблицами фактически означает команду SET RELATION. А установка значения свойства объекта Relation.OneToMany = .T. фактически означает команду SET SKIP TO. Еще раз напомню, для правильной работы связи просто необходимо установить главный индекс в подчиненной таблице. Т.е. свойство Order соответствующего объекта Cursor. Индекс главной таблицы на работу связи никак не влияет.

Особенности работы SET RELATION в Grid (Browse - окне)

Если главную таблицу Вы отображаете в одном Grid, а подчиненную в другом, то только одна команда SET RELATION TO (без дополнительной команды SET SKIP TO) даст эффект отображения данных вида один-ко-многим. Т.е. при перемещении указателя записей в главном Grid в подчиненном автоматически будут отображаться только записи соответствующие текущей записи главной таблицы. Без каких-либо дополнительных настроек и программирования.

Если данные и главной и подчиненной таблицы Вы отображаете в одном Grid, то требуется дать команду SET SKIP TO для визуализации связи один-ко-многим. При этом строки главной таблицы, которым соответствует "много" записей в подчиненной таблице будут отображать свое содержимое только для первой записи подчиненной таблицы, а содержимое остальных строк будет отображаться в виде "квадратиков"

В Grid существует набор свойств, которые позволяют организовать связь с главной таблицей, если в Grid отображается содержимое подчиненной таблица (ChildOrder, LinkMaster, RelationExp). Но я не советовал бы их использовать, поскольку их настройка "наложится" на настройку объекта Relation в DataEnvironment и результат выйдет совершенно непредсказуемый. Лучше настраивать связь в том объекте, который для этого собственно и предназначен.

Особенности работы SET RELATION в отчетах (Report)

Если таблицы имеют связь вида один-ко-многим, то для печати в отчете этих "многих" обязательно следует использовать команду SET SKIP TO (для DataEnvironment настроить Relation.OneToMany = .T.). В этом случае строки главной таблицы, которые имеют несколько записей в подчиненной таблице "размножатся"

Когда следует использовать связь

Ну, с "постоянной связью" все относительно просто. Ее следует использовать, если Вы собираетесь построить Referential Integrity. Просто все остальные способы использования "постоянных связей" не стоят усилий по их созданию.

А вот с "обычной" связью все не так однозначно.

Дело в том, что связь имеет смысл при отображении данных (Grid, Browse, Report), а при программировании зачастую удобнее пользоваться прямым поиском в дочерней таблице через LOCATE или SEEK() (не всегда, но "как правило").

Да и при просмотре не все так гладко. Есть несколько проблем, которые в принципе решаемы при помощи связей, но приводят к заметному (для пользователя) "притормаживанию" приложения:
На подчиненную таблицу требуется наложить дополнительный фильтр
Необходимо иметь возможность изменения сортировки подчиненной таблицы

Эти и ряд других проблем решаются с помощью создания выборок (Select-SQL) или Local View. Когда отображается не вся таблица, а только нужная ее часть. Некое подобие клиент-серверной технологии. Но в этом случае фактически отпадает надобность в установке связи, поскольку вся необходимая информация и так отбирается в запросе или Local View.

В результате получается, что связи имеют смысл, только когда не требуется никакой дополнительной обработки подчиненной таблицы. А в современных задачах это достаточно редкая ситуация. Попробуйте объяснить пользователю, почему это он не может отсортировать Grid, щелкнув по заголовку столбца. Какая еще связь нарушится? А мне хочется!

Так что же, связи вообще не нужны? Ну, почему же. Просто они, как и индексы для сортировки (имею в виду индексы, необходимые для отображения информации в нужном порядке) перешли в разряд "второстепенных" инструментов и используются не на основных таблицах, а на временных выборках и Local View.

 

Добавить комментарий


Защитный код
Обновить

Команды

Релиз Firefox 8, Thunderbird 8 и сопутствующих проектов Mozilla
Проект Mozilla официально представил релиз web-браузера Firefox 8.0, первый выпуск в рамках нового сокращенного цикла разработки, которому будет присвоен статус релиза с пр...14-11-2011

Хороший ход

События объекта Database Container
События объекта Database Container (DBC) предоставляют связь между событиями, написанными разработчиком, и активностью базы данных во время работы пользователя, такой как от...14-11-2011

Руководства

О правилах хорошего тона программирования на Лисе
1. Рекомендуется использовать на каждой рабочей станции копию Лисы. 2. Для ускорения необходимо разделить общедоступные базы и библиотеки. 3. Разделить функции для к...12-11-2011