Переключение на Главную Страницу Страницы: [1]  ОтправитьПечать
Очень популярная тема (более 25 ответов) Быстрая проверка вхождения в группу справочника (число прочтений - 26895 )
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 записан
 
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 записан
 
novichek
Экс-Участник


Re: Быстрая проверка вхождения в группу справочника
Ответ #30 - 26. Декабря 2009 :: 04:37
Печать  
Апну тему Улыбка
Вскрылся тут один ньюанс с приведенными выше триггерами для случая УРБД. Ситуация:
н.р. элемент справочника лежал в корне (не имел родителя)
В перефирийной базе создали новую группу и поместили в нее этот элемент.
Что делает 1с по приходу обмена:
1. удаляет элемент.
2. создает его заново с таким же идом и выставленной принадлежностью группе (еще не существующей)
3. создает группу.

соответственно на п.2 у триггера вставки сносит крышу и он начинает ошалело инсертить в доп табличку... короче зацикливается. "Расциклить" его не проблема, но вот как бы так корректно эту ситуацию побороть, чтобы после каждого обмена не перестраивать все дерево?
  
Наверх
 
IP записан
 
novichek
Экс-Участник


Re: Быстрая проверка вхождения в группу справочника
Ответ #31 - 29. Декабря 2009 :: 03:22
Печать  
Что-то никакой активности Печаль . Либо у всех справочники небольшие, либо УРБД никто не пользует. В общем в моем случае пока остановился на следующем:
Во-первых, немного изменил триггер, который срабатывает на добавление элемента:
Код
Выбрать все
		WHILE @ParentID != $ПустойИД
		BEGIN
			INSERT INTO "+ИмяТаблицыДерева+" (ID, ParentID) VALUES (@ID, @ParentID)
			Set @ParentID = (SELECT ParentID FROM "+ИмяТаблицы+" WHERE ID = @ParentID)
			Set @ParentID = IsNull(@ParentID,$ПустойИД)
		END

		If @ParentID != $ПустойИД
		BEGIN
			INSERT INTO "+ИмяТаблицыДерева+" (ID, ParentID) VALUES (@ID, @ParentID)
		END

		If @IsFolder = 1
		BEGIN
		--для свежедобавленной группы проверим, может уже есть элементы ей принадлежащие?
		--(такое м.б. во время обмена УРБД). В этом случае лучше потом перестроить все дерево (сделаем себе "заметку")
			If Exists (Select id From "+ИмяТаблицы+" WHERE ParentID = @ID)
			BEGIN
				INSERT INTO "+ИмяТаблицыДерева+" (ID, ParentID) VALUES ($ПустойИД, $ПустойИД)
			END
		END
 


Как видно, добавилась проверка в цикле что найденный элемент не NULL и если для добавляемой группы найдена ссылка на нее, то добавляем в таблицу "сигнальную" строку с пустыми идами, которая говорит нам что надо бы все "дерево" перестроить.

Затем, н.р. в ПриНачалеРаботыСистемы смотрим, если такой элемент есть, то обновим все дерево. Н.р. вот таким запросом:
Код
Выбрать все
	Set NoCount On
	Declare @Count int

	truncate table "+ИмяТаблицыДерева+"

	--убираем индексы, чтобы сократить время добавления записей в таблицу
	IF EXISTS (SELECT name FROM sysindexes WHERE name = 'IX_Дерево_"+ВидСправочника+"_ID')
		DROP INDEX "+ИмяТаблицыДерева+".IX_Дерево_"+ВидСправочника+"_ID

	IF EXISTS (SELECT name FROM sysindexes WHERE name = 'IX_Дерево_"+ВидСправочника+"_ParentID')
		DROP INDEX "+ИмяТаблицыДерева+".IX_Дерево_"+ВидСправочника+"_ParentID

	--выбираем все группы и рассчитаем с помощью функции их уровни
	Select id, parentid as parent, dbo.fn_Уровень_"+ВидСправочника+"(id) level
	Into "+времтаб+"
	From "+ИмяТаблицы+" (NoLock) Where IsFolder = 1

	insert into "+времтаб+" (id,parent,level) values ($ПустойИД,$ПустойИД,0)
	Create index IX1 on "+времтаб+" (id)
	Create index IX2 on "+времтаб+" (level)

	Set @Count = (Select Max(level) From "+времтаб+")

	--добавляем 'детей' последнего уровня
	Insert Into "+ИмяТаблицыДерева+"
	Select id, parentid From "+ИмяТаблицы+" (NoLock) where parentid in
		(Select id from "+времтаб+" where level = @count)

	While @Count > 0
	Begin
		--добавляем 'внуков', 'правнуков' и т.д. для родителей текущего уровня
		Insert Into "+ИмяТаблицыДерева+"
		Select dt.id, wr.parent
		From "+ИмяТаблицыДерева+" dt (NoLock)
		Inner Join "+времтаб+" wr on wr.id = dt.parentid and wr.level = @count

		--добавляем элементы и группы этого уровня (всех подчиненных для верхнего уровня)
	 	Insert Into "+ИмяТаблицыДерева+"
		  Select id, parentid From "+ИмяТаблицы+" (NoLock) where parentid in
	 		(Select id from "+времтаб+" where level = @count-1)

		Set @Count = @Count - 1
	End

	--восстановим индексы
	CREATE  INDEX [IX_Дерево_"+ВидСправочника+"_ID] ON [dbo].[Дерево_"+ВидСправочника+"]([ID]) ON [PRIMARY]
	CREATE  INDEX [IX_Дерево_"+ВидСправочника+"_ParentID] ON [dbo].[Дерево_"+ВидСправочника+"]([ParentID]) ON [PRIMARY]

	Drop table "+времтаб+"
 



функция для получения уровня группы:
Код
Выбрать все
	CREATE   FUNCTION dbo."+ИмяФункции+" (@id char(9))  
	RETURNS tinyint
	BEGIN
		Declare @lev tinyint, @parent char(9)

		Set @parent = @id
		Set @lev = 0
		while @parent != $ПустойИД
		Begin
			Select @parent = ParentId From "+ИмяТаблицы+" Where id = @parent
			Set @lev = @lev + 1
		End

		return  @lev
	END
 


На тестовой базе и обычной машине в качестве сервера, перезаполнение всей таблички занимает около 2 секунд при ~70 тыс. элементах в справочнике
  
Наверх
 
IP записан
 
novichek
Экс-Участник


Re: Быстрая проверка вхождения в группу справочника
Ответ #32 - 29. Декабря 2009 :: 12:51
Печать  
и еще, мне одному кажется что в таком виде триггер update слишком ресурсозатратный? Можно н.р. сделать еще одну табличку и хранить в ней id элемента, ид непосредственного родителя (можно туда же засунуть и уровень элемента) и при апдейте элемента сравнивать, если его родитель не изменился, то ничего не делать.
  
Наверх
 
IP записан
 
HeiHeShang
Full Member
***
Отсутствует


I Love YaBB 2!

Сообщений: 101
Зарегистрирован: 01. Августа 2006
Re: Быстрая проверка вхождения в группу справочника
Ответ #33 - 05. Мая 2010 :: 06:18
Печать  
Добавил я тригеры и теперь 1С вылетает с ошибкой, база занята другим процессом, если судить по открытым процесам, то мой коннект единственный, как от этого избавится непонятно пока
  
Наверх
 
IP записан
 
Z1
God Member
*****
Отсутствует


I Love YaBB 2!

Сообщений: 2906
Местоположение: Москва
Зарегистрирован: 26. Мая 2006
Пол: Мужской
Re: Быстрая проверка вхождения в группу справочника
Ответ #34 - 05. Мая 2010 :: 06:55
Печать  
А как вам идея строить дерево в дополнительной таблице
только из групп ( дерево будет значительно меньше  да и тригерры короче по времени выполнения)
и условие ставить не на ID а на PARENTID
  
Наверх
 
IP записан
 
Z1
God Member
*****
Отсутствует


I Love YaBB 2!

Сообщений: 2906
Местоположение: Москва
Зарегистрирован: 26. Мая 2006
Пол: Мужской
Re: Быстрая проверка вхождения в группу справочника
Ответ #35 - 05. Мая 2010 :: 07:03
Печать  
Цитата:
Апну тему Улыбка
Вскрылся тут один ньюанс с приведенными выше триггерами для случая УРБД. Ситуация:
н.р. элемент справочника лежал в корне (не имел родителя)
В перефирийной базе создали новую группу и поместили в нее этот элемент.
Что делает 1с по приходу обмена:
1. удаляет элемент.
2. создает его заново с таким же идом и выставленной принадлежностью группе (еще не существующей)
3. создает группу.

