Связать таблицы в foxpro

Обновлено: 23.04.2024

Как правило, в Visual FoxPro при создании форм, отчетов и запросов используется несколько таблиц, между которыми установлены постоянные отношения. Такие таблицы называются связанными. Из двух связанных таблиц одна является главной, а другая — подчиненной. Главную таблицу называют родительской, а подчиненную — дочерней. При создании индексов для родительской таблицы должен быть определен ключ типа Primary

(Первичный) или типа Candidate (Кандидат), а для дочерней таблицы — индекс для связи с родительской таблицей типа Regular (Обычный).

Рассмотрим, например, отношения между таблицей Customer, содержащей список клиентов, и таблицей Phoncust, в которой размещены данные о телефонах, по которым можно связываться с клиентами. Таблица customer является родительской по отношению к таблице PhonCust. В свою очередь, таблица PhonCust является дочерней по отношению к таблице Customer. При создании отношений между этими таблицами для customer должен быть определен первичный ключ, а для таблицы PhonCust — создан индекс по полю, содержащему код клиента, по которому осуществляется связь между таблицами.

Для создания отношений между таблицами customer и PhonCust выполните следующие действия:

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

Рис. 4.20. Диалоговое окно Edit Relationship для установления отношений между таблицами

Диалоговое окно Edit Relationship (Редактирование отношения) закрывается, а в окне конструктора базы данных отображается в виде линии созданное отношение между таблицами (рис. 4.21).

Установите курсор на линию, соединяющую связываемые таблицы. Толщина линии увеличивается. При щелчке по ней правой кнопкой мыши появляется контекстное меню, содержащее четыре команды (рис. 4.22). команда Remove Relationship (Удалить отношение) удаляет установленное между таблицами отношение. При выборе команды Edit Relationship (Редактирование отношения) открывается диалоговое окно Edit Relationship (Редактирование отношения), позволяющее изменить установленное отношение. Команда Edit Referential Integrity (Редактирование целостности данных) открывает диалоговое окно Referential Integrity Builder (Построитель целостности данных). Об этом окне речь пойдет в следующем разделе.

устанавливает связь между двумя открытыми таблицами: родительской и дочерней. Число дочерних таблиц произвольно. Тип связи – "один к одному" (1:1).

Опции и параметры:

eExpression1 – выражение, связывающее родительскую и дочернюю таблицы. Обычно это индексное выражение управляющего индекса (тега) дочерней таблицы. Установка управляющего индекса или тега может быть выполнена командой SET ORDER.

Дочерняя таблица может, впрочем, не иметь управляющего индекса, если только eExpression1 – это числовое выражение. В противном случае VFP генерирует ошибку.

Если eExpression1 – это числовое выражение и дочерняя таблица не имеет управляющего индекса, то eExpression1 вычисляется, когда файловый указатель перемещается в родительской таблице. Файловый указатель дочерней таблицы перемещается на запись с номером eExpression1.

INTO nWorkArea1 | cTableAlias1 – рабочая область или псевдоним дочерней таблицы.

eExpression2 INTO nWorkArea2 | cTableAlias2 . – задает выражение связи (eExpression2) и еще одну дочернюю таблицу (посредством параметра nWorkArea2 | cTableAlias2), связывая ее с родительской таблицей. После запятой можно задать и другие дочерние таблицы.

IN nWorkArea | cTableAlias – рабочая область или псевдоним родительской таблицы. Если опция опущена, то в качестве родительской берется текущая таблица.

ADDITIVE – сохраняет существующие связи родительской таблицы. При отсутствии опции существующие связи будут разрушены, а новые созданы.

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

После установления связи любое изменение позиции файлового указателя родительской таблицы влечет перемещение позиции файлового указателя дочерней таблицы на первую соответствующую запись, то есть на запись, отвечающую выражению eExpression1. Кроме того, в Browse-окне дочерней таблицы отображаются только те ее записи, которые отвечают выражению eExpression1.

Если подходящая запись не может быть найдена, то дочерняя таблица позиционируется в конце файла.

SET RELATION TO

разрывает все связи текущей таблицы (она должна быть родительской).

