Переключение на Главную Страницу Страницы: 1 [2] 3  ОтправитьПечать
Очень популярная тема (более 25 ответов) Быстрая проверка вхождения в группу справочника (число прочтений - 25124 )
ADirks
1c++ developer
1c++ moderator
Отсутствует


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

Сообщений: 692
Местоположение: Новосибирск
Зарегистрирован: 22. Мая 2006
Пол: Мужской
Re: Быстрая проверка вхождения в группу справочник
Ответ #15 - 25. Июля 2006 :: 06:48
Печать  
Сложновато, зато быстро  Улыбка
Представь отчёт по остаткам с фильтром по группе, при достаточно большом справочнике. Использование функции, которая внутри себя делает кучу запросов будет тормозить весьма и весьма. А джойн по индексированным полям - это намного легче. Кстати, метода Joe Celko мне тоже представляется несколько более медленной, потому что там условия ставятся на больше/меньше, а такие условия отрабатывают несколько медленнее чем на равенство/неравенство. К тому же insert и update получаются очень тяжёлыми - чуть ли не всю вспомогательную таблицу надо обмолотить.
  
Наверх
 
IP записан
 
Piterken
YaBB Newbies
*
Отсутствует



Сообщений: 4
Зарегистрирован: 21. Июля 2006
Пол: Мужской
Re: Быстрая проверка вхождения в группу справочник
Ответ #16 - 25. Июля 2006 :: 07:22
Печать  
Да, по-поводу вставки, удаления, изменения в иерархии в методе Joe Celko -  это долго. С другой стороны, количество элементов в данной таблице будет существенно меньше(точнее равным количесту эл-тов справочника), особенно при достаточно большой глубине дерева, поэтому не поручусь, что запрос будет существенно медленнее .

А еще, ведь никто не мешает нам заносить вспомогательные данные из метода Joe Celko в сам справочник. Ну будет на 2 int поля больше - и все затраты.
  
Наверх
 
IP записан
 
Утюг
Junior Member
**
Отсутствует



Сообщений: 56
Местоположение: Ростов-на-Дону
Зарегистрирован: 25. Июля 2006
Пол: Мужской
Re: Быстрая проверка вхождения в группу справочник
Ответ #17 - 25. Июля 2006 :: 08:00
Печать  
ADirks писал(а) 25. Июля 2006 :: 06:48:
Сложновато, зато быстро  Улыбка
Представь отчёт по остаткам с фильтром по группе, при достаточно большом справочнике.

Я, конечно, не делал замеров, но на справочнике в 25 тыс. меня скорость устраивала. Другое дело, что цели получения полного кода могут быть разными, и я допускаю существование задач, где время исполнения данной функции будет неприемлимым Смущённый.
  
Наверх
 
IP записан
 
ADirks
1c++ developer
1c++ moderator
Отсутствует


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

Сообщений: 692
Местоположение: Новосибирск
Зарегистрирован: 22. Мая 2006
Пол: Мужской
Re: Быстрая проверка вхождения в группу справочник
Ответ #18 - 15. Декабря 2006 :: 10:04
Печать  
Доработанная версия триггеров - учитывается перенос в другую группу

