Справочник функций

Ваш аккаунт

Войти через: 
Забыли пароль?
Регистрация
Информацию о новых материалах можно получать и без регистрации:

Почтовая рассылка

Подписчиков: -1
Последний выпуск: 19.06.2015

рекурсия в SQL запросе

11
03 декабря 2007 года
oxotnik333
2.9K / / 03.08.2007
Есть таблица из которой формируется TreeView с полями: ID(int), ParentIndex(int), ItemIndex(int), ItemName(varchar), Permission (int).
ID - ключ
ParentIndex - индекс родительского Node
ItemLevel = TTreeNode::Level
Permission - некое число означающее некие права на редактирование ветки TreeView (включая подветки).
Каким образом можно составить SQL запрос (скорее всего рекурсивный) что бы он "работал" пока (Permission == "заданное значение" или ItemIndex !=0) ?
Т.е. выбираем записи пока удовлетворяется условие права на редактирование или до тех пор пока Level у Node не достигнет 0.
489
03 декабря 2007 года
NeO_u
277 / / 11.10.2006
обычный селект вообще-то выбирает все записи в которых совпадают нужные тебе условия...зачем тебе к селекту еще и рекурсию лепить?
т.е. select * from table where permission = ... and ItemIndex != 0. Выберет тебе все записи у которых ItemIndex !=0 и permsiion = ...
11
03 декабря 2007 года
oxotnik333
2.9K / / 03.08.2007
рекурсия нужна что бы пробежаться только по одной ветке, т.к. если тупо делать селект то выдаст кучу не нужных мне записей совсем из других веток...
а при рекурсии (скорей всего это будет что то типа процедуры с параметром ParentIndex) выбирается только одна ветка, сожержащая выделенный Node.
Т.е. мне надо запретить редактирование Нода, при условии что у родительского (пра-пра... пра-родительского) Нода (заранее не известно в каком уровне вложенности, их может быть очень много) стоит запрет на редактирование.
ЗЫ: Привязываюсь рекурсии потому что составление самого TreeView идет через рекурсию
489
03 декабря 2007 года
NeO_u
277 / / 11.10.2006
Ну пробегись по всем у кого parentindex = нужнумо тебе ноду. добавь в селект еще одно условие, что у тебя парентиндекс = нужный нод.
Тогда у тебя выберутся все элементы у которых пермиссион такой-то, итеминдекс не равен 0 и парентиндекс = ноду.. Так же тебе надо? выбрать все дочернии записи, относительного какого-то нода..
11
03 декабря 2007 года
oxotnik333
2.9K / / 03.08.2007
у каждого нода свой парентиндекс, и бегать средствами билдера по ним долго будет, тем более через сервер работаю (тормоза будут)
нужна ХП на сервере которая выдаст да/нет на редактирование средствами SQL
1
03 декабря 2007 года
kot_
7.3K / / 20.01.2000
Цитата: oxotnik333
у каждого нода свой парентиндекс, и бегать средствами билдера по ним долго будет, тем более через сервер работаю (тормоза будут)
нужна ХП на сервере которая выдаст да/нет на редактирование средствами SQL


Рекурсия в SQL языке как таковая отсутствует. Можно организовать рекурсивный вызов в ХП через курсор.
Но правда не понятно - зачем рекурсия здесь. Права доступа должны быть описаны у каждого дочернего раздела, на основании прав родительского. ИМХО.

11
03 декабря 2007 года
oxotnik333
2.9K / / 03.08.2007
Цитата:
Можно организовать рекурсивный вызов в ХП через курсор


это получится "беготня" по БД средствами ВСВ (или я не правильно понял?)

Цитата:
Права доступа должны быть описаны у каждого дочернего раздела, на основании прав родительского.


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

Цитата:
Рекурсия в SQL языке как таковая отсутствует.


ошибаетесь :)
оператор WITH

1
03 декабря 2007 года
kot_
7.3K / / 20.01.2000
Цитата: oxotnik333
это получится "беготня" по БД средствами ВСВ (или я не правильно понял?)


не правильно понял. Курсор создается на стороне сервера в ХП. Выглядит это примерно так:

Код:
CREATE PROCEDURE sGetPermition
 @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

и т.д. Т.е. ты вначале формируешь набор данных - потом в цикле по нему проходишь и обрабатываешь так как тебе надо. Можно рекурсивно вызывать ХП. Пример приводить не буду - задай поиск деревья в БД - найдешь инфу.
Цитата: oxotnik333

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


