Переключение на Главную Страницу Страницы: [1] 2 3  ОтправитьПечать
Очень популярная тема (более 25 ответов) Быстрая проверка вхождения в группу справочника (число прочтений - 26812 )
Piterken
YaBB Newbies
*
Отсутствует



Сообщений: 4
Зарегистрирован: 21. Июля 2006
Пол: Мужской
Быстрая проверка вхождения в группу справочника
21. Июля 2006 :: 08:04
Печать  
Собственно, много разной информации по данной теме, но хочется раз и навсегда
найти универсальное и наиболее быстое решение с помощью прямого запроса. Может кто-то
выложит свои варианты решения, и тогда общественность ( и я в том числе) будет
очень довольна Подмигивание
  
Наверх
 
IP записан
 
ADirks
1c++ developer
1c++ moderator
Отсутствует


А нужны ли мы нам?

Сообщений: 692
Местоположение: Новосибирск
Зарегистрирован: 22. Мая 2006
Пол: Мужской
Re: Быстрая проверка вхождения в группу справочник
Ответ #1 - 21. Июля 2006 :: 09:25
Печать  
Например так.  Делаем дополнительную таблицу, в которой хранятся пары ID, ParentID для всех вышестоящих родителей. У меня это выглядит так:
Код
Выбрать все
	// Вспомогательная таблица для хранения иерархии объектов приложения прав
	ЗапросНаСозданиеТаблицы = "
	|Set NoCount ON
	|CREATE TABLE Дерево_ТипыОбъектовПриложенияРазрешений (
	|	ID char(9) NOT NULL,
	|	ParentID char(9) NOT NULL
	|)";
	ЗапросНаСозданиеИндексов = "
	|Set NoCount ON
	|CREATE INDEX [IX_Дерево_ТипыОбъектовПриложенияРазрешений_ID]
	|	ON dbo.[Дерево_ТипыОбъектовПриложенияРазрешений](ID)
	|CREATE INDEX [IX_Дерево_ТипыОбъектовПриложенияРазрешений_ParentID]
	|	ON dbo.[Дерево_ТипыОбъектовПриложенияРазрешений](ParentID)
	|";
 


Делаем триггеры
Код
Выбрать все
	ИмяТриггера = "ТриггерВставка_Иерархия_ТипыОбъектовПриложенияРазрешений";
	ИмяТаблицы = МД.ИмяТаблицыСправочника("ТипыОбъектовПриложенияРазрешений");

	//insert
	ТекстЗапроса = "CREATE TRIGGER "+ИмяТриггера+" ON "+ИмяТаблицы+"
	|AFTER INSERT
	|AS
	|DECLARE @ID char(9), @ParentID char(9), @Level tinyint
	|SELECT @ID = ID, @ParentID = ParentID FROM Inserted
	|SET @Level = 0
	|WHILE @ParentID <> '"+глПустойИдентификатор9+"'
	|BEGIN
	|	INSERT INTO "+ИмяВторойБазы+"Дерево_ТипыОбъектовПриложенияРазрешений
	|		(ID, ParentID) VALUES (@ID, @ParentID)
	|	SELECT @ParentID = ParentID FROM "+ИмяТаблицы+" WHERE ID = @ParentID
	|	SET @Level = @Level + 1
	|END
	|
	|UPDATE "+ИмяТаблицы+" SET "+ПолеПорядокСортировки+" = @Level WHERE ID = @ID
	|";
	Если СоздатьТриггер(ИмяТриггера, ТекстЗапроса, 1) = 0 Тогда
		Возврат 0;
	КонецЕсли;

	//update
	ИмяТриггера = "ТриггерОбновление_Иерархия_ТипыОбъектовПриложенияРазрешений";
	ТекстЗапроса = "CREATE TRIGGER "+ИмяТриггера+" ON "+ИмяТаблицы+"
	|AFTER UPDATE
	|AS
	|DECLARE @ID char(9), @ParentID char(9), @Level tinyint
	|SELECT @ID = ID, @ParentID = ParentID FROM Inserted
	|DELETE FROM "+ИмяВторойБазы+"Дерево_ТипыОбъектовПриложенияРазрешений WHERE ID = @ID
	|SET @Level = 0
	|WHILE @ParentID <> '"+глПустойИдентификатор9+"'
	|BEGIN
	|	INSERT INTO "+ИмяВторойБазы+"Дерево_ТипыОбъектовПриложенияРазрешений (ID, ParentID) VALUES (@ID, @ParentID)
	|	SELECT @ParentID = ParentID FROM "+ИмяТаблицы+" WHERE ID = @ParentID
	|	SET @Level = @Level + 1
	|END
	|
	|UPDATE "+ИмяТаблицы+" SET "+ПолеПорядокСортировки+" = @Level WHERE ID = @ID
	|";
	Если СоздатьТриггер(ИмяТриггера, ТекстЗапроса, 1) = 0 Тогда
		Возврат 0;
	КонецЕсли;

	//delete
	ИмяТриггера = "ТриггерУдаление_Иерархия_ТипыОбъектовПриложенияРазрешений";
	ТекстЗапроса = "CREATE TRIGGER "+ИмяТриггера+" ON "+ИмяТаблицы+"
	|AFTER DELETE
	|AS
	|DECLARE @ID char(9)
	|SELECT @ID = ID FROM Deleted
	|DELETE FROM "+ИмяВторойБазы+"Дерево_ТипыОбъектовПриложенияРазрешений WHERE ID = @ID
	|";
	Если СоздатьТриггер(ИмяТриггера, ТекстЗапроса, 1) = 0 Тогда
		Возврат 0;
	КонецЕсли;
 