Пример 1. Устанавливается связь между родительской (Books) и дочерней таблицами (BooksContent) по полю BookId.

open database 'd:\HomeLibrary\HomeLibrary'

use BooksContent order tag BookId in 0

set relation to BookId into BooksContent in Books

&& Выбираем и просматриваем таблицы

&& Если в родительской таблице выбрана запись с BookId = 1,

&& то Browse-окне дочерней таблицы отобразятся только записи,

&& имеющие BookId = 1 (рис. 15.12)

browse last nowait

&& Разрываем связи в текущей рабочей области

set relation to

229.1.-Отображение-связи-между-таблицами-в-Browse-окнах

Рис. 15.12. Отображение связи между таблицами в Browse-окнах

Установленная связь отобразится также и в окне Data session (рис. 15.13).

229.2.-Отображение-связи-между-таблицами-в-окне-Data-session

Рис. 15.13. Отображение связи между таблицами в окне Data session

Пример 2. Устанавливаются связи между родительской таблицей BooksAuthors и дочерними таблицами Authors и Books.

open database 'd:\HomeLibrary\HomeLibrary'

use Authors order tag AuthorId in 0

use Books order tag BookId in 0

use BooksAuthors in 0

set relation to AuthorId into Authors, ;

BookId into Books in BooksAuthors

&& Выбираем и просматриваем таблицы

&& Выбор записи в родительской таблице BooksAuthors приведет

&& к появлению в Browse-окнах дочерних таблиц по одной записи (рис. 15.14)

browse last nowait

browse last nowait

&& Разрываем все связи в текущей рабочей области

set relation to

229.3.-Родительская-и-две-дочерние-таблицы

Рис. 15.14. Родительская и две дочерние таблицы

Пример 3. Устанавливается связь с таблицей, не имеющей управляющего индекса.

open database d:\HomeLibrary\HomeLibrary

use Authors in 0

set relation to recno('Books') into Authors in Books

&& Выбираем и просматриваем таблицы

browse last nowait

&& Разрываем связь между таблицами

set relation off into Authors

SET RELATION OFF INTO nWorkArea | cTableAlias

разрывает связь родительской таблицы, открытой в текущей рабочей области, с дочерней, задаваемой параметром nWorkArea | cTableAlias.

SET SKIP TO [TableAlias1 [, TableAlias2] . ]

создает связь "один ко многим" (1:М) между таблицами.

TO TableAlias1 [, TableAlias2] . – содержит псевдонимы дочерних таблиц.

Если в текущей рабочей области открыта родительская таблица, тогда вызов

разрывает ее связь 1:М.

Чтобы установить связь 1:М, первоначально нужно, используя SET RELATION, установить связь 1:1, а затем уже употребить SET SKIP.

Нередко дочерняя таблица содержит несколько записей, отвечающих одной записи родительской таблицы. Команда SET SKIP позволяет установить связь одной записи родительской таблицы с несколькими записями дочерней. Связь проявляется следующим образом: чтобы выполнить перемещение на соседнюю запись родительской таблицы, команду SKIP потребуется употребить столько раз, сколько записей дочерней таблицы соответствует текущей записи родительской. Иными словами, файловый указатель родительской таблицы будет находиться на одной ее записи до тех пор, пока не будут выполнены перемещения по всем отвечающим ей записям дочерней таблицы.

Пусть дочерняя таблица TableAlias1 в свою очередь связана с другой таблицей TableAlias2, то есть является по отношению к ней родительской, а старшая родительская таблица связана посредством SET SKIP с таблицами TableAlias1 и TableAlias2. Тогда для перемещения с текущей записи старшей родительской таблицы на соседнюю запись команду SKIP потребуется выполнить m1 * m2 раз, где m1 и m2 – это соответственно число записей таблиц TableAlias1 и TableAlias2, отвечающих текущей записи родительской таблицы.

Выберем на вкладке Data диспетчера проекта базу данных HomeLibrary и нажмем на кнопку Modify. В открывшемся проектировщике базы данных установим между таблицами связи (рис. 2.9).