Код
Выбрать все
Функция ТриггерДляИерархии(ВидСправочника)
	ИмяТаблицыДерева	= ИмяВторойБазы+".Дерево_"+ВидСправочника;
	ИмяТаблицы			= РадугаМД.ИмяТаблицыСправочника(ВидСправочника);
	ПолеУровеньИерархии	= "SP" + РадугаМД.ИДРеквизитаСправочника(ВидСправочника, "УровеньИерархии");

	//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
	|";
	Если СоздатьТриггер(ИмяТриггера, ТекстЗапроса) = 0 Тогда
		Возврат 0;
	КонецЕсли;

	//update
	ИмяТриггера = "ТриггерОбновление_Иерархия_"+ВидСправочника;
	ТекстЗапроса = "CREATE TRIGGER "+ИмяТриггера+" ON "+ИмяТаблицы+"
	|AFTER UPDATE
	|AS
	|DECLARE @ID char(9), @ParentID char(9), @IsFolder tinyint, @Level tinyint
	|
	|SELECT @ID = ID, @ParentID = ParentID, @IsFolder = IsFolder
	|FROM Inserted
	|
	|IF @IsFolder = 1
	|BEGIN
	|	DELETE "+ИмяТаблицыДерева+"
	|		FROM "+ИмяТаблицыДерева+"
	|		INNER JOIN (
	|		SELECT
	|			Parents.ParentID ParentID,
	|			Children.ID ID
	|		FROM
	|			"+ИмяТаблицыДерева+" Parents
	|			INNER JOIN "+ИмяТаблицыДерева+" Children ON
	|				Children.ParentID = Parents.ID
	|		WHERE
	|			Parents.ID = @ID
	|		) Children ON
	|			Children.ParentID = "+ИмяТаблицыДерева+".ParentID
	|			AND Children.ID = "+ИмяТаблицыДерева+".ID
	|END
	|
	|DELETE FROM "+ИмяТаблицыДерева+" WHERE ID = @ID
	|
	|SET @Level = 0
	|WHILE @ParentID <> '     0   '
	|BEGIN
	|	INSERT INTO "+ИмяТаблицыДерева+" (ID, ParentID) VALUES (@ID, @ParentID)
	|
	|	IF @IsFolder = 1
	|	BEGIN
	|		INSERT INTO "+ИмяТаблицыДерева+"
	|			SELECT DISTINCT SubTree.ID, @ParentID
	|			FROM "+ИмяТаблицыДерева+" SubTree
	|			WHERE SubTree.ParentID = @ID
	|
	|	END
	|
	|	SELECT @ParentID = ParentID FROM "+ИмяТаблицы+" WHERE ID = @ParentID
	|	SET @Level = @Level + 1
	|END
	|
	|UPDATE "+ИмяТаблицы+" SET "+ПолеУровеньИерархии+" = @Level WHERE ID = @ID
	|
	|
	|IF @IsFolder = 1
	|BEGIN
	|	UPDATE "+ИмяТаблицы+" SET "+ПолеУровеньИерархии+" = (SELECT count(*) FROM "+ИмяТаблицыДерева+" WHERE "+ИмяТаблицыДерева+".ID = ThisRef.ID)
	|	FROM "+ИмяТаблицы+" ThisRef
	|	INNER JOIN "+ИмяТаблицыДерева+" SubTree ON SubTree.ID = ThisRef.ID
	|	WHERE SubTree.ParentID = @ID
	|END
	|";
	Если СоздатьТриггер(ИмяТриггера, ТекстЗапроса) = 0 Тогда
		Возврат 0;
	КонецЕсли;

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

	Возврат 1;
КонецФункции
 

  
Наверх
 
IP записан
 
kms
1c++ power user
1c++ moderator
Отсутствует


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

Сообщений: 4632
Зарегистрирован: 19. Мая 2006
Re: Быстрая проверка вхождения в группу справочник
Ответ #19 - 15. Декабря 2006 :: 10:09
Печать  
А первоначальное заполнение таблицы соответствий родителей как выполняется?

+
Если кто-то помнит, можно ли в 1cv7.dds запихнуть триггеры, напомните, плз.
  

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


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

Сообщений: 692
Местоположение: Новосибирск
Зарегистрирован: 22. Мая 2006
Пол: Мужской
Re: Быстрая проверка вхождения в группу справочник
Ответ #20 - 15. Декабря 2006 :: 10:35
Печать  
kms писал(а) 15. Декабря 2006 :: 10:09:
А первоначальное заполнение таблицы соответствий родителей как выполняется?

да тупо
Код
Выбрать все
		Спр = СоздатьОбъект("Справочник."+МетаСпр.Идентификатор);
		Спр.ВыбратьЭлементы();
		Пока Спр.ПолучитьЭлемент() = 1 Цикл
			Если ПустоеЗначение(Спр.Родитель) = 0 Тогда
				Спр.Записать();
			КонецЕсли;
		КонецЦикла;
 

  
Наверх
 
IP записан
 
Arta
1c++ power user
Отсутствует



Сообщений: 2537
Местоположение: Нижний Новгород
Зарегистрирован: 19. Мая 2006
Пол: Мужской
Re: Быстрая проверка вхождения в группу справочник
Ответ #21 - 15. Декабря 2006 :: 12:38
Печать  
kms писал(а) 15. Декабря 2006 :: 10:09:
Если кто-то помнит, можно ли в 1cv7.dds запихнуть триггеры, напомните, плз.


Я бы все таки пошел по предложенному тут пути. По крайней мере с индексами работает год, в том числе на MSDE на ноуте.
http://www.softpoint.ru/article_id15.htm
  
Наверх
 
IP записан
 
kms
1c++ power user
1c++ moderator
Отсутствует


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

Сообщений: 4632
Зарегистрирован: 19. Мая 2006
Re: Быстрая проверка вхождения в группу справочник
Ответ #22 - 15. Декабря 2006 :: 12:55
Печать  
Arta

Спасибо с ссылку, я все собирался разыскать твое пред. сообщение и прочитать статью. Улыбка
Ну и г-ну Сердюку спасибо. Всех благ ему.
  

