рекурсия в SQL запросе
ID - ключ
ParentIndex - индекс родительского Node
ItemLevel = TTreeNode::Level
Permission - некое число означающее некие права на редактирование ветки TreeView (включая подветки).
Каким образом можно составить SQL запрос (скорее всего рекурсивный) что бы он "работал" пока (Permission == "заданное значение" или ItemIndex !=0) ?
Т.е. выбираем записи пока удовлетворяется условие права на редактирование или до тех пор пока Level у Node не достигнет 0.
т.е. select * from table where permission = ... and ItemIndex != 0. Выберет тебе все записи у которых ItemIndex !=0 и permsiion = ...
а при рекурсии (скорей всего это будет что то типа процедуры с параметром ParentIndex) выбирается только одна ветка, сожержащая выделенный Node.
Т.е. мне надо запретить редактирование Нода, при условии что у родительского (пра-пра... пра-родительского) Нода (заранее не известно в каком уровне вложенности, их может быть очень много) стоит запрет на редактирование.
ЗЫ: Привязываюсь рекурсии потому что составление самого TreeView идет через рекурсию
Тогда у тебя выберутся все элементы у которых пермиссион такой-то, итеминдекс не равен 0 и парентиндекс = ноду.. Так же тебе надо? выбрать все дочернии записи, относительного какого-то нода..
нужна ХП на сервере которая выдаст да/нет на редактирование средствами SQL
нужна ХП на сервере которая выдаст да/нет на редактирование средствами SQL
Рекурсия в SQL языке как таковая отсутствует. Можно организовать рекурсивный вызов в ХП через курсор.
Но правда не понятно - зачем рекурсия здесь. Права доступа должны быть описаны у каждого дочернего раздела, на основании прав родительского. ИМХО.
это получится "беготня" по БД средствами ВСВ (или я не правильно понял?)
не всякий раз, и хотелось бы иметь более гибкую систему распределения прав, нежели полное наследование.
ошибаетесь :)
оператор WITH
не правильно понял. Курсор создается на стороне сервера в ХП. Выглядит это примерно так:
@id
as
declare csLoadPricePart cursor for
select * from ....
open csLoadPricePart
fetch next from csLoadPricePart into
@priceid,@priceitem,@goodid,@price,@pricetypeid,@ispossible,@currex,@price_over,@discount
while @@fetch_status = 0
begin
insert into priceitems(priceid,goodid,price,pricetypeid,ispossible,currex,price_over,discount)
values (@priceid,@goodid,@price,@pricetypeid,@ispossible,@currex,@price_over,@discount)
delete FROM tmpPrice where priceitem = @priceitem
fetch next from csLoadPricePart into
@priceid,@priceitem,@goodid,@price,@pricetypeid,@ispossible,@currex,@price_over,@discount
end
close csLoadPricePart
и т.д. Т.е. ты вначале формируешь набор данных - потом в цикле по нему проходишь и обрабатываешь так как тебе надо. Можно рекурсивно вызывать ХП. Пример приводить не буду - задай поиск деревья в БД - найдешь инфу.
не всякий раз, и хотелось бы иметь более гибкую систему распределения прав, нежели полное наследование.
Что мешает делать гибкую систему? Не очень понятно. Базируясь на правах доступа к родителю - вовсе не значит - копировать права один в один. Все зависит от логики программы. Кроме того - могу сказать однозначно - что вариант с получением прав доступа к каждому объекту - однозначно более гибок и прост :) - чем искать по родителям и потом уже определять - можно/нельзя
Есть 3 таблицы:
1. base: IDKey(int), ParentIndex(int), ItemLevel(int), ItemName(varchar)
2. Users: Id (int), User_Name(varchar)
3. Permissions: Id(int), Id_User(int), Id_base(int)
таблица base представляет собой "плоскую" стркутуру дерева с узлами (ветками) и подветками, т.е. ParentIndex в подчиненной ветке указывает на IDKey родительской, таким образом нельзя однозначно сказать для какой "низшей" записи будет самый первый родитель, т.е. сначала надо пробежаться по всем родителям вверх.
Это т.с. вводная.
Непосредственно сама задача:
На какой то узел назначется User и заполняется таблица Permissions: Id_User = User.Id, Id_base = base.IDKey
этот юзер должен иметь те же самые права и на подчиненные записи, но не должен иметь права на родительские.
Таким образом надо составить некий рекурсивный (т.к. заранее не известно кол-во вложенностей) запрос по таблице base с условиями останова:
1. если достигнут нулевой (самый верхний)уровень
или
2. если будет найден base.IDKey = Permissions.Id_User (т.е. если будет найдены соответсвующие права у нужного юзера (из Permissions)
ЗЫ: про рекурсию много читал, только никак не врублюсь как ее заточит под данный случай
ЗЫЗЫ: СУБД MSSQL 2000/2005
ЗЫЗЫЗЫ: желательно это оформить в виде ХП на сервере
Результат поиска в слепую.
Например тыць
третье сообщение сверху.
Если следующий вопрос будет "как мне пределать" - уж не обессудьте.
Репутацию снижаю за хроническое нежелание делать что либо самому.
буду работать "над собой"
буду работать "над собой"
Да не за что. Всегда пожалуйста. :)
Просто для того что бы получать нормальные и осмысленные ответы - старайтесь прежде чем спросить - думать и делать самостоятельно - и вам пользы больше (ведь только вы видете свою задачу) и отвечать на вопрос гораздо интересней. Кроме того, гораздо проще взять ваш код и найти в нем ошибку - чем писать специально процедуру - непонятно зачем тратя свое время.
Кстати, как правило, права доступа к объектам проще всего определять на основе битовой маски - потому как определение прав доступа к детям на основе прав доступа к родителям, да еще и с произвольным уровнем вложенности - это очень затратная процедура на самом деле. Но я думаю вы еще с этим столкнетесь. :)
AS BEGIN
WITH tree (ID_Key, Parent_Index, Item_Level, Item_Name)
AS (SELECT IDKey, ParentIndex, ItemLevel, ItemName
FROM base
WHERE IDKey = @ID
UNION ALL
SELECT IDKey, ParentIndex, ItemLevel, ItemName
FROM base AS Base
INNER JOIN tree AS tr
ON tr.Parent_Index = Base.IDKey)
SELECT *
FROM tree
END
а как ее тормознуть в определенный момент, допустим если Base.IDKey = 3 ?
AS BEGIN
WITH tree (ID_Key, Parent_Index, Item_Level, Item_Name)
AS (SELECT IDKey, ParentIndex, ItemLevel, ItemName
FROM base
WHERE IDKey = @ID
UNION ALL
SELECT IDKey, ParentIndex, ItemLevel, ItemName
FROM base AS Base
INNER JOIN tree AS tr
ON tr.Parent_Index = Base.IDKey)
SELECT *
FROM tree
END
а как ее тормознуть в определенный момент, допустим если Base.IDKey = 3 ?
буквально пару сообщениями выше я приводил пример как использовать курсор. Все что вам необходимо сделать - адаптировать его под свой код. Т.е. расширить условие завершения цикла
в курсор соответственно загрузить свои данные.
определение права доступа для конкретной строки таблицы и конкретного юзера:
AS BEGIN
WITH tree(ID_Key, Parent_Index, Item_Level, Item_Name)
AS (SELECT IDKey, ParentIndex, ItemLevel, ItemName
FROM base
WHERE IDKey = @ID
UNION ALL
SELECT Base.IDKey, Base.ParentIndex, Base.ItemLevel, Base.ItemName
FROM base AS Base INNER JOIN
tree AS tr ON tr.Parent_Index = Base.IDKey)
SELECT tree.Parent_Index, tree.Item_Level, tree.Item_Name, Users.Id, PermissonType.Id , PermissonType.PermissionType
FROM Users INNER JOIN
Permissions ON Users.Id = Permissions.Id_User INNER JOIN
tree AS tree ON Permissions.Id_Base = tree.ID_Key INNER JOIN
PermissonType ON Permissions.Id_PermissionType = PermissonType.Id
WHERE Users.Id = @ID_User
END
паралельно вопрос возник:
в таблице Permissions для конкретного юзера на конкретную запись не должно быть больше 1-го права, дркгими словами нельзя дать на запись права сразу на чтение и его же запретить.
Таким образом в поля Id_base и Id_User нельзя дважды вставить одинаковые значения, т.е.:
Id_base | Id_User
1 | 2
2 | 1
1 | 2 --- ошибка!!! такая запись уже существует!
вопрос, как на стороне сервера (СУБД) запретить это и как ошибку обработать?
Пока ничего лучше не придумал:
INSERT INTO Permissions (Id_User, Id_Base, Id_PermissionType) values (:User, :Base, :PermissionType)
может есть какое более "красивое" решение?
определение права доступа для конкретной строки таблицы и конкретного юзера:
AS BEGIN
WITH tree(ID_Key, Parent_Index, Item_Level, Item_Name)
AS (SELECT IDKey, ParentIndex, ItemLevel, ItemName
FROM base
WHERE IDKey = @ID
UNION ALL
SELECT Base.IDKey, Base.ParentIndex, Base.ItemLevel, Base.ItemName
FROM base AS Base INNER JOIN
tree AS tr ON tr.Parent_Index = Base.IDKey)
SELECT tree.Parent_Index, tree.Item_Level, tree.Item_Name, Users.Id, PermissonType.Id , PermissonType.PermissionType
FROM Users INNER JOIN
Permissions ON Users.Id = Permissions.Id_User INNER JOIN
tree AS tree ON Permissions.Id_Base = tree.ID_Key INNER JOIN
PermissonType ON Permissions.Id_PermissionType = PermissonType.Id
WHERE Users.Id = @ID_User
END
паралельно вопрос возник:
в таблице Permissions для конкретного юзера на конкретную запись не должно быть больше 1-го права, дркгими словами нельзя дать на запись права сразу на чтение и его же запретить.
Таким образом в поля Id_base и Id_User нельзя дважды вставить одинаковые значения, т.е.:
Id_base | Id_User
1 | 2
2 | 1
1 | 2 --- ошибка!!! такая запись уже существует!
вопрос, как на стороне сервера (СУБД) запретить это и как ошибку обработать?
Пока ничего лучше не придумал:
INSERT INTO Permissions (Id_User, Id_Base, Id_PermissionType) values (:User, :Base, :PermissionType)
может есть какое более "красивое" решение?
Использовать составной уникальный индекс.
как в таком случае исключения обработать, если "вдруг" индекс не уникален будет?
...
}
catch(EOleException &exception){
...
}