8.1.-Связи-таблиц-базы-данных-HomeLibrary.dbc

Рис. 2.9. Связи таблиц базы данных HomeLibrary.dbc

Связь, например, таблицы Authors с таблицей BooksAuthors устанавливается мышкой: захватывается первичный индекс таблицы Authors (он выделен жирным шрифтом) и перетаскивается к одноименному индексу таблицы BooksAuthors. Тип устанавливаемой связи – 1:М (1 ко многим). Таблица Authors в установленной связи является родительской, а BooksAuthors – дочерней.

Установленную связь можно отредактировать (рис. 2.10), дважды ударив по отображающей ее линии мышкой.

8.2.-Диалог-Edit-Relationship

Рис. 2.10. Диалог Edit Relationship

Установленные связи дают возможность ввести правила обеспечения ссылочной целостности данных.

Нажмем в диалоге Edit Relationship кнопку Referential Integrity или выберем пункт Edit Referential Integrity меню Database, откроем построитель ссылочной целостности данных и приведем его табличную часть в соответствие с рис. 2.11.

8.3.-Формирование-правил-обеспечения-ссылочной-целостности-данных

Рис. 2.11. Формирование правил обеспечения ссылочной целостности данных

Для таблиц Authors и BooksAuthors установлено правило обновление "Каскад", означающее, что при изменении кода автора (поле AuthorId) в таблице Authors такие же изменения произойдут в соответствующих записях таблицы BooksAuthors. Все это VFP, пока действует правило, будет обеспечивать автоматически без участия пользователя.

Для прочих таблиц указано правило обновление "Игнорировать". В общем случае применительно к таблице Books оно означает, что при изменении поля BookId в этой таблице поле BookId соответствующих записей таблиц BooksAuthors и BooksContent останутся неизмененным. Или, иными словами, нарушается ссылочная целость данных. Однако на практике такая ситуация невозможна, поскольку поле BookId имеет тип Integer (AutoInc), то есть недоступно для редактирования. То же справедливо и для поля TypeId, поэтому в связке WorkType – BooksContent также задано правило обновления "Игнорировать".

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

Правило вставки "Игнорировать" распространяется на все таблицы. Оно разрешает добавлять в дочернюю таблицу запись, не имеющую соответствующего индексного значения в родительской. Так, теоретически в таблицу BooksContent можно добавить запись, в которой значение поля TypeId не совпадает ни с одним значением одноименного поля в родительской таблице WorkType. Или, другими словами, это поле адресует несуществующий вид произведения. На практике, однако, этого не произойдет, поскольку пользователь будет определять значение поля TypeId таблицы BooksContent, выбирая его из списка имеющихся значений. Кроме того, сохранение записи таблицы BooksContent с неопределенным значением поля TypeId будет запрещено.

По аналогичным причинам правило вставки "Игнорировать" установлено и на другие таблицы.

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

Ответственность за соблюдение введенных правил возлагается на процедуры, генерируемые VFP и записываемые в хранимые процедуры базы данных.

Открыв эти процедуры (команда меню Database – Edit Stored Procedures), обнаружим среди них в частности процедуры __RI_DELETE_authors( ) и __RI_UPDATE_authors( ), относящиеся к таблице Authors и реализующие заданные правила удаления и обновления.

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

Связать таблицы в Foxpro очень просто, но такая возможность должна быть заранее предусмотрена. Делается это так:

В первой таблице создается служебное поле, в которое заносится некое абстрактное значение, чаще всего номер. Главное требование к нему - он должен быть абсолютно уникален. Поэтому Фокс не имеет таких средств - не хотят разработчики брать на себя столь высокую ответственность. Разработать алгоритм, гарантирующий уникальность кода - дело программиста. В простейшем случае это обычный счетчик - ищем максимальный номер, увеличиваем его на единицу, и радуемся. Если с программой работают две девушки, печатающие одним пальцем. А если в системе триста тысяч абонентов, система сбойнет через минуту. И миллионов сто-двести уйдут не на тот счет. Что тогда сделают с умненьким программистом?

