Сравнение 5 различных методов доступа к базе данных 1С

Не так давно на этом сайте были размещены результаты тестирования вариантов доступа к данным на примере rainbow и технологии ADO. Тестирование проводилось на достаточно сложных запросах, которые нечасто встречаются в работе с базой данных. Результаты его заинтересовали. Вместе с тем было принято решение не ограничиваться только этими вариантами доступа к базе данных, а расширить список. Кроме того было принято решение выполнить в качестве теста достаточно типовые задачи, например типичный запрос к регистру остатков для построения отчета.

Сравнение 5 различных методов доступа к базе данных 1С

Softpoint.ru/

Не так давно на этом сайте были размещены результаты тестирования вариантов доступа к данным на примере rainbow и технологии ADO. Тестирование проводилось на достаточно сложных запросах, которые нечасто встречаются в работе с базой данных. Результаты его заинтересовали. Вместе с тем было принято решение не ограничиваться только этими вариантами доступа к базе данных, а расширить список. Кроме того было принято решение выполнить в качестве теста достаточно типовые задачи, например типичный запрос к регистру остатков для построения отчета.

АнтонГусев

В данном тестировании будут принимать участие следующие методы доступа к данным:

  • стандартный доступ путем выполнения запроса ;
  • прямой запрос к данным с помощью rainbow;
  • прямой запрос к данным с помощью технологии ADO;
  • прямой запрос к данным с помощью 1С++;
  • прямой запрос к данным с помощью внешней компоненты neta.dll.

Параметры тестирования:

Будет использоваться самописная база на компоненте «Торговля». База содержит информацию за период 2,5 года. Перед тестированием базы данных, она была сжата («Shrink Database»). Объем базы данных, отображаемый в «EM», составляет 465 мегабайт. Тестовой задачей будет построение стандартного отчета по регистру «Партии» с периодом с «05.02.2002» по «20.01.2004» (то есть почти за два года). В первом варианте будет задано условие построения отчета по двум местам хранения (без группирования по местам хранения, простое суммирование данных). Второй вариант построяния отчета - без всяких условий.

В запросе должна быть единственная группировка по товару. В отчет выводятся начальные и конечные остатки за период, а также приход и расход товаров в количественном выражении. Печатная форма выводится. Замер времени работы будет производиться с помощью недокументированной функции : _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 раза больше времени. Это показывает нам, что производительность стандартного запроса деградирует гораздо более высокими темпами при росте выборки, чем производительность прямого запроса.


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