Сравнение 5 различных методов доступа к базе данных 1С
Сравнение 5 различных методов доступа к базе данных 1С
Не так давно на этом сайте были размещены результаты тестирования вариантов доступа к данным на примере rainbow и технологии ADO. Тестирование проводилось на достаточно сложных запросах, которые нечасто встречаются в работе с базой данных. Результаты его заинтересовали. Вместе с тем было принято решение не ограничиваться только этими вариантами доступа к базе данных, а расширить список. Кроме того было принято решение выполнить в качестве теста достаточно типовые задачи, например типичный запрос к регистру остатков для построения отчета.
В данном тестировании будут принимать участие следующие методы доступа к данным:
- стандартный доступ путем выполнения запроса
1С; - прямой запрос к данным с помощью
rainbow; - прямой запрос к данным с помощью технологии
ADO; - прямой запрос к данным с помощью
1С++; - прямой запрос к данным с помощью внешней компоненты
neta.dll.
Параметры тестирования:
Будет использоваться самописная база на компоненте «Торговля». База содержит информацию за период 2,5 года. Перед тестированием базы данных, она была сжата («Shrink Database»). Объем базы данных, отображаемый в «EM», составляет 465 мегабайт. Тестовой задачей будет построение стандартного отчета по регистру «Партии» с периодом с «05.02.2002» по «20.01.2004» (то есть почти за два года). В первом варианте будет задано условие построения отчета по двум местам хранения (без группирования по местам хранения, простое суммирование данных). Второй вариант построяния отчета - без всяких условий.
В запросе должна быть единственная группировка по товару. В отчет выводятся начальные и конечные остатки за период, а также приход и расход товаров в количественном выражении. Печатная форма выводится. Замер времени работы будет производиться с помощью недокументированной функции 1С: _GetPerformanceCounter().
Тестовая платформа:
Аппаратная часть: Атлон-1400 512Мб DDR, HDD 2*40Гб IDE Raid-0.
Программная часть: Win2003 server, SQL 2000 server sp3, 1С (21 релиз).
В качестве тестового вопроса будет использоваться стандартный запрос по регистру партий товаров. Посмотреть текст запроса (вариант без фильтра по местам хранения), а заодно и прочитать статью можно здесь. Запрос с фильтром по двум местам хранения не приводится с целью экономии места и времени. Внимание: отличие тестового запроса от приведенного в том, что результирующая таблица в тестовом запросе еще соединяется со справочником товаров, группируется по наименованию и сортируется по возрастанию. Таким образом запрос возвращает наименование товара, а не ID объекта.
Код модуля отчета для тестирования представлен ниже, думаю по названиям процедур видно, какая за что отвечает...
Перем НачДата,КонДата;
Перем Магазин1, Магазин2;
Перем ТекстЗапросаРадуга, ТекстЗапросаАДО;
//________________________________________________________
Процедура Выполнить1С()
НачЧисло=_GetPerformanceCounter();
//начало теста
Запрос=СоздатьОбъект("Запрос");
СЗ=СоздатьОбъект("СписокЗначений");
СЗ.ДобавитьЗначение(Магазин1);
СЗ.ДобавитьЗначение(Магазин2);
ТекстЗапроса="//{{ЗАПРОС(ОдинС)
|Период с НачДата по КонДата;
|Магазин = Регистр.Партии.Магазин;
|Наименование = Регистр.Партии.Товар;
|Количество = Регистр.Партии.Количество;
|Функция Нач = НачОст(Количество);
|Функция Прих = Приход(Количество);
|Функция Расх = Расход(Количество);
|Функция Кон = КонОст(Количество);
|Группировка Наименование без групп;
|Условие(Магазин в СЗ);
|"//}}ЗАПРОС
;
Если Запрос.Выполнить(ТекстЗапроса)=0 Тогда
Предупреждение("Не выполнился запрос!",10);
Возврат;
КонецЕсли;
Таб=СоздатьОбъект("Таблица");
Таб.ИсходнаяТаблица("Таблица");
Таб.ВывестиСекцию("Заголовок");
Пока Запрос.Группировка(1)=1 Цикл
ТекИмя=Запрос.Наименование;
ТекНач=Запрос.Нач;
ТекПрих=Запрос.Прих;
ТекРасх=Запрос.Расх;
ТекКон=Запрос.Кон;
Таб.ВывестиСекцию("Строка");
КонецЦикла;
Таб.Опции(0,0,0,0);
Таб.ТолькоПросмотр(1);
Таб.Показать();
Запрос="";
//конец теста
КонЧисло=_GetPerformanceCounter();
ТекРезультат=КонЧисло-НачЧисло;
Сообщить(ТекРезультат);
КонецПроцедуры
//________________________________________________________
Процедура ВыполнитьРадуга()
НачЧисло=_GetPerformanceCounter();
//начало теста
ЗапросРадуги=СоздатьОбъект("ODBCQuery");
Таб=СоздатьОбъект("Таблица");
Таб.ИсходнаяТаблица("Таблица");
Таб.ВывестиСекцию("Заголовок");
Если ЗапросРадуги.Prepare(ТекстЗапросаРадуга,1,1)=1 Тогда
Если ЗапросРадуги.Open()=1 Тогда
ЗапросРадуги.GotoNext();
Пока ЗапросРадуги.IsOK()=1 Цикл
//Считываем данные
ТекИмя=ЗапросРадуги.GetString(0);
ТекНач=ЗапросРадуги.GetString(1);
ТекПрих=ЗапросРадуги.GetString(2);
ТекРасх=ЗапросРадуги.GetString(3);
ТекКон=ЗапросРадуги.GetString(4);
Таб.ВывестиСекцию("Строка");
//****************
ЗапросРадуги.GotoNext();
КонецЦикла;
ЗапросРадуги.Close();
Иначе
Предупреждение("Ошибка открытия запроса!",10);
КонецЕсли;
ЗапросРадуги.Reset();
Иначе
Предупреждение("Ошибка выполнения запроса!",10);
КонецЕсли;
ЗапросРадуги="";
Таб.Опции(0,0,0,0);
Таб.ТолькоПросмотр(1);
Таб.Показать();
//конец теста
КонЧисло=_GetPerformanceCounter();
ТекРезультат=КонЧисло-НачЧисло;
Сообщить(ТекРезультат);
КонецПроцедуры
//________________________________________________________
Процедура ВыполнитьАДО()
//начало теста
Если МонопольныйРежим()=1 Тогда
Предупреждение("Невозможно выполнить запрос в монопольном режиме!",10);
//Действия
Возврат;
//********
КонецЕсли;
Соединение=СоздатьОбъект("ADODB.Connection");
ТекСервер="***";
ТекПароль="***";
ТекБаза="***";
СтрокаКоннекта="driver={SQL Server};server="+ТекСервер+";uid=sa;pwd="+ТекПароль+";Database="+ТекБаза;
Соединение.ConnectionTimeOut=20;
Соединение.CursorLocation=3;
Попытка
Соединение.Open(СтрокаКоннекта);
Исключение
Предупреждение("Невозможно установить соединение с базой данных!");
//Действия
//********
КонецПопытки;
Таб=СоздатьОбъект("Таблица");
Таб.ИсходнаяТаблица("Таблица");
Таб.ВывестиСекцию("Заголовок");
ЗапросАДО=СоздатьОбъект("ADODB.Command");
ЗапросАДО.ActiveConnection=Соединение;
НачЧисло=_GetPerformanceCounter();
ЗапросАДО.CommandText=ТекстЗапросаАДО;
Выборка=ЗапросАДО.Execute();
Если Выборка.EOF()=-1 Тогда
Иначе
Выборка.MoveFirst();
Пока Выборка.EOF()=0 Цикл
//Обработка выборки данных
ТекИмя=Выборка.Fields(0).Value;
ТекНач=Выборка.Fields(1).Value;
ТекПрих=Выборка.Fields(2).Value;
ТекРасх=Выборка.Fields(3).Value;
ТекКон=Выборка.Fields(4).Value;
Таб.ВывестиСекцию("Строка");
//*****************
Выборка.MoveNext();
КонецЦикла;
КонецЕсли;
Выборка.Close();
Соединение.Close();
Выборка="";
ЗапросАДО="";
Соединение="";
Таб.Опции(0,0,0,0);
Таб.ТолькоПросмотр(1);
Таб.Показать();
//конец теста
КонЧисло=_GetPerformanceCounter();
ТекРезультат=КонЧисло-НачЧисло;
Сообщить(ТекРезультат);
КонецПроцедуры
//________________________________________________________
Процедура Выполнить1Спп()
НачЧисло=_GetPerformanceCounter();
//начало теста
Запрос=СоздатьОбъект("ODBCRecordSet");
Запрос.Открыть(ТекстЗапросаРадуга);
Запрос.УстТипыКолонок1С("Строка,Число,Число,Число,Число");
ТЗ=СоздатьОбъект("ТаблицаЗначений");
Запрос.ПолучитьРезультатыВ_ТЗ(ТЗ,1);
Запрос.Закрыть();
Таб=СоздатьОбъект("Таблица");
Таб.ИсходнаяТаблица("Таблица");
Таб.ВывестиСекцию("Заголовок");
ТЗ.ВыбратьСтроки();
Пока ТЗ.ПолучитьСтроку()=1 Цикл
ТекИмя=ТЗ.Имя;
ТекНач=ТЗ.Нач;
ТекПрих=ТЗ.Прих;
ТекРасх=ТЗ.Расх;
ТекКон=ТЗ.Кон;
Таб.ВывестиСекцию("Строка");
КонецЦикла;
Таб.Опции(0,0,0,0);
Таб.ТолькоПросмотр(1);
Таб.Показать();
//конец теста
КонЧисло=_GetPerformanceCounter();
ТекРезультат=КонЧисло-НачЧисло;
Сообщить(ТекРезультат);
КонецПроцедуры
//________________________________________________________
Процедура ВыполнитьНета()
НачЧисло=_GetPerformanceCounter();
//начало теста
Запрос=СоздатьОбъект("AddIn.NPeriodik");
Таб=СоздатьОбъект("Таблица");
Таб.ИсходнаяТаблица("Таблица");
Таб.ВывестиСекцию("Заголовок");
ТЗ=СоздатьОбъект("ТаблицаЗначений");
ТЗ.НоваяКолонка("Имя");
ТЗ.НоваяКолонка("Нач");
ТЗ.НоваяКолонка("Прих");
ТЗ.НоваяКолонка("Расх");
ТЗ.НоваяКолонка("Кон");
ТЗПарм=СоздатьОбъект("ТаблицаЗначений");
ТЗПарм.НоваяКолонка("Номер");
ТЗПарм.НоваяКолонка("Тип");
ТЗПарм.НоваяКолонка("Размер");
ТЗПарм.НоваяСтрока();
ТЗПарм.Номер=1;
ТЗПарм.Тип=1;
ТЗПарм.Размер=0;
ТЗПарм.НоваяСтрока();
ТЗПарм.Номер=2;
ТЗПарм.Тип=4;
ТЗПарм.Размер=4;
ТЗПарм.НоваяСтрока();
ТЗПарм.Номер=3;
ТЗПарм.Тип=4;
ТЗПарм.Размер=4;
ТЗПарм.НоваяСтрока();
ТЗПарм.Номер=4;
ТЗПарм.Тип=4;
ТЗПарм.Размер=4;
ТЗПарм.НоваяСтрока();
ТЗПарм.Номер=5;
ТЗПарм.Тип=4;
ТЗПарм.Размер=4;
//________________________________________________________
Запрос.ЗапросК1С(ТекстЗапросаРадуга,ТЗПарм,ТЗ,2);
ТЗ.ВыбратьСтроки();
Пока ТЗ.ПолучитьСтроку()=1 Цикл
ТекИмя=ТЗ.Имя;
ТекНач=ТЗ.Нач;
ТекПрих=ТЗ.Прих;
ТекРасх=ТЗ.Расх;
ТекКон=ТЗ.Кон;
Таб.ВывестиСекцию("Строка");
КонецЦикла;
Таб.Опции(0,0,0,0);
Таб.ТолькоПросмотр(1);
Таб.Показать();
//конец теста
КонЧисло=_GetPerformanceCounter();
ТекРезультат=КонЧисло-НачЧисло;
Сообщить(ТекРезультат);
КонецПроцедуры
//________________________________________________________
Процедура ПриОткрытии()
НачДата='05.02.2002';
КонДата='20.01.2004';
Спр=СоздатьОбъект("Справочник.Магазины");
Спр.НайтиПоКоду("1");
Магазин1=Спр.ТекущийЭлемент();
Спр.НайтиПоКоду("3");
Магазин2=Спр.ТекущийЭлемент();
Если ЗагрузитьВнешнююКомпоненту("1cpp.dll")=0 тогда
Предупреждение ("Компонента 1с++ не найдена");
Форма.кнПлюс.Доступность(0);
КонецЕсли;
Если ЗагрузитьВнешнююКомпоненту("neta.dll")=0 тогда
Предупреждение ("Компонента neta не найдена");
Форма.кнНета.Доступность(0);
КонецЕсли;
КонецПроцедуры
Тестирования проводилось по 3 раза для каждого варианта. Перед началом тестирования все варианты прогонялись, чтобы все необходимые данные закэшировались в оперативной памяти. Время теста дано в миллисекундах.
Ниже приведена таблица для режима тестирования с фильтром по двум местам хранения:
| № | Наименование. | ||||
|---|---|---|---|---|---|
| 1C | Rainbow | ADO | 1С++ | Neta.dll | |
| 1 | 36298 | 4243 | 4302 | 3810 | 3802 |
| 2 | 35838 | 4219 | 4238 | 3797 | 3800 |
| 3 | 36060 | 4203 | 4226 | 3753 | 3798 |
| Среднее: | 36065 | 4222 | 4255 | 3787 | 3800 |
Ниже приведена таблица для режима тестирования без фильтров:
| № | Наименование. | ||||
|---|---|---|---|---|---|
| 1C | Rainbow | ADO | 1С++ | Neta.dll | |
| 1 | 149338 | 6502 | 6429 | 5889 | 6004 |
| 2 | 155806 | 6389 | 6437 | 5880 | 5998 |
| 3 | 151409 | 6451 | 6393 | 5889 | 5995 |
| Среднее: | 152184 | 6447 | 6420 | 5886 | 5999 |
Итак, объявляем победителей. Первое место достается 1С++, второе Neta.dll, третье rainbow.
Также хотелось бы обратить внимание на некоторые закономерности, выявленные тестированием. При отключении фильтра по местам хранения время формирования прямого запроса увеличилось в 1,5 раза. 1C при этом стала затрачивать почти в 4,5 раза больше времени. Это показывает нам, что производительность стандартного запроса 1С деградирует гораздо более высокими темпами при росте выборки, чем производительность прямого запроса.


Начать дискуссию