И теперь получить все подчинённые элементы - дело техники. Например так:
Код
Выбрать все
	Запрос.Подготовить("SET NoCount ON
	|SELECT
	|	ТипыОбъектов.ID Элемент
	|FROM
	|	"+ИмяВторойБазы+"Дерево_ТипыОбъектовПриложенияРазрешений Дерево (NoLock)
	|	INNER JOIN $Справочник.ТипыОбъектовПриложенияРазрешений ТипыОбъектов (NoLock)  ON ТипыОбъектов.ID = Дерево.ID
	|WHERE
	|	Дерево.ParentID = :Группа
 

  
Наверх
 
IP записан
 
Piterken
YaBB Newbies
*
Отсутствует



Сообщений: 4
Зарегистрирован: 21. Июля 2006
Пол: Мужской
Re: Быстрая проверка вхождения в группу справочник
Ответ #2 - 21. Июля 2006 :: 10:57
Печать  
Спасибо за оперативносьУлыбка Ответ достойный.
Данный подход безусловно быстрый, даже очень.
Есть вопросы :
- в какое место выносятся процедуры
создания дополнительных структур данных в базе ?
( ну ведь наверняка придется это делать не один раз)
- что за вторая база ?
- с точки зрения автора стоит ли применять данный подход
для использования его только в смысле принадлежности элемента
группе ?

Может ли кто-то предложить аналог функции ПринадлежитГруппе() прямым
запросом или хранимой функцией который еще и работает быстрее
чем ПринадлежитГруппе() ?




  
Наверх
 
IP записан
 
ADirks
1c++ developer
1c++ moderator
Отсутствует


А нужны ли мы нам?

Сообщений: 692
Местоположение: Новосибирск
Зарегистрирован: 22. Мая 2006
Пол: Мужской
Re: Быстрая проверка вхождения в группу справочник
Ответ #3 - 21. Июля 2006 :: 12:31
Печать  
Создание всяческих вспомогательных таблиц и функций вынесено в обработку, которая при старте всё что надо проверяет.
Вторая база - это просто вторая база  Улыбка  Чтобы 1С при реструктуризации не сносила наши хитрые таблички, они делаются в другой базе, и в запросах к ним идёт обращение через ИмяДругойБазы.dbo.ИмяТаблицы

Такой подход можно применять и при подсчёте итогов по группам в запросе. И для фильтрации элементов справочника. Более того, можно для каждого пользователя построить его персональное дерево (http://www.citforum.ru/database/articles/tree/). А я пока только для правов это применял, чтобы с нижнего уровня иерархии добраться до первого актуального значения права в вышестоящих группах.

Кстати, метода не моя, но никак не могу найти статью, где она описывалась.


А вот ещё ссылки по деревьям в SQL:
http://www.citforum.ru/database/articles/tree/
http://www.citforum.ru/database/articles/tree.shtml
http://www.sql.ru/articles/mssql/01091502TreesInSQL.shtml
  
Наверх
 
IP записан
 
vig
Junior Member
**
Отсутствует


Мой опыт показывает, что
умирают обычно другие.

Сообщений: 34
Местоположение: Киев
Зарегистрирован: 21. Мая 2006
Пол: Мужской
Re: Быстрая проверка вхождения в группу справочник
Ответ #4 - 21. Июля 2006 :: 12:50
Печать  
Для меня открытие, что при реструктуризации 1С может снести "чужие таблицы".
Я насоздавал таблиц приблизительно год назад в текущей базе. Пользователи постоянно с ними работают.
Таким образом есть угроза потери информации?
  
Наверх
IP записан
 
steban
1c++ developer
Отсутствует


#define sizeof(x) rand()

Сообщений: 787
Зарегистрирован: 19. Мая 2006
Пол: Мужской
Re: Быстрая проверка вхождения в группу справочник
Ответ #5 - 21. Июля 2006 :: 13:07
Печать  
vig писал(а) 21. Июля 2006 :: 12:50:
Таким образом есть угроза потери информации?

Ну не то, чтобы угроза, а гарантированная потеря информации в "чужих таблицах" при реструктуризации(в SQL).
DBF-версия при реструктуризации "чужие таблицы" не уничтожает.
  

int getRandomNumber()&&{&&  return 4; //chosen by fair dice roll&&         //guaranteed to be random&&}
Наверх
 
IP записан
 
berezdetsky
1c++ power user
Отсутствует


barba non facit sisadminum

Сообщений: 1986
Местоположение: Москва
Зарегистрирован: 19. Мая 2006
Пол: Мужской
Re: Быстрая проверка вхождения в группу справочник
Ответ #6 - 21. Июля 2006 :: 13:08
Печать  
1C при реструктуризации проверяет только то, что написано в .dds. Так что угрозы нет - 1С не сносит даже "чужие" триггеры на своих таблицах.

------------

А вот при загрузке - да. Удаляется всё.
  

пароль как коньяк, чем больше звездочек, тем лучше
Наверх
IP записан
 
steban
1c++ developer
Отсутствует


#define sizeof(x) rand()

Сообщений: 787
Зарегистрирован: 19. Мая 2006
Пол: Мужской
Быстрая проверка вхождения в группу справочника
Ответ #7 - 21. Июля 2006 :: 13:13
Печать  
Во как  Ужас. Это выходит я соврал.....
Ладно, при случае попробую что можно делать в SQL-базе и чего нельзя.
З.Ы. как-то очень давно я поимел геморрой с вмешательством в структуру базы на SQL.
  

int getRandomNumber()&&{&&  return 4; //chosen by fair dice roll&&         //guaranteed to be random&&}
Наверх
 
IP записан
 
kms
1c++ power user
1c++ moderator
Отсутствует


я хочу, чтоб сюда проложили
дорогу оттуда...

Сообщений: 4632
Зарегистрирован: 19. Мая 2006
Re: Быстрая проверка вхождения в группу справочник
Ответ #8 - 21. Июля 2006 :: 13:16
Печать  
berezdetsky писал(а) 21. Июля 2006 :: 13:08:
А вот при загрузке - да. Удаляется всё.

Ну, доп. таблицы - то оставляет по-любому.
  

De quelle planète es-tu?
Наверх
 
IP записан
 
berezdetsky
1c++ power user
Отсутствует


barba non facit sisadminum

Сообщений: 1986
Местоположение: Москва
Зарегистрирован: 19. Мая 2006
Пол: Мужской
Re: Быстрая проверка вхождения в группу справочник
Ответ #9 - 21. Июля 2006 :: 13:21
Печать  
kms писал(а) 21. Июля 2006 :: 13:16:
berezdetsky писал(а) 21. Июля 2006 :: 13:08:
А вот при загрузке - да. Удаляется всё.

Ну, доп. таблицы - то оставляет по-любому.

Значит, и я соврал.  Улыбка Загружать приходится - раз в год на пасху.
  

пароль как коньяк, чем больше звездочек, тем лучше
Наверх
IP записан
 
vig
Junior Member
**
Отсутствует


Мой опыт показывает, что
умирают обычно другие.

Сообщений: 34
Местоположение: Киев
Зарегистрирован: 21. Мая 2006
Пол: Мужской
Re: Быстрая проверка вхождения в группу справочник
Ответ #10 - 21. Июля 2006 :: 13:24
Печать  
Следовательно, чтобы не создавать проблем с backup'ом, заводить друю базу для "неродных" таблиц 1С большого смысла не имеет. Хух. Пронесло...
  
Наверх
IP записан
 
steban
1c++ developer
Отсутствует


#define sizeof(x) rand()

Сообщений: 787
Зарегистрирован: 19. Мая 2006
Пол: Мужской
Re: Быстрая проверка вхождения в группу справочник
Ответ #11 - 21. Июля 2006 :: 13:32
Печать  
vig писал(а) 21. Июля 2006 :: 13:24:
Хух. Пронесло...

Я все-таки проверю.

А вообще тема до 3 поста включительно достойна занесения в FAQ. Четко и по теме.
Так-же как и соседняя: http://www.1cpp.ru/forum/YaBB.pl?num=1153475454
  

int getRandomNumber()&&{&&  return 4; //chosen by fair dice roll&&         //guaranteed to be random&&}
Наверх
 
IP записан
 
ADirks
1c++ developer
1c++ moderator
Отсутствует


А нужны ли мы нам?

Сообщений: 692
Местоположение: Новосибирск
Зарегистрирован: 22. Мая 2006
Пол: Мужской
Re: Быстрая проверка вхождения в группу справочник
Ответ #12 - 24. Июля 2006 :: 04:04
Печать  
steban писал(а) 21. Июля 2006 :: 13:32:
vig писал(а) 21. Июля 2006 :: 13:24:
Хух. Пронесло...

Я все-таки проверю.

Для пущей надёжности всё равно лучше держать дополнительные таблицы в отдельной базе. Фиг его не знает, чего там 1С удумает. Да и понятнее так.  Хотя...  на вкус и цвет...
  
Наверх
 
IP записан
 
Piterken
YaBB Newbies
*
Отсутствует



Сообщений: 4
Зарегистрирован: 21. Июля 2006
Пол: Мужской
Re: Быстрая проверка вхождения в группу справочник
Ответ #13 - 24. Июля 2006 :: 08:20
Печать  
На мой взгляд, очень грамотная ссылка
http://www.sql.ru/articles/mssql/01091502TreesInSQL.shtml

Может кто-то уже написал тригеры, поддерживающие
структуру данных, описанную в этой статье ? Или может
у кого-то есть решение, без тригеров, средствами 1С
(хотя первый вариант - вроде предпочтительнее )

И вопрос по методологии инициализации вспомогательных
данных - как-то мне кажется что пихать все в одну обработку,
не очень то в стиле ООП  Подмигивание . Кто еще как решает этот вопрос ?
  
Наверх
 
IP записан
 
Утюг
Junior Member
**
Отсутствует



Сообщений: 56
Местоположение: Ростов-на-Дону
Зарегистрирован: 25. Июля 2006
Пол: Мужской
Re: Быстрая проверка вхождения в группу справочник
Ответ #14 - 25. Июля 2006 :: 06:32
Печать  
ADirks
На мой взгляд, несколько сложновато. Предложу свой способ:

Строка = "IF EXISTS (SELECT name FROM sysobjects
     |WHERE name = 'fn_fullcode' AND type = 'FN')
     |DROP FUNCTION fn_fullcode
     |";
     
     
Если Запрос.ВыполнитьИнструкцию(Строка) = 0 Тогда
     Если ПустаяСтрока(Запрос.ПолучитьОписаниеОшибки())=0 Тогда
           Предупреждение("Ошибка ODBCRecordSet: " + РазделительСтрок + Запрос.ПолучитьОписаниеОшибки());
           СтатусВозврата(0);
     КонецЕсли;      
КонецЕсли;
     
Строка = "CREATE FUNCTION fn_fullcode(@child char(9))
|RETURNS char(900)
|AS
|BEGIN
|DECLARE @full char(900)
|SELECT @child=parentid, @full=RTRIM(LTRIM(code)) + '|' FROM $Справочник.Номенклатура WHERE id = @child
|WHILE @@rowcount > 0 BEGIN
|    SELECT @child=parentid, @full=RTRIM(LTRIM(code)) + '|' + @full FROM $Справочник.Номенклатура WHERE id = @child
|END
|RETURN @full
|END
|";
     
Если Запрос.ВыполнитьИнструкцию(Строка) = 0 Тогда
     Если ПустаяСтрока(Запрос.ПолучитьОписаниеОшибки())=0 Тогда
           Предупреждение("Ошибка ODBCRecordSet: " + РазделительСтрок + Сессия.ПолучитьОписаниеОшибки());
           СтатусВозврата(0);
     КонецЕсли;      
КонецЕсли;

Запрос.УстановитьТекстовыйПараметр("Ном", ВыбНоменклатура);
ТЗ =  Запрос.ВыполнитьИнструкцию("SELECT имябазы.dbo.fn_fullcode (:Ном)");

Если ТЗ.КоличествоСтрок() > 0 Тогда
       ПолныйКод = ТЗ.ПолучитьЗначение(1,1);
Иначе
       ПолныйКод = "";
КонецЕсли;

Если Найти(ГруппаНаПринадлежностьККоторойПроверяем.Код, ПолныйКод) > 0 Тогда
       Предупреждение("Выбранная номенклатура входит в указанную группу!);
Иначе
       Предупреждение("Выбранная номенклатура не входит в указанную группу!);
КонецЕсли;

Ну, примерно так.
В самом начале я удаляю функцию, на случай изменения ее текста.
  
Наверх
 
IP записан
 
Переключение на Главную Страницу Страницы: [1] 2 3 
ОтправитьПечать