Что мешает делать гибкую систему? Не очень понятно. Базируясь на правах доступа к родителю - вовсе не значит - копировать права один в один. Все зависит от логики программы. Кроме того - могу сказать однозначно - что вариант с получением прав доступа к каждому объекту - однозначно более гибок и прост :) - чем искать по родителям и потом уже определять - можно/нельзя

11
07 декабря 2007 года
oxotnik333
2.9K / / 03.08.2007
задача несколько изменилась:
Есть 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
ЗЫЗЫЗЫ: желательно это оформить в виде ХП на сервере
1
07 декабря 2007 года
kot_
7.3K / / 20.01.2000
Видимо так сильно нужно - что поиском пользоваться просто не охота.
Результат поиска в слепую.
Например тыць
третье сообщение сверху.
Если следующий вопрос будет "как мне пределать" - уж не обессудьте.
Репутацию снижаю за хроническое нежелание делать что либо самому.
11
07 декабря 2007 года
oxotnik333
2.9K / / 03.08.2007
Спасибо за отзыв :)
буду работать "над собой"
1
07 декабря 2007 года
kot_
7.3K / / 20.01.2000
Цитата: oxotnik333
Спасибо за отзыв :)
буду работать "над собой"


Да не за что. Всегда пожалуйста. :)
Просто для того что бы получать нормальные и осмысленные ответы - старайтесь прежде чем спросить - думать и делать самостоятельно - и вам пользы больше (ведь только вы видете свою задачу) и отвечать на вопрос гораздо интересней. Кроме того, гораздо проще взять ваш код и найти в нем ошибку - чем писать специально процедуру - непонятно зачем тратя свое время.
Кстати, как правило, права доступа к объектам проще всего определять на основе битовой маски - потому как определение прав доступа к детям на основе прав доступа к родителям, да еще и с произвольным уровнем вложенности - это очень затратная процедура на самом деле. Но я думаю вы еще с этим столкнетесь. :)

11
07 декабря 2007 года
oxotnik333
2.9K / / 03.08.2007
с рекурсией разобрался:
Код:
ALTER PROC [dbo].[HierarchyChart] (@ID int)
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 ?
1
07 декабря 2007 года
kot_
7.3K / / 20.01.2000
Цитата: oxotnik333
с рекурсией разобрался:
Код:
ALTER PROC [dbo].[HierarchyChart] (@ID int)
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 ?


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

 
Код:
while @@fetch_status = 0 AND Base.IDKey <> 3 AND ParentIndex is NOT NULL// и так далее

в курсор соответственно загрузить свои данные.
11
11 декабря 2007 года
oxotnik333
2.9K / / 03.08.2007
вот может кому нидь пригодится...
определение права доступа для конкретной строки таблицы и конкретного юзера:
Код:
CREATE PROC HierarchyChart (@ID int, @ID_User int)
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 --- ошибка!!! такая запись уже существует!

вопрос, как на стороне сервера (СУБД) запретить это и как ошибку обработать?
Пока ничего лучше не придумал:
 
Код:
DELETE FROM Permissions WHERE Id_User = :User AND Id_Base = :Base
INSERT INTO Permissions (Id_User, Id_Base, Id_PermissionType) values (:User, :Base, :PermissionType)

может есть какое более "красивое" решение?
1
11 декабря 2007 года
kot_
7.3K / / 20.01.2000
Цитата: oxotnik333
вот может кому нидь пригодится...
определение права доступа для конкретной строки таблицы и конкретного юзера:
Код:
CREATE PROC HierarchyChart (@ID int, @ID_User int)
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 --- ошибка!!! такая запись уже существует!

вопрос, как на стороне сервера (СУБД) запретить это и как ошибку обработать?
Пока ничего лучше не придумал:
 
Код:
DELETE FROM Permissions WHERE Id_User = :User AND Id_Base = :Base
INSERT INTO Permissions (Id_User, Id_Base, Id_PermissionType) values (:User, :Base, :PermissionType)

может есть какое более "красивое" решение?


Использовать составной уникальный индекс.

11
11 декабря 2007 года
oxotnik333
2.9K / / 03.08.2007
Цитата: kot_
Использовать составной уникальный индекс.



как в таком случае исключения обработать, если "вдруг" индекс не уникален будет?

1
11 декабря 2007 года
kot_
7.3K / / 20.01.2000
Цитата: oxotnik333
как в таком случае исключения обработать, если "вдруг" индекс не уникален будет?


 
Код:
try{
 ...
}
catch(EOleException &exception){

...

}
Реклама на сайте | Обмен ссылками | Ссылки | Экспорт (RSS) | Контакты
Добавить статью | Добавить исходник | Добавить хостинг-провайдера | Добавить сайт в каталог