Со второй таблицей проще. Там есть аналогичное поле, которое содержит тот же код (в простейшем случае - номер), взятый из первой таблицы. Там уникальности никакой нет - если человек предъявил три документа, будет три записи с этим кодом. Главное, что требуется - четко связать одну запись из первой таблицы с этими самыми тремя - из второй. Такая связь называется один-ко-многим.

Для такой цели нужен именно абстрактный номер, или код. Фамилия+Имя+Отчество не годится - во-первых длинно, а во-вторых девушки любят менять фамилию. Номер паспорта тоже не годится - его меняют еще чаще. ИНН неплох, но его можно узнать далеко не всегда. Прикиньте - вы пришли в библиотеку, а с вас ИНН спрашивают! Так не бывает.

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

Однако, радуйтесь!

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

Ничто не мешает создать и поля с разными названиями, но мне нравятся одинаковые. Поскольку таблицы разные, это вполне допустимо (и ведь только что на одинаковые имена я сам же и ругался!).

Сугубо факультативно (на данный момент)

Для связывания таблиц в Foxpro служит команда set rela, или конструктор среды данных. Сначала устанавливается связь одна-к-одной, а следующей командой - "одна-ко-многим". Есть несколько вариантов установления связей, но все они тормозят компьютер жутко. На вашей тестовой базе это может быть незаметно, а на реальной базе клиент вас проклянет. Если кому интересно, загляните на статмед.ру, там я сравниваю две программы "Поликлиника" фирм "Грит" и "АММ". Обе, оракловские, и делают одно и то же. Но работают по разному, очень по разному. "Грит" по функционалу в данный момент на полкорпуса впереди, но выборку их программа делает - состаришься, пока ждешь. Так и не решил, кто же лучше.

Теперь проиндексируем таблицы

Есть в Foxpro такая вещь - командное окно. С его помощью можно сделать абсолютно все, зачастую быстрее и лучше, чем другими способами. К сожалению, получается не так наглядно, да и многое приходится помнить наизусть. Вот к его помощи и придется сейчас прибегнуть.

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

Объяснения получаются длинными, но фактически связь устанавливается двумя командами - set order и set relation. Но даже с учетом открытия таблиц задача решается в пять строчек, которые и показаны не скриншоте.

Ай-ай какая неприятность! Подсветка кода глючит. Имя папки "tables" должно быть черного цвета, как и имя файла. Но оно, увы, совпадает с одним из ключевых слов, а редактор простенький, вот и ошибается. Таких ситуаций лучше избегать, хотя к ошибкам они и не приводят

* открываем таблицы, с индексами, практически вручную
USE tables\fio
USE tables\docum INDEX tables\docum IN 2

* выбираем во второй таблице индекс, по которому устанавливается данная связь
SET ORDER TO TAG pokodu IN 2

* устанавливает связь
SET RELATION TO kod INTO 2

* формируем составную таблицу, часть полей в которой из первой, а часть из второй таблицы
BROWSE FIELDS a.famil,a.name,b.naimen,b.ser_doc

Тут нужны некоторые пояснения:

Состояние базы данных, и даже ее наличие, не имеют значения. Работа ведется только с таблицами.
Таблица FIO открывается в первой рабочей области, поскольку иное не указано. Можно было указать IN 1, но это лишнее.
При открытии таблицы указывается папка, в которой она расположена, поскольку без проекта и базы Фокс ее не найдет
Наличие индекса для первой таблицы не имеет значения. Он влияет только на порядок строк, и для связи не используется.

Для второй таблице IN 2 означает, что она будет открыта в другой рабочей области (второй), чтобы при ее открытии не закрылась первая
И для таблицы, и для индексного файла должна указываться папка, где они расположены
Наличие индекса по ключевому полю для второй таблицы обязательно.
Команда SET RELATION выбирает в индексном файле нужный для связи индекс
Вместо INTO 2 можно было указать имя таблицы - docum, но в примере использован номер рабочей области, где она открыта

Имена полей в составной таблице можно использовать в любом порядке, просто перед полями первой таблицы нужно указывать префикс a., а для полей второй таблицы - префикс b.

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

Читайте также: