... sql. (прим. правда интересно и правда надо. давно оставлял "на потом")
положим есть в некая сущноть
CREATE TABLE object
(
id int NOT NULL ,
idclass int NOT NULL ,
...
...
)
Сущность определенного класса class... для чего у сущности object есть ссылка object.idclass на класс class, который суть
CREATE TABLE class
(
id int NOT NULL ,
idparentclass int NOT NULL ,
...
...
)
Здесь класс class содержит ссылку class.idparentclass на свой родительский класс class . Терминатором "рода" можно выбрать хоть 0. Не так важно сейчас.
Можно всё это хозяйство себе представить, что как будто, object - это файл дисковой системы, а цепочка родительский/дочерние class-ы, это как бы директории полного пути файла.
Нужно в одном запросе получить набор, состоящий из всех предков класса.
Спасибо.
Прим. Делаю давно и весьма успешно при помоши дополнительной таблицы с избыточной информацией о предках класса, типа:
CREATE TABLE classBackLookup
(
id int NOT NULL ,
idclass int NOT NULL ,
idparentclass int NOT NULL ,
)
Недостаток .... всем понятен. Хотя работает.
Вопрос про запрос
Правила форума
Пожалуйста, ознакомьтесь с правилами данного форума
Пожалуйста, ознакомьтесь с правилами данного форума
-
- Маньяк
- Сообщения: 2803
- Зарегистрирован: 29 май 2003, 22:29
- Откуда: Магадан - Миссиссага
-
- Пользователь
- Сообщения: 107
- Зарегистрирован: 02 авг 2004, 22:00
sql
Не очень понятно, хотя в своих приложениях я использую следуюшии алгоритм, Может это не то что надо тебе , надеюсь хоть даст идею как двигаться дальше
IF object_id('dbo.Employees') IS NOT NULL
DROP TABLE Employees
GO
IF object_id('dbo.ufn_GetSubtree') IS NOT NULL
DROP FUNCTION dbo.ufn_GetSubtree
GO
CREATE TABLE Employees
(
empid int NOT NULL,
mgrid int NULL,
empname varchar(25) NOT NULL,
salary money NOT NULL,
CONSTRAINT PK_Employees_empid PRIMARY KEY(empid),
CONSTRAINT FK_Employees_mgrid_empid
FOREIGN KEY(mgrid)
REFERENCES Employees(empid)
)
CREATE INDEX idx_nci_mgrid ON Employees(mgrid)
INSERT INTO Employees VALUES(1 , NULL, 'Nancy' , $10000.00)
INSERT INTO Employees VALUES(2 , 1 , 'Andrew' , $5000.00)
INSERT INTO Employees VALUES(3 , 1 , 'Janet' , $5000.00)
INSERT INTO Employees VALUES(4 , 1 , 'Margaret', $5000.00)
INSERT INTO Employees VALUES(5 , 2 , 'Steven' , $2500.00)
INSERT INTO Employees VALUES(6 , 2 , 'Michael' , $2500.00)
INSERT INTO Employees VALUES(7 , 3 , 'Robert' , $2500.00)
INSERT INTO Employees VALUES(8 , 3 , 'Laura' , $2500.00)
INSERT INTO Employees VALUES(9 , 3 , 'Ann' , $2500.00)
INSERT INTO Employees VALUES(10, 4 , 'Ina' , $2500.00)
INSERT INTO Employees VALUES(11, 7 , 'David' , $2000.00)
INSERT INTO Employees VALUES(12, 7 , 'Ron' , $2000.00)
INSERT INTO Employees VALUES(13, 7 , 'Dan' , $2000.00)
INSERT INTO Employees VALUES(14, 11 , 'James' , $1500.00)
GO
CREATE FUNCTION dbo.ufn_GetSubtree
(
@mgrid AS int
)
RETURNS @tree table
(
empid int NOT NULL,
mgrid int NULL,
empname varchar(25) NOT NULL,
salary money NOT NULL,
lvl int NOT NULL,
path varchar(900) NOT NULL
)
AS
BEGIN
DECLARE @lvl AS int, @path AS varchar(900)
SELECT @lvl = 0, @path = '.'
INSERT INTO @tree
SELECT empid, mgrid, empname, salary,
@lvl, '.' + CAST(empid AS varchar(10)) + '.'
FROM Employees
WHERE empid = @mgrid
WHILE @@ROWCOUNT > 0
BEGIN
SET @lvl = @lvl + 1
INSERT INTO @tree
SELECT E.empid, E.mgrid, E.empname, E.salary,
@lvl, T.path + CAST(E.empid AS varchar(10)) + '.'
FROM Employees AS E JOIN @tree AS T
ON E.mgrid = T.empid AND T.lvl = @lvl - 1
END
RETURN
END
GO
SELECT empid, mgrid, empname, salary
FROM ufn_GetSubtree(3)
GO
/*
empid mgrid empname salary
2 1 Andrew 5000.0000
5 2 Steven 2500.0000
6 2 Michael 2500.0000
*/
/*
SELECT REPLICATE (' | ', lvl) + empname AS employee
FROM ufn_GetSubtree(1)
ORDER BY path
*/
/*
employee
-----------------
Nancy
| Andrew
| | Steven
| | Michael
| Janet
| | Robert
| | | David
| | | | James
| | | Ron
| | | Dan
| | Laura
| | Ann
| Margaret
| | Ina
*/
IF object_id('dbo.Employees') IS NOT NULL
DROP TABLE Employees
GO
IF object_id('dbo.ufn_GetSubtree') IS NOT NULL
DROP FUNCTION dbo.ufn_GetSubtree
GO
CREATE TABLE Employees
(
empid int NOT NULL,
mgrid int NULL,
empname varchar(25) NOT NULL,
salary money NOT NULL,
CONSTRAINT PK_Employees_empid PRIMARY KEY(empid),
CONSTRAINT FK_Employees_mgrid_empid
FOREIGN KEY(mgrid)
REFERENCES Employees(empid)
)
CREATE INDEX idx_nci_mgrid ON Employees(mgrid)
INSERT INTO Employees VALUES(1 , NULL, 'Nancy' , $10000.00)
INSERT INTO Employees VALUES(2 , 1 , 'Andrew' , $5000.00)
INSERT INTO Employees VALUES(3 , 1 , 'Janet' , $5000.00)
INSERT INTO Employees VALUES(4 , 1 , 'Margaret', $5000.00)
INSERT INTO Employees VALUES(5 , 2 , 'Steven' , $2500.00)
INSERT INTO Employees VALUES(6 , 2 , 'Michael' , $2500.00)
INSERT INTO Employees VALUES(7 , 3 , 'Robert' , $2500.00)
INSERT INTO Employees VALUES(8 , 3 , 'Laura' , $2500.00)
INSERT INTO Employees VALUES(9 , 3 , 'Ann' , $2500.00)
INSERT INTO Employees VALUES(10, 4 , 'Ina' , $2500.00)
INSERT INTO Employees VALUES(11, 7 , 'David' , $2000.00)
INSERT INTO Employees VALUES(12, 7 , 'Ron' , $2000.00)
INSERT INTO Employees VALUES(13, 7 , 'Dan' , $2000.00)
INSERT INTO Employees VALUES(14, 11 , 'James' , $1500.00)
GO
CREATE FUNCTION dbo.ufn_GetSubtree
(
@mgrid AS int
)
RETURNS @tree table
(
empid int NOT NULL,
mgrid int NULL,
empname varchar(25) NOT NULL,
salary money NOT NULL,
lvl int NOT NULL,
path varchar(900) NOT NULL
)
AS
BEGIN
DECLARE @lvl AS int, @path AS varchar(900)
SELECT @lvl = 0, @path = '.'
INSERT INTO @tree
SELECT empid, mgrid, empname, salary,
@lvl, '.' + CAST(empid AS varchar(10)) + '.'
FROM Employees
WHERE empid = @mgrid
WHILE @@ROWCOUNT > 0
BEGIN
SET @lvl = @lvl + 1
INSERT INTO @tree
SELECT E.empid, E.mgrid, E.empname, E.salary,
@lvl, T.path + CAST(E.empid AS varchar(10)) + '.'
FROM Employees AS E JOIN @tree AS T
ON E.mgrid = T.empid AND T.lvl = @lvl - 1
END
RETURN
END
GO
SELECT empid, mgrid, empname, salary
FROM ufn_GetSubtree(3)
GO
/*
empid mgrid empname salary
2 1 Andrew 5000.0000
5 2 Steven 2500.0000
6 2 Michael 2500.0000
*/
/*
SELECT REPLICATE (' | ', lvl) + empname AS employee
FROM ufn_GetSubtree(1)
ORDER BY path
*/
/*
employee
-----------------
Nancy
| Andrew
| | Steven
| | Michael
| Janet
| | Robert
| | | David
| | | | James
| | | Ron
| | | Dan
| | Laura
| | Ann
| Margaret
| | Ina
*/
-
- Маньяк
- Сообщения: 2803
- Зарегистрирован: 29 май 2003, 22:29
- Откуда: Магадан - Миссиссага
2Yuri Dimant,
Спасибо за совет. И, кстати, за пример. Такие мысли посещали. Конечно, функцией можно сделать. Можно просто пройтись тогда всем родственникам вверх в цикле (мы ведь имеем односвязанный писок, если опустить те связи, что я постил в "избыточной" таблице).
Укладывать на сервере функцию, конечно, хорошо. Но мне не хотелось бы, посткольку то, что сделано работает и для сервера и для отдельно стоящей (локально) базы, например dBase. В таком случа, проще программно, прямо в клиентском приложении "пересчитать" предков в цикла с sql запросами по полю idparentclass. Тем более что число из не велико (уровень вложенность не прешает в практических случаях 3-6).
Спасибо.
Спасибо за совет. И, кстати, за пример. Такие мысли посещали. Конечно, функцией можно сделать. Можно просто пройтись тогда всем родственникам вверх в цикле (мы ведь имеем односвязанный писок, если опустить те связи, что я постил в "избыточной" таблице).
Укладывать на сервере функцию, конечно, хорошо. Но мне не хотелось бы, посткольку то, что сделано работает и для сервера и для отдельно стоящей (локально) базы, например dBase. В таком случа, проще программно, прямо в клиентском приложении "пересчитать" предков в цикла с sql запросами по полю idparentclass. Тем более что число из не велико (уровень вложенность не прешает в практических случаях 3-6).
Спасибо.
- Malysh'OK
- Завсегдатай
- Сообщения: 305
- Зарегистрирован: 03 авг 2005, 18:41
- Откуда: Москва-Ванкувер
В оракле специально для такого типа задач есть "деревянный" запрос : select * from class start with id = :id connect by prior idparentclass = id. А если нужно универсальное решение для всех типов баз данных, тогда действительно лучше на клиенте все собрать...vg писал(а):Но мне не хотелось бы, посткольку то, что сделано работает и для сервера и для отдельно стоящей (локально) базы, например dBase. В таком случа, проще программно, прямо в клиентском приложении "пересчитать" предков в цикла с sql запросами по полю idparentclass. Тем более что число из не велико (уровень вложенность не прешает в практических случаях 3-6).
Спасибо.
- Ильгиз
- Пользователь
- Сообщения: 55
- Зарегистрирован: 13 сен 2005, 12:00
- Откуда: Уфа - Китченер
Если задача исходит из идеи объектно-ориентированного программирования, выбрать нужно так называемые под-объекты (sub-objects), составляющие экземпляр объекта.
В любом случае, хотелось бы уточнить, имеется ли в виду в выражении "предки объекта" _описания_ родительских классов или под-объекты.
Я слышал, что многие базы данных (по крайней мере, свободная PostgreSQL) позволяют отождествлять понятие таблицы и класса. Одна таблица может быть объявлена наследником другой, и тогда она будет неявно содержать все поля предка плюс поля, объявленные в её описании.
Замечательное преимущество таких баз в том, что каждой записи в таблице наследника автоматически соответствует запись в таблице предка. Тогда для поиска по полям, общим для всех наследников класса достаточно сделать поиск по таблице предка. Найденные строки потом можно уточнить к типу наследника по значению поля "CLASS_ID", которое идентифицирует класс объекта.
К примеру, можно объявить базовый класс FileSysEntry с полем Name и классы-наследники File, Directory, NamedSocket etc. В некоторый момент пользователь хочет сделать поиск по всем экземплярам наследников. В обычной базе данных для этого пришлось бы повторять поиск по каждой таблице. В объектно-ориентированной базе достаточно выполнить запрос по таблице FileSysEntry. Благодаря полю CLASS_ID, мы сможем узнать, к какому конкретному классу относятся найденные объекты.
Другой вопрос, если вы хотите выяснить иерархию классов по найденному объекту. Здесь, видимо, понадобилась бы либо некая существующая системная таблица, либо дополнительная мета-таблица, где были бы сопоставлены значения CLASS_ID, имя таблицы и идентификатор родительского класса (или множество индентификаторов таких классов).
В любом случае, хотелось бы уточнить, имеется ли в виду в выражении "предки объекта" _описания_ родительских классов или под-объекты.
Я слышал, что многие базы данных (по крайней мере, свободная PostgreSQL) позволяют отождествлять понятие таблицы и класса. Одна таблица может быть объявлена наследником другой, и тогда она будет неявно содержать все поля предка плюс поля, объявленные в её описании.
Замечательное преимущество таких баз в том, что каждой записи в таблице наследника автоматически соответствует запись в таблице предка. Тогда для поиска по полям, общим для всех наследников класса достаточно сделать поиск по таблице предка. Найденные строки потом можно уточнить к типу наследника по значению поля "CLASS_ID", которое идентифицирует класс объекта.
К примеру, можно объявить базовый класс FileSysEntry с полем Name и классы-наследники File, Directory, NamedSocket etc. В некоторый момент пользователь хочет сделать поиск по всем экземплярам наследников. В обычной базе данных для этого пришлось бы повторять поиск по каждой таблице. В объектно-ориентированной базе достаточно выполнить запрос по таблице FileSysEntry. Благодаря полю CLASS_ID, мы сможем узнать, к какому конкретному классу относятся найденные объекты.
Другой вопрос, если вы хотите выяснить иерархию классов по найденному объекту. Здесь, видимо, понадобилась бы либо некая существующая системная таблица, либо дополнительная мета-таблица, где были бы сопоставлены значения CLASS_ID, имя таблицы и идентификатор родительского класса (или множество индентификаторов таких классов).
-
- Маньяк
- Сообщения: 2803
- Зарегистрирован: 29 май 2003, 22:29
- Откуда: Магадан - Миссиссага
>>Если задача исходит из идеи объектно-ориентированного программирования, ...
Именно. Та идея (вернее уже имплементрованная объектная метабаза), была реализована для МагаданГеологии 9 лет назад. Тогда PostgreSQL был?
(шутю)
>>Я слышал, что многие базы данных (по крайней мере, свободная PostgreSQL) позволяют отождествлять понятие таблицы и класса. Одна таблица может быть объявлена наследником другой, и тогда она будет неявно содержать все поля предка плюс поля, объявленные в её описании.
Именно это и сделано. Причём идёт речь имеенно о неявном включении данных родительской таблицы (по ссылке) в результирующий набор данных. Это ключевой момент, поскольку другая ветка разраработчиков около-OODB используют явное включение, что не есть всегда хорошо (например, так сделаны geotabase ESRI)
>>Замечательное преимущество таких баз в том, что каждой записи в таблице наследника автоматически соответствует запись в таблице предка. Тогда для поиска ...
Не только... Не знаю, как уж сделано в PostgreSQL, но у нас поддерживалось и включение методов тоже (а не только данных) для обработки "родовых данных". Был добавлен полиморфизм методов. В конце ещё навернул прочую, врпочем не нужную, фигню как абстрактные классы, множественное наследование и т.д.
Всё это хоз-во хранилось исключительно в таблицах. Ты будешь смеяться, но эта OODB надстройка имплементирована так, что её можно было расположить как на сервере, так и, например, в dBase
>>К примеру, можно объявить базовый класс FileSysEntry с полем Name и классы-наследники File, Directory, NamedSocket etc. В некоторый момент пользователь хочет сделать поиск по всем экземплярам наследников.
Эта... У нас к примеру можно было для родительских данных ещё и повторно использовать методы
Ну ... с поиском данных ... таки да, конечно. В этом и крутость. Кроме того можно было выбирать, например, все объекты класса, подкласса и т.д.
>>Другой вопрос, если вы хотите выяснить иерархию классов по найденному объекту. Здесь, видимо, понадобилась бы либо некая существующая системная таблица, либо дополнительная мета-таблица, где были бы сопоставлены значения CLASS_ID, имя таблицы и идентификатор родительского класса (или множество индентификаторов таких классов).
В этом то и был поинт моего вопроса. Кстати, чтобы выстроить иерархию (если не критично по скорости выполнения) то можно и программно. Я же делал при помощи, как ты называешь дополнительных системных таблиц. Кстати, структура такой таблицы в минимальной реализации - очень проста, см.выше classBackLookup.
ПС. На теперешней работе используюю только ссылки на классы, типа как фолдеры. Весьма удобно для пользователей, когда они видят базу, содержащуюю динамически поддерживаемы фолдеры. Их юзер сам создаёт как-бы для иерархического подразделения инфы.
Короче, основные идей пока не используются., и думаю не надо это здесь.
Именно. Та идея (вернее уже имплементрованная объектная метабаза), была реализована для МагаданГеологии 9 лет назад. Тогда PostgreSQL был?
(шутю)
>>Я слышал, что многие базы данных (по крайней мере, свободная PostgreSQL) позволяют отождествлять понятие таблицы и класса. Одна таблица может быть объявлена наследником другой, и тогда она будет неявно содержать все поля предка плюс поля, объявленные в её описании.
Именно это и сделано. Причём идёт речь имеенно о неявном включении данных родительской таблицы (по ссылке) в результирующий набор данных. Это ключевой момент, поскольку другая ветка разраработчиков около-OODB используют явное включение, что не есть всегда хорошо (например, так сделаны geotabase ESRI)
>>Замечательное преимущество таких баз в том, что каждой записи в таблице наследника автоматически соответствует запись в таблице предка. Тогда для поиска ...
Не только... Не знаю, как уж сделано в PostgreSQL, но у нас поддерживалось и включение методов тоже (а не только данных) для обработки "родовых данных". Был добавлен полиморфизм методов. В конце ещё навернул прочую, врпочем не нужную, фигню как абстрактные классы, множественное наследование и т.д.
Всё это хоз-во хранилось исключительно в таблицах. Ты будешь смеяться, но эта OODB надстройка имплементирована так, что её можно было расположить как на сервере, так и, например, в dBase

>>К примеру, можно объявить базовый класс FileSysEntry с полем Name и классы-наследники File, Directory, NamedSocket etc. В некоторый момент пользователь хочет сделать поиск по всем экземплярам наследников.
Эта... У нас к примеру можно было для родительских данных ещё и повторно использовать методы

>>Другой вопрос, если вы хотите выяснить иерархию классов по найденному объекту. Здесь, видимо, понадобилась бы либо некая существующая системная таблица, либо дополнительная мета-таблица, где были бы сопоставлены значения CLASS_ID, имя таблицы и идентификатор родительского класса (или множество индентификаторов таких классов).
В этом то и был поинт моего вопроса. Кстати, чтобы выстроить иерархию (если не критично по скорости выполнения) то можно и программно. Я же делал при помощи, как ты называешь дополнительных системных таблиц. Кстати, структура такой таблицы в минимальной реализации - очень проста, см.выше classBackLookup.
ПС. На теперешней работе используюю только ссылки на классы, типа как фолдеры. Весьма удобно для пользователей, когда они видят базу, содержащуюю динамически поддерживаемы фолдеры. Их юзер сам создаёт как-бы для иерархического подразделения инфы.
Короче, основные идей пока не используются., и думаю не надо это здесь.