De quelle planète es-tu?
Наверх
 
IP записан
 
novichek
Экс-Участник


Re: Быстрая проверка вхождения в группу справочник
Ответ #23 - 29. Января 2008 :: 07:18
Печать  
ADirks писал(а) 15. Декабря 2006 :: 10:35:
kms писал(а) 15. Декабря 2006 :: 10:09:
А первоначальное заполнение таблицы соответствий родителей как выполняется?

да тупо
Код
Выбрать все
		Спр = СоздатьОбъект("Справочник."+МетаСпр.Идентификатор);
		Спр.ВыбратьЭлементы();
		Пока Спр.ПолучитьЭлемент() = 1 Цикл
			Если ПустоеЗначение(Спр.Родитель) = 0 Тогда
				Спр.Записать();
			КонецЕсли;
		КонецЦикла;
 



а так существенно быстрее:
Код
Выбрать все
		|Declare @max int, @cur int
		|Set @cur = (Select min(row_id) from "+идТабСпр+")
		|Set @max = (Select max(row_id) from "+идТабСпр+")
		|
		|While @cur <= @max Begin
		|	UPDATE "+идТабСпр+"
		|	Set "+идПолеУровеньИерархии+" = 0
		|	Where row_id = @cur
		|	Set @cur = @Cur + 1
		|End
 


ЗЫ: почему-то без While триггер не хотел отрабатывать :/
может кто, за одно, объяснит почему?
  
Наверх
 
IP записан
 
sadovnikov
1c++ power user
Отсутствует


I Love YaBB 2!

Сообщений: 420
Зарегистрирован: 06. Марта 2007
Re: Быстрая проверка вхождения в группу справочник
Ответ #24 - 29. Января 2008 :: 07:24
Печать  
Цитата:
ЗЫ: почему-то без While триггер не хотел отрабатывать :/
может кто, за одно, объяснит почему?


Эти триггера не рассчитаны на массовый Update. Только на изменение одной записи в таблице.
  
Наверх
 
IP записан
 
leonvlas
Junior Member
**
Отсутствует


I Love YaBB 2!

Сообщений: 47
Зарегистрирован: 03. Августа 2007
Re: Быстрая проверка вхождения в группу справочник
Ответ #25 - 19. Февраля 2008 :: 10:32
Печать  
Моя функция по аналогии предыдущей
Которая возращает нужный уровень
CREATE FUNCTION fn_RetLevelChildId(@lchild char(9),@llavelid int)
      RETURNS char(9)
BEGIN
     Declare @lid char(9)
     Declare @ltbl table (RowId int identity(1,1), ItemName varchar(9))
     Declare @l_tbl table (RowId int identity(1,1), ItemName varchar(9))

SELECT
     @lchild=parentid,
     @lid=id
FROM sc156
WHERE id = @lchild

--if @@rowcount > 0
--            Insert @ltbl VALUES(@lid)

WHILE @@rowcount > 0 BEGIN
    SELECT
           @lchild=parentid,
           @lid=id
    FROM sc156
     WHERE id = @lchild
     
     if @@rowcount > 0
           Insert @ltbl VALUES(@lid)
END

-- выстроим по порядку
Insert @l_tbl
     select ItemName
     from @ltbl
     order by  RowId DESC
     
if @@rowcount = 0
     SET @lid = '     0   '
else
     BEGIN
         Select @lid = ItemName from @l_tbl where RowId = @llavelid
         if @@rowcount = 0
         SET @lid = '     0   '
     END
     
RETURN @lid
END

Вызываю так. Только для tsql
SELECT [Номенклатура.Ссылка],
Родитель = (dbo.fn_RetLevelChildId(Номенклатура.ID,2)) is Справочник.Номенклатура
FROM [Справочник.Номенклатура] Номенклатура
WHERE [Номенклатура.Ссылка] = [@Переменная("выб")]
  
Наверх
 
IP записан
 
sml
Full Member
***
Отсутствует


I Love 1С++!

Сообщений: 186
Зарегистрирован: 28. Февраля 2008
Re: Быстрая проверка вхождения в группу справочник
Ответ #26 - 28. Февраля 2008 :: 13:51
Печать  
а мона и так (пример для справочника Номенклатура):

