Особенности:
Ограничения:
Сам механизм состоит из трёх блоков:
Схематично работа механизма выглядит так:
Исходный код функции создания файла БД:
//функция создаёт новый файл БД с возможностью перезаписи старого //Параметр 1: строка - полное имя файла базы данных //Параметр 2: булево - флаг перезаписи в случае если файл БД уже существует //возвращает 0 в случае удачного завершения работы и не 0 если функция потерпела неудачу Функция СоздатьФайлАксес(Знач ИмяФайла, Знач ПерезаписыватьФайл = Истина) //Проверим валидность имени файла Если ПустаяСтрока(ИмяФайла) Тогда Возврат -2; Иначе //анализ нахождения файла на диске Если ПерезаписыватьФайл Тогда Файл = Новый Файл(ИмяФайла); Если Файл.Существует() Тогда Файл = Неопределено; УдалитьФайлы(ИмяФайла); КонецЕсли; КонецЕсли; КонецЕсли; Состояние("Создаётся файл access: " + ИмяФайла); //создаём оболочку БД Попытка АДОХ = Новый COMОбъект("ADOX.Catalog"); Исключение Сообщить("Не удалось сформировать файл с данными. При создании объекта ADOX.Catalog произошла ошибка!" + Символы.ПС + ОписаниеОшибки()); КонецПопытки; //Подключение к только что созданной БД СтрокаПодключения = "Provider=Microsoft.Jet.OLEDB.4.0;Data Source=""" + ИмяФайла + """;Jet OLEDB:Engine Type=5;"; //подключаемся Попытка АДОХ.Create(СтрокаПодключения); Исключение Сообщить("Не удалось сформировать файл с данными. При создании объекта ADOX.Catalog произошла ошибка!" + Символы.ПС + ОписаниеОшибки()); Возврат -1; КонецПопытки; АДОХ.ActiveConnection.Close(); АДОХ = Неопределено; Возврат 0; КонецФункции
Функция получает 2 параметра:
Сначала выполняется проверка полного имени файла. Дальше отрабатывает механизм перезаписи файла. В случае необходимости существующий файл удаляется. После чего процедура переходит к созданию пустого файла БД. Делается это с помощью объекта ADOX.Catalog . Для разбора тонкостей работы ADOX.Catalog рекомендую поискать соответствующую информацию на сайте Microsoft.
Вторая «шестерёнка» механизма выгрузки в access реализует создание таблицы в файле БД. Для создания таблиц используется всё тот же объект ADOX.Catalog.
Исходный код процедуры создания новой таблицы в БД:
//функция вносит данные в таблицу //Параметр 1: строка - полное имя файла базы данных //Параметр 2: ТаблицаЗначений - Таблица с данными к выгрузке //Параметр 3: структура - описание создаваемой таблицы (сформировать можно с помощью функции: СоставлениеСтруктурыОписанияПолей) //Возвращает 0 в случае удачного завершения работы и не 0 если функция потерпела неудачу Функция ВнесениеДанныхТаблицу(Знач ИмяФайла, Знач ТаблицаДанных, Знач ОписаниеТаблицы) СтрокаПодключения = "Provider=Microsoft.Jet.OLEDB.4.0;Data Source=""" + ИмяФайла + """;Jet OLEDB:Engine Type=5;"; //объект отвечающий за связь с БД Коннектор = Новый COMОбъект("ADODB.Connection"); Коннектор.ConnectionString = СтрокаПодключения; //подключение Попытка Коннектор.Open(); Исключение Сообщить("Не удалось открыть файл БД!"); Сообщить(ОписаниеОшибки()); Возврат -1; КонецПопытки; Состояние("Заполняю данными таблицу: " + ОписаниеТаблицы.ИмяТаблицы); //командный объект БД Ком = Новый COMОбъект("ADODB.Command"); Ком.ActiveConnection = Коннектор; //константа 1 означает "запрос", бывают еще представления и хранимые процедуры Ком.CommandType = 1; СчётчикСтрок = 1; Для Каждого СтрокаТЗ Из ТаблицаДанных Цикл ОбработкаПрерыванияПользователя(); //обходим колонки данных СписокПолей = Формат(СчётчикСтрок, "ЧГ=") + "," + Символы.ПС; СчётчикСтрок = СчётчикСтрок + 1; СчётчикКолонок = 0; //цикл для формирования списка полей Для Каждого КолонкаТЗ Из ТаблицаДанных.Колонки Цикл ТекЗначение = СтрокаТЗ[КолонкаТЗ.Имя]; ОписаниеПоля = ОписаниеТаблицы.ОписаниеПолей.Получить(СчётчикКолонок); //анализ типа данных Если ОписаниеПоля.Тип = "3" Тогда //число целое //преобразуем null к 0 потому как дальнейшая функция Формат() не преобразует его правильно ТекЗначение = ?(ТекЗначение = null, 0, ТекЗначение); СписокПолей = СписокПолей + Формат(ТекЗначение, "ЧН=null; ЧГ=") + "," + Символы.ПС; ИначеЕсли ОписаниеПоля.Тип = "5" Тогда //число дробное //преобразуем null к 0 потому как дальнейшая функция Формат() не преобразует его правильно ТекЗначение = ?(ТекЗначение = null, 0, ТекЗначение); СписокПолей = СписокПолей + Формат(ТекЗначение, "ЧРД=.; ЧН=null; ЧГ=") + "," + Символы.ПС; ИначеЕсли ОписаниеПоля.Тип = "7" Тогда //дата ТекЗначение = ?(ТекЗначение = null, '00010101', ТекЗначение); Если ТекЗначение = '00010101' Тогда МодЗначение = "null"; Иначе МодЗначение = "'" + Формат(ТекЗначение, "ДЛФ=DT") + "'"; КонецЕсли; СписокПолей = СписокПолей + МодЗначение + "," + Символы.ПС; ИначеЕсли ОписаниеПоля.Тип = "11" Тогда //булево ТекЗначение = ?(ТекЗначение = null, Ложь, ТекЗначение); СписокПолей = СписокПолей + Формат(ТекЗначение, "БЛ=false; БИ=true") + "," + Символы.ПС; ИначеЕсли ОписаниеПоля.Тип = "202" Тогда //строка ТекЗначение = ?(ТекЗначение = null, "", ТекЗначение); ЗначениеСтрокой = Лев(ТекЗначение, 255); Если ПустаяСтрока(ЗначениеСтрокой) Тогда СписокПолей = СписокПолей + "null," + Символы.ПС; Иначе //Одиночная кавычка является спец символом в sql //преобразуем этот символ через код МодЗначение = СтрЗаменить(ЗначениеСтрокой, "'", "' + chr(" + КодСимвола("'") + ") + '"); СписокПолей = СписокПолей + "'" + СокрЛП(Лев(МодЗначение, 255)) + "'," + Символы.ПС КонецЕсли; ИначеЕсли ОписаниеПоля.Тип = "203" Тогда //строка Если ПустаяСтрока(ЗначениеСтрокой) Тогда СписокПолей = СписокПолей + "null," + Символы.ПС; Иначе МодЗначение = СтрЗаменить(ТекЗначение, "'", "' + chr(" + КодСимвола("'") + ") + '"); СписокПолей = СписокПолей + "'" + МодЗначение + "'," + Символы.ПС КонецЕсли; КонецЕсли; СчётчикКолонок = СчётчикКолонок + 1; КонецЦикла; //Для Каждого КолонкаТЗ Из ТаблицаДанных.Колонки Цикл //обрезаем последнюю запятую СписокПолей = Лев(СписокПолей, СтрДлина(СписокПолей) - 2); //окончательное формирование теста запроса ТекстЗапроса = "INSERT INTO " + ОписаниеТаблицы.ИмяТаблицы + " VALUES (" + СписокПолей + ")"; Ком.CommandText = ТекстЗапроса; //запись данных в БД (выполнение запроса) Попытка Ком.Execute(); Исключение Сообщить("Ошибка записи данных!" + Символы.ПС + "Текст запроса: " + ТекстЗапроса + Символы.ПС + ОписаниеОшибки()); Возврат -2; КонецПопытки; КонецЦикла; //Для Каждого СтрокаТЗ Из Данные Цикл //закрываем подключение Ком.ActiveConnection.Close(); Ком = Неопределено; Коннектор = Неопределено; Возврат 0; КонецФункции
Функция получает 3 параметра:
Получив управление, функция открывает файл БД. При удачном подключении создается объект «ADOX.Catalog» (каталог таблиц), который в первую очередь проверяет существование таблицы в соответствии с параметром функции №3. Следующим действием создаётся новая таблица. К таблице добавляется ключевое поле (индекс). Далее на основании параметра функции №2 создаются поля таблицы соответствующих типов. По завершении вновь созданная таблица добавляется в каталог и соединение закрывается.
Разумеется вручную собирать параметр функции №2 достаточно трудоёмко, для облегчения я написал функцию, которая на основании таблицы значений с данными формирует структуру описания таблицы.
Исходный код функции составление структуры описания полей:
//вспомогательная функция по пормированию структуры описания полей //Параметр 1: ТаблицаЗначений - Таблица с данными к выгрузке //Параметр 2: строка - полное имя таблицы (не должно содержать не допустимые символы: ' , . ) //Возвращает структуру описания таблицы Функция СоставлениеСтруктурыОписанияПолей(Знач ТаблицаДанных, Знач ИмяТаблицы) СтруктураОписания = Новый Структура("ИмяТаблицы,ОписаниеПолей"); СтруктураОписания.ИмяТаблицы = ИмяТаблицы; СтруктураОписания.ОписаниеПолей = Новый Массив; //обходим колонки таблицы значений Для Каждого КолонкаТЗ Из ТаблицаДанных.Колонки Цикл СтруктураСвойстПоля = Новый Структура("Имя,Тип,Длина,Синоним"); ТипЗначенияКолонки = КолонкаТЗ.ТипЗначения; ДлинаСтроки = ТипЗначенияКолонки.КвалификаторыСтроки.Длина; //проверка типов содержащихся в колонке //любой составной тип будет строкой Если ТипЗначенияКолонки.Типы().Количество() > 2 Тогда СтруктураСвойстПоля.Имя = КолонкаТЗ.Имя; СтруктураСвойстПоля.Тип = "202";//adVarWChar, type 202 [строка в Юникоде длиной в 255 символов (DT_WSTR)] СтруктураСвойстПоля.Длина = 255; СтруктураСвойстПоля.Синоним = КолонкаТЗ.Заголовок; ИначеЕсли ТипЗначенияКолонки.СодержитТип(Тип("Строка")) Тогда СтруктураСвойстПоля.Имя = КолонкаТЗ.Имя; Если ДлинаСтроки = 0 Тогда СтруктураСвойстПоля.Тип = "203";//adLongVarWChar ("memo"), type 203 [текстовый поток в Юникоде (DT_NTEXT)] Иначе СтруктураСвойстПоля.Тип = "202";//adVarWChar, type 202 [строка в Юникоде длиной в 255 символов (DT_WSTR)] //СтруктураСвойстПоля.Длина = ?(ДлинаСтроки 0 Тогда //ошибка редактора статьи при вставке кода, исправить не смог, смотрите обработку СтруктураСвойстПоля.Тип = "5";//adDouble, type 5 Иначе СтруктураСвойстПоля.Тип = "3";//adInteger, type 3 КонецЕсли; ИначеЕсли ТипЗначенияКолонки.СодержитТип(Тип("Булево")) Тогда СтруктураСвойстПоля.Имя = КолонкаТЗ.Имя; СтруктураСвойстПоля.Тип = "11";//adBoolean, type 11 СтруктураСвойстПоля.Длина = Неопределено; СтруктураСвойстПоля.Синоним = КолонкаТЗ.Заголовок; ИначеЕсли ТипЗначенияКолонки.СодержитТип(Тип("Дата")) Тогда СтруктураСвойстПоля.Имя = КолонкаТЗ.Имя; СтруктураСвойстПоля.Тип = "7";//adDate, type 7 СтруктураСвойстПоля.Длина = Неопределено; СтруктураСвойстПоля.Синоним = КолонкаТЗ.Заголовок; Иначе СтруктураСвойстПоля.Имя = КолонкаТЗ.Имя; СтруктураСвойстПоля.Тип = "202";//adVarWChar, type 202 [строка в Юникоде длиной в 255 символов (DT_WSTR)] СтруктураСвойстПоля.Длина = 255; СтруктураСвойстПоля.Синоним = КолонкаТЗ.Заголовок; КонецЕсли; СтруктураОписания.ОписаниеПолей.Добавить(СтруктураСвойстПоля); КонецЦикла; Возврат СтруктураОписания; КонецФункции
Функция получает 2 параметра:
Первоначально создается структура с описанием самой БД, после чего добавляется массив, который буде содержать в себе другие структуры с описанием полей. В процессе цикла по колонкам таблицы значений заполняется массив описания полей. Структура описания поля содержит 4 основных свойства:
Закончив формирование структуры, оная возвращается.
Наконец можно переходить к функции записывающей данные в таблицу БД. В этой функции, можно сказать, сосредоточено ядро функционала всего механизма. Таким образом, данная функция является самой сложной, но это не должно вас пугать. Программист потому и программист, что развивает аналитический склад ума.
Исходный код функции внесение данных в таблицу:
//функция вносит данные в таблицу //Параметр 1: строка - полное имя файла базы данных //Параметр 2: ТаблицаЗначений - Таблица с данными к выгрузке //Параметр 3: структура - описание создаваемой таблицы (сформировать можно с помощью функции: СоставлениеСтруктурыОписанияПолей) //Возвращает 0 в случае удачного завершения работы и не 0 если функция потерпела неудачу Функция ВнесениеДанныхТаблицу(Знач ИмяФайла, Знач ТаблицаДанных, Знач ОписаниеТаблицы) СтрокаПодключения = "Provider=Microsoft.Jet.OLEDB.4.0;Data Source=""" + ИмяФайла + """;Jet OLEDB:Engine Type=5;"; //объект отвечающий за связь с БД Коннектор = Новый COMОбъект("ADODB.Connection"); Коннектор.ConnectionString = СтрокаПодключения; //подключение Попытка Коннектор.Open(); Исключение Сообщить("Не удалось открыть файл БД!"); Сообщить(ОписаниеОшибки()); Возврат -1; КонецПопытки; Состояние("Заполняю данными таблицу: " + ОписаниеТаблицы.ИмяТаблицы); //командный объект БД Ком = Новый COMОбъект("ADODB.Command"); Ком.ActiveConnection = Коннектор; //константа 1 означает "запрос", бывают еще представления и хранимые процедуры Ком.CommandType = 1; СчётчикСтрок = 1; Для Каждого СтрокаТЗ Из ТаблицаДанных Цикл ОбработкаПрерыванияПользователя(); //обходим колонки данных СписокПолей = Формат(СчётчикСтрок, "ЧГ=") + "," + Символы.ПС; СчётчикСтрок = СчётчикСтрок + 1; СчётчикКолонок = 0; //цикл для формирования списка полей Для Каждого КолонкаТЗ Из ТаблицаДанных.Колонки Цикл ТекЗначение = СтрокаТЗ[КолонкаТЗ.Имя]; ОписаниеПоля = ОписаниеТаблицы.ОписаниеПолей.Получить(СчётчикКолонок); //анализ типа данных Если ОписаниеПоля.Тип = "3" Тогда //число целое СписокПолей = СписокПолей + Формат(ТекЗначение, "ЧН=null; ЧГ=") + "," + Символы.ПС; ИначеЕсли ОписаниеПоля.Тип = "5" Тогда //число дробное СписокПолей = СписокПолей + Формат(ТекЗначение, "ЧРД=.; ЧН=null; ЧГ=") + "," + Символы.ПС; ИначеЕсли ОписаниеПоля.Тип = "7" Тогда //дата Если ТекЗначение = '00010101' Тогда МодЗначение = "null"; Иначе МодЗначение = "'" + Формат(ТекЗначение, "ДЛФ=DT") + "'"; КонецЕсли; СписокПолей = СписокПолей + МодЗначение + "," + Символы.ПС; ИначеЕсли ОписаниеПоля.Тип = "11" Тогда //булево СписокПолей = СписокПолей + Формат(ТекЗначение, "БЛ=false; БИ=true") + "," + Символы.ПС; ИначеЕсли ОписаниеПоля.Тип = "202" Тогда //строка ЗначениеСтрокой = Лев(ТекЗначение, 255); Если ПустаяСтрока(ЗначениеСтрокой) Тогда СписокПолей = СписокПолей + "null," + Символы.ПС; Иначе //Одиночная кавычка является спец символом в sql //преобразуем этот символ через код МодЗначение = СтрЗаменить(ЗначениеСтрокой, "'", "' + chr(" + КодСимвола("'") + ") + '"); СписокПолей = СписокПолей + "'" + СокрЛП(Лев(МодЗначение, 255)) + "'," + Символы.ПС КонецЕсли; ИначеЕсли ОписаниеПоля.Тип = "203" Тогда //строка Если ПустаяСтрока(ЗначениеСтрокой) Тогда СписокПолей = СписокПолей + "null," + Символы.ПС; Иначе МодЗначение = СтрЗаменить(ТекЗначение, "'", "' + chr(" + КодСимвола("'") + ") + '"); СписокПолей = СписокПолей + "'" + МодЗначение + "'," + Символы.ПС КонецЕсли; КонецЕсли; СчётчикКолонок = СчётчикКолонок + 1; КонецЦикла; //Для Каждого КолонкаТЗ Из ТаблицаДанных.Колонки Цикл //обрезаем последнюю запятую СписокПолей = Лев(СписокПолей, СтрДлина(СписокПолей) - 2); //окончательное формирование теста запроса ТекстЗапроса = "INSERT INTO " + ОписаниеТаблицы.ИмяТаблицы + " VALUES (" + СписокПолей + ")"; Ком.CommandText = ТекстЗапроса; //запись данных в БД (выполнение запроса) Попытка Ком.Execute(); Исключение Сообщить("Ошибка записи данных!" + Символы.ПС + "Текст запроса: " + ТекстЗапроса + Символы.ПС + ОписаниеОшибки()); Возврат -2; КонецПопытки; КонецЦикла; //Для Каждого СтрокаТЗ Из Данные Цикл //закрываем подключение Ком.ActiveConnection.Close(); Ком = Неопределено; Коннектор = Неопределено; Возврат 0; КонецФункции
Функция получает 3 параметра:
Для работы с БД создаётся уже знакомое нам подключение. Подключившись, создаём объект, отвечающий за исполнение sql инструкций. Далее организуем цикл по строкам таблицы значений (параметр функции №2) и вложенный цикл по колонкам таблицы. Счётчик строк будет использоваться для индексирования строк. Счётчик колонок будет использоваться для поиска описания поля в структуре описания таблицы (параметр №3). Перебирая колонки очередной строки, анализируем их типы (по описанию таблицы) и выполняем соответствующие преобразования. В итоге у нас оказывается готовая команда sql для записи строки в БД. Применяем полученную sql инструкцию, объектом ADODB.Command. По окончании процесса закрываем подключения и очищаем память.
К сожалению на этом сайте я не смог прикрепить обработку для толстого клиента, которая может выгружать любые справочники в базу access. Но на яндекс диске её можно загрузить: http://yadi.sk/d/UzK_PAsJ3vjTS . В данной обработке задействованы все детали универсального механизма выгрузки. Конечно данной обработкой, этот механизм не ограничивается. Его можно смело копировать как в отдельный модуль, так и в свои обработки. Я не исключаю ошибочных ситуаций, которые могу привести к краху выгрузки, но это не коммерческий проект. Вы вольны доработать данный механизм под свои нужды. Ниже следуют 2 приложения с описанием констант adox. Надеюсь, данный механизм будет вам полезным. Желаю вам успехов на профессиональном поприще!
Приложение 1 список дополнительных свойств поля таблицы access:
AutoIncrement
Задает тип поля "Счетчик" с автоматическим увеличением значения при добавлении записи.
Default
Значение поля по умолчанию.
Description
Описание поля.
Fixed Length
Определяет ,будет поле фиксированой длины или переменой.
Increment
Значение, на которое будет увеличиваться поле типа "счетчик".
Nullable
Определяет, может ли поле не содержать никакого значения, иными словами должно ли это поле быть обязательным для заполнения.
Seed
Значение, с которого начнется отсчет в первой записи для поля типа "счетчик".
Jet OLEDB:Allow Zero Length
Определяет, могут ли текстовые поля содержать строки нулевой длины. Игнорируется для несимвольных полей.
Jet OLEDB:AutoGenerate
Определяет, будет ли автоматически генерироваться новое значение GUID для полей типа adGUID.
Jet OLEDB:Column Validation Rule
Выражение, определяющее правильность записываемого в поле значения, должно быть написано в формате SQL WHERE но без ключевого слова WHERE.
Jet OLEDB:Column Validation Text
Текст, который будет показан, в случае несоответствия заносимого в поле значения, определенному для этого поля правилу (Rule).
Jet OLEDB:Compressed UNICODE Strings
Определяет, будет ли Microsoft Jet сжимать UNICODE строки при записи на диск. Игнорируется, если база данных не в формате Microsoft Jet version 4.0.
Jet OLEDB:Hyperlink
Определяет, что данные в поле являются гиперссылкой. Игнорируется для полей с типом отличным от adLongVarWChar.
Jet OLEDB:IISAM Not Last Column
For Installable-ISAMs, this property informs the I-ISAM that there are more columns that are going to be added to the table after this one. If you are using ITableDefinition::AddColumn or ITableDefintion::CreateTable, it is required that you set this property for every.
Jet OLEDB:One BLOB per Page
Определяет, должны ли данные хранится в раздельных страницах (True) или могут использовать общие страницы базы данных, для экономии места на диске. Работает только с полями типа adLongVarBinary.
Приложение 2 список типов значений применяемых в ADOX
5
adDouble, type 5 - дробное число
7
adDate, type 7 - дата/время
6
adCurrency, type 6 - целое число
3
adInteger, type 3 - беззнаковое целое число
11
adBoolean, type 11 - булево
202
adVarWChar, type 202 - строка unicode длиной 255 символов
203
adLongVarWChar ("memo"), type 203 - строка unicode неограниченная строка