соответственно на п.2 у триггера вставки сносит крышу и он начинает ошалело инсертить в доп табличку... короче зацикливается. "Расциклить" его не проблема, но вот как бы так корректно эту ситуацию побороть, чтобы после каждого обмена не перестраивать все дерево?

Твоя проблема (ИМХО) полностью решается с помощью #34

Тригер должен реагтровать только на пункт 3 ( изменения для элементов пропускаем )
  
Наверх
 
IP записан
 
HeiHeShang
Full Member
***
Отсутствует


I Love YaBB 2!

Сообщений: 101
Зарегистрирован: 01. Августа 2006
Re: Быстрая проверка вхождения в группу справочника
Ответ #36 - 05. Мая 2010 :: 13:56
Печать  
Я изменения элементов и так не учитываю
  
Наверх
 
IP записан
 
Z1
God Member
*****
Отсутствует


I Love YaBB 2!

Сообщений: 2906
Местоположение: Москва
Зарегистрирован: 26. Мая 2006
Пол: Мужской
Re: Быстрая проверка вхождения в группу справочника
Ответ #37 - 05. Мая 2010 :: 14:14
Печать  
HeiHeShang писал(а) 05. Мая 2010 :: 13:56:
Я изменения элементов и так не учитываю

Тогда приведи все свое что есть (смотри в качестве образца #2)
  
Наверх
 
IP записан
 
HeiHeShang
Full Member
***
Отсутствует


I Love YaBB 2!

Сообщений: 101
Зарегистрирован: 01. Августа 2006
Re: Быстрая проверка вхождения в группу справочника
Ответ #38 - 06. Мая 2010 :: 05:30
Печать  
Код
Выбрать все
CREATE TRIGGER parents_update ON SC84
	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 parents.dbo.Дерево_Родителей
			FROM parents.dbo.Дерево_Родителей
			INNER JOIN (
			SELECT
				Parents.ParentID ParentID,
				Children.ID ID
			FROM
				parents.dbo.Дерево_Родителей Parents
				INNER JOIN parents.dbo.Дерево_Родителей Children ON
					Children.ParentID = Parents.ID
			WHERE
				Parents.ID = @ID
			) Children ON
				Children.ParentID = parents.dbo.Дерево_Родителей.ParentID
				AND Children.ID = parents.dbo.Дерево_Родителей.ID
	END

	DELETE FROM parents.dbo.Дерево_Родителей WHERE ID = @ID

	SET @Level = 0
	WHILE @ParentID <> '     0   '
	BEGIN
		INSERT INTO parents.dbo.Дерево_Родителей (ID, ParentID) VALUES (@ID, @ParentID)

		IF @IsFolder = 1
		BEGIN
			INSERT INTO parents.dbo.Дерево_Родителей
				SELECT DISTINCT SubTree.ID, @ParentID
				FROM parents.dbo.Дерево_Родителей SubTree
				WHERE SubTree.ParentID = @ID

		END

		SELECT @ParentID = ParentID FROM SC84 WHERE ID = @ParentID
		SET @Level = @Level + 1
	END

	UPDATE SC84 SET SP10576 = @Level WHERE ID = @ID


	IF @IsFolder = 1
	BEGIN
		UPDATE SC84 SET SP10576 = (SELECT count(*) FROM parents.dbo.Дерево_Родителей WHERE parents.dbo.Дерево_Родителей.ID = ThisRef.ID)
		FROM SC84 ThisRef
		INNER JOIN parents.dbo.Дерево_Родителей SubTree ON SubTree.ID = ThisRef.ID
		WHERE SubTree.ParentID = @ID
	END
 

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



Сообщений: 3046
Местоположение: Киров
Зарегистрирован: 23. Мая 2006
Пол: Мужской
Re: Быстрая проверка вхождения в группу справочника
Ответ #39 - 06. Мая 2010 :: 05:35
Печать  
добавь
set nocount on
  

1&&2&&3
Наверх
 
IP записан
 
Переключение на Главную Страницу Страницы: [1] 
ОтправитьПечать