Простейший пример иерархических рекурсивных данных
Реляционная модель хорошо работает для базовых/подчиненных записей в пределах одной таблицы, если в ней существует лишь один уровень принадлежности — другими словами, если каждая запись либо принадлежит другой записи, либо владеет другой записью. В табл. 13.1 приведен список персонала (состоящий из начальников и подчиненных), который при одном уровне принадлежности можно было бы разделить на две таблицы.
Таблица 13.1. Простейший пример рекурсивных иерархических данных
Emp_ID Boss_ID Emp_Name Boss 1 <nil> Frank Eng Boss 2 <nil> Sharon Oakstein Boss 3 <nil> Charles Willings Staff 1 Boss 1 Roger Otkin Staff 2 Boss 1 Marylin Fionne Staff 3 Boss 1 Judy Czeglarek Staff 4 Boss 2 Sean O'Donhail Staff 5 Boss 3 Karol Klauss Staff 6 Boss 3 James Riordan
В табл. 13.2 перечислены все значения свойств для двух наборов компонентов TTable, TDataSource и TDBGrid, связанных с одной и той же физической таблицей. Первый набор свойств предназначен для вывода родительских записей, а второй — для вывода дочерних записей, принадлежащих текущему выбранному родителю. Свойства MasterSource и MasterFields подчиненного компонента TTable автоматически ограничивают его набором записей, подчиненных текущей записи родительской таблицы.
Таблица 13.2. Значения свойств для отображения записей-родителей и записей-детей
Свойства компонентов для родительских записейTable1.TableName = 'employees'
Table1.IndexFieldName = 'Boss_ID;Emp_ID' Table1.SetRange([''],['']); DataSource1.DataSet = 'Table1' DBGrid1.DataSource = 'DataSource1' |
Свойства компонентов для дочерних записейTable2.TableName = 'employees'
Table2.IndexFieldName = 'Boss_ID;Emp_ID' Table2.MasterSource = 'DataSource1' Table2.MasterFields = 'Emp_ID' DataSource2.DataSet = 'Table2' DBGrid2.DataSource = 'DataSource2' |
Чтобы ограничить родительский компонент TTable и не выводить в нем дочерние записи, задайте условие-фильтр, пропускающий лишь записи с пустым полем Boss_ID (это и есть родительские записи).
Замечание
Вместо свойства Filter можно использовать метод SetRange. С помощью этого метода мы заставим Table1 выводить только записи о начальниках (то есть записи с Boss_ID = nil). Вызов Table1.SetRange можно включить в обработчик Table1.AfterOpen, чтобы метод гарантированно вызывался независимо от того, оставлена ли таблица открытой в режиме конструирования или она открывается во время выполнения.
На Рисунок 13.2 изображена форма Delphi с двумя компонентами TDBGrid, свойства которых настроены в соответствии с табл. 13.2. Слева перечислены записи о начальниках (родительские записи), справа — записи о подчиненных (дочерние записи). Все эти записи взяты из одной физической таблицы.
При каждом изменении DataSource1 (связанного с Table1) происходит автоматическое обновление Table2, как будто выполняется код из листинга 13.1.