Код
Выбрать все
	sql="declare @oldid char(9)
	    |declare @id char(9)
		|declare @urr int
		|
		|set @id = '"+?(ВыбТовар.Выбран()=1, GetID(ВыбТовар),"     0   ")+"'
		|set @oldid = @id
		|set @urr="+?(ВыбТовар.Выбран()=1, ВыбТовар.Уровень(),0)+"
		|
		|create table #tmpid1 (ID char(9) NOT NULL, ur int not null, FullCode varchar(360) not null)
		|
		|While RTrim(LTrim(@id)) <> '0'
		|  begin
		|   insert into #tmpid1 (ID, ur, FullCode) values (@id, @urr, space(9-@urr)+'/')
		|   select @id = PARENTID
		|    from sc33
		|   where id = @id
		|   set @urr=@urr-1
		|  end
		|  set @id=@oldid
		|  set @urr="+?(ВыбТовар.Выбран()=1, ВыбТовар.Уровень(),0)+"
		|
		|  create table #tmpid2 (ID char(9) NOT NULL, ur int not null, FullCode varchar(360) not null)
		|  set @urr=@urr+1
		|  insert into #tmpid2 (ID, ur, FullCode)
		|   select ID, @urr, rtrim(code)
		|     from sc33 s1
		|    where parentid=@id and s1.isfolder = '1'
		|  While @@rowcount > 0
		|  begin
		|   set @urr=@urr+1
		|   insert into #tmpid2 (ID, ur, FullCode)
		|   select distinct s1.ID, @urr, rtrim(s2.FullCode)+'  /'+rtrim(s1."+сорт+")
		|    from sc33 s1,
		|	   #tmpid2 s2
		|   where s2.ID = s1.parentid
		|     and s1.isfolder = '1'
		|     and not exists (select 1
		|			     from #tmpid2 s3
		|			    where s1.ID = s3.ID)
		|  end
	  |
		|";
	Тхт.ДобавитьСтроку(sql);
	RecSet=Conn.Execute(sql);
 



а это функция для получения внутреннего ID
Код
Выбрать все
//*****
Function GetID(Val Obj)
	Var Str;
	Str = ValueToStringInternal(Obj);
	LV = CreateObject("ValueList");
	LV.FromSeparatedString(Mid(Str,2,Strlen(Str)-2));
	Str = LV.GetValue(LV.GetListSize());
	//If Right(Str,3) = CodeDB Then
	//	Str = ""+_IDToStr(Str)+CodeDB;
	//Else
	//	Str = ""+_IDToStr(Str)+"   ";
	//EndIF;
	баз=Right(Str,3);
	Str = ""+_IDToStr(Str)+баз;
	While Strlen(Str) < 9 Do
		Str = " "+Str;
	EndDo;
	Return Str;
EndFunction
 



сия весчь реально работает даже без ВК 1c++
заполняет от выбранной группы все нижестоящие и вышестоящие группы справочника Номенклатура

FullCode используется для дальнейшей сортировки, а в переменной сорт прописывается либо code, либо descr, либо ID
  
Наверх
 
IP записан
 
sadovnikov
1c++ power user
Отсутствует


I Love YaBB 2!

Сообщений: 420
Зарегистрирован: 06. Марта 2007
Re: Быстрая проверка вхождения в группу справочник
Ответ #27 - 29. Февраля 2008 :: 04:40
Печать  
sml писал(а) 28. Февраля 2008 :: 13:51:
а мона и так (пример для справочника Номенклатура)...


Разумеется, можно и так. Но сравни скорость работы. ИМХО, будешь неприятно удивлен и задумаешься о триггерах...
  
Наверх
 
IP записан
 
sml
Full Member
***
Отсутствует


I Love 1С++!

Сообщений: 186
Зарегистрирован: 28. Февраля 2008
Re: Быстрая проверка вхождения в группу справочник
Ответ #28 - 29. Февраля 2008 :: 06:11
Печать  
sadovnikov писал(а) 29. Февраля 2008 :: 04:40:
Разумеется, можно и так. Но сравни скорость работы. ИМХО, будешь неприятно удивлен и задумаешься о триггерах...


дык, я в Скуле - лапух, а триггеры видел лишь в виде микросхем.
  
Наверх
 
IP записан
 
sadovnikov
1c++ power user
Отсутствует


I Love YaBB 2!

Сообщений: 420
Зарегистрирован: 06. Марта 2007
Re: Быстрая проверка вхождения в группу справочник
Ответ #29 - 29. Февраля 2008 :: 06:14
Печать  
sml писал(а) 29. Февраля 2008 :: 06:11:
дык, я в Скуле - лапух, а триггеры видел лишь в виде микросхем.


Ты видел неправильные триггеры Улыбка
Те, которые в микросхемах - с ножками. А нам нужны с буковками.
Читай RTFM...
  
Наверх
 
IP записан
 
Переключение на Главную Страницу Страницы: 1 [2] 3 
ОтправитьПечать