ADO доступ к таблице Excel




Принцип обмена данными из 1С с сайтом (на MySQL) и выдачи (публикации) этих данных по запросу.
PHP-Скрипт автоматической загрузки данных из файла данных в формате CSV в базу данных сайта работающего на WordPress.

В продолжение моей темы: 1С:Альфа-Авто Автосалон Автосервис: обмен с сайтом.
С помощью данного скрипта можно загружать в автоматическом режиме, по расписанию, данные сервисных книжек (ремонтов авто) из 1С:Альфа-Авто Автосалон Автосервис.
Также можно загружать данные в ручном режиме: для этого делается скрытая страница, где размещается специальная кнопка.
Комментарии размещенные внутри скрипта разъяснят логику и порядок действия.
Комментарии с "/////    echo" использовались для отладки.
Дополнительно создана таблица для журналирования результатов загрузки данных.
Скрипт включает в себя защиту от SQL инъекций (думаю безопасность соблюдена в полной мере).
В кратце:
1. Пишется скрипт, который запускает этот.
2. Создается регламентное задание в WordPress, по которому запускается скрипт из п.1. 
3. Этот скрипт осуществляет проверку на существование файла обмена в папке.
4. Если данные не новые, загрузка не производится.
5. Если данные новые, очищается таблица сервисных книжек.
6. Загружаются новые данные.

Собственно сам скрипт:

<?php // Полная загрузка сервисных книжек, создан 2025-01-05 12:44:55

global $wpdb2;
global $failure;
global $file_hist;

/////  echo '<H2><b>Старт загрузки</b></H2><br>';

$failure=FALSE;
//подключаемся к базе
$wpdb2 = include_once 'connection.php'; ; // подключаемся к MySQL
// если не удалось подключиться, и нужно оборвать PHP с сообщением об этой ошибке
if (!empty($wpdb2->error))
{
/////   echo '<H2><b>Ошибка подключения к БД, завершение.</b></H2><br>';
$failure=TRUE;
wp_die( $wpdb2->error );
}

$m_size_file=0;
$m_mtime_file=0;
$m_comment='';
/////проверка существования файлов выгрузки из 1С
////файл выгрузки сервисных книжек
$file_hist = ABSPATH.'/_1c_alfa_exchange/AA_hist.csv';
if (!file_exists($file_hist))
{
/////   echo '<H2><b>Файл обмена с сервисными книжками не существует.</b></H2><br>';
$m_comment='Файл обмена с сервисными книжками не существует';
$failure=TRUE;
}

/////инициируем таблицу лога
/////если не существует файла то возврат и ничего не делаем
if ($failure){
///включает защиту от SQL инъекций и данные можно передавать как есть, например: $_GET['foo']
/////   echo '<H2><b>Попытка вставить запись в лог таблицу</b></H2><br>';
$insert_fail_zapros=$wpdb2->insert('vin_logs', array('time_stamp'=>time(),'last_mtime_upload'=>$m_mtime_file,'last_size_upload'=>$m_size_file,'comment'=>$m_comment));
wp_die();
/////    echo '<H2><b>Возврат в начало.</b></H2><br>';
return $failure;
}
/////проверка лога загрузки, что бы не загружать тоже самое
$masiv_data_file=stat($file_hist);   ////передаем в массив свойство файла
$m_size_file=$masiv_data_file[7];    ////получаем размер файла
$m_mtime_file=$masiv_data_file[9];   ////получаем дату модификации файла
////создаем запрос на получение последней удачной загрузки
////выбираем по штампу времени создания (редактирования) файла загрузки AA_hist.csv, $m_mtime_file

/////   echo '<H2><b>Размер файла: '.$m_size_file.'</b></H2><br>';
/////   echo '<H2><b>Штамп времени файла: '.$m_mtime_file.'</b></H2><br>';
/////   echo '<H2><b>Формирование запроса на выборку из лога</b></H2><br>';
////препарируем запрос
$text_zaprosa=$wpdb2->prepare("SELECT * FROM `vin_logs` WHERE `last_mtime_upload` = %s", $m_mtime_file);
$results=$wpdb2->get_results($text_zaprosa);

if ($results)
{   foreach ( $results as $r)
{
////если штамп времени и размер файла совпадают, возврат
if (($r->last_mtime_upload==$m_mtime_file) && ($r->last_size_upload==$m_size_file))
{////echo '<H2><b>Возврат в начало, т.к. найдена запись в логе.</b></H2><br>';
$insert_fail_zapros=$wpdb2->insert('vin_logs', array('time_stamp'=>time(),'last_mtime_upload'=>$m_mtime_file,'last_size_upload'=>$m_size_file,'comment'=>'Загрузка отменена, новых данных нет, т.к. найдена запись в логе.'));
wp_die();
return $failure;
}
}
}
////если данные новые, пишем в лог запись о начале загрузки
/////echo '<H2><b>Попытка вставить запись о начале загрузки в лог таблицу</b></H2><br>';
$insert_fail_zapros=$wpdb2->insert('vin_logs', array('time_stamp'=>time(),'last_mtime_upload'=>0, 'last_size_upload'=>$m_size_file, 'comment'=>'Начало загрузки'));

////очищаем таблицу
$clear_tbl_zap=$wpdb2->prepare("TRUNCATE TABLE %s", 'vin_history');
$clear_tbl_zap_repl=str_replace("'","`",$clear_tbl_zap);
$results=$wpdb2->query($clear_tbl_zap_repl);
/////   echo '<H2><b>Очистка таблицы сервисных книжек</b></H2><br>';
if (empty($results))
{
/////   echo '<H2><b>Ошибка очистки таблицы книжек, завершение.</b></H2><br>';
//// если очистка не удалась, возврат
$failure=TRUE;
wp_die();
return $failure;
}

////загружаем данные
$table='vin_history';         // Имя таблицы для импорта
//$file_hist Имя CSV файла, откуда берется информация     // (путь от корня web-сервера)
$delim=';';          // Разделитель полей в CSV файле
$enclosed='"';      // Кавычки для содержимого полей
$escaped='\

50 Comments

  1. vovan519

    А зачем КоличествоСтрок = RecordSet.Fields(0).Value ?

    Reply
  2. Sigrlami

    (1)Дальше необходимо было знать количество строк,для индикатора процесса.

    Reply
  3. MRAK

    Молодец, код хорошо прокомментирован

    Reply
  4. Ish_2

    Отлично.

    Reply
  5. artem666

    напиши лучше про Insert и Update, с Select уже куча инфы в инете 🙂

    Reply
  6. Sigrlami

    (5)если действительно нужно можно написать. 😀 Впринципе то все сводится к знаниям SQL/

    Reply
  7. YRAtomic

    (6) Очень нужно.

    Еще вопрос — как быстро происходит инициализация com-объекта по сравнению с обычным Excel.Application ?

    Reply
  8. VitaliySm

    Вообще автору стоит оформить эту статью в виде обработки-шаблона. Т.е. кроме описания стоит выложить также шаблон обработки который любой скачавший сможет быстро донастроить под свои нужды.

    Reply
  9. artem666

    Excel.Application — это наимедленейшее средство работы с экселем. С Insert и Update все сводится не только к знанию SQL, есть свои хитрости, причем мне удалось побороть только Update… 😐

    Reply
  10. Sigrlami

    (8)спасибо учту.в ближайшее время выложу 😀

    Reply
  11. Valiko77

    Классный метод! Спасибо огромное за публикацию!

    Reply
  12. CrazyBear

    Спасибо)) Но хотелось бы увдиеть обработку.. 😉

    Reply
  13. Oleg_nsk

    сделал… Действительно очень быстро, спасибо..

    Reply
  14. acsent

    Ничего не рассказано, про IMEX=1

    Reply
  15. Sigrlami

    (14)ссылка на msdn как бы намекает:

    «»IMEX=1;» tells the driver to always read «intermixed» (numbers, dates, strings etc) data columns as text»

    по-русски:этот параметр необходим чтоб воспринимать все колонки как значения типа Строка.

    Reply
  16. acsent

    Просто есть ограничение на данный способ загрузки. Если в колонках хранятся разнородные данные, то в лучше случае мы вытащим лишь представления

    Reply
  17. acsent

    //определение типа команды

    Command.CommandType = 1;

    Комментировать у КЭПа учился?

    Reply
  18. Sigrlami

    (16)в конце статьи я описал,что значения возвращаем строкового типа,независимо от того какие они в Excel.

    (17)http://msdn.microsoft.com/en-us/library/aa705017(BTS.10).aspx

    Reply
  19. logarifm
  20. Gmix

    Все хорошо но нельзя забывать о проблеме превышения 255 символов!

    Тут много нюансов есть!

    собственно и моя статейка http://gmixdev.ru/index.php?option=com_content&task=view&id=14&Itemid=9

    и на форуме есть сообщения на тему 255

    так что дерзайте!

    Reply
  21. Sigrlami

    (20)Спасибо.Ознакомлюсь.

    Reply
  22. Leoway

    Спасибо помогло. Есть только один вопрос.

    Когда получаем список листов командой

    Для каждого Лист ИЗ axCatalog.Tables Цикл

    КонецЦикла;

    Листы загружаются в какой-то странной последовательности, а не так как они расположены в файле. Я так предполагаю в порядке их создания в файле Excel.

    При Com соединении такой картины не наблюдается.

    Можно ли с этим бороться?

    Reply
  23. Sigrlami

    (22)

    0.Рад,что помогло. 🙂

    1.да,в порядке в котором они создавались в экселе.

    2.Ничего не мешает вам выгрузить листы в список значений и потом их как-то обрабатывать или в другую универсальную коллекцию… 💡

    Reply
  24. Leoway

    (23) Этот метод понятен. Но в основном такие обработки пишутся для пользователей, у которых свои представления и порядки создания листов в Excel. Именно по этому у меня и возник мой вопрос.

    Reply
  25. amyd

    не обязательно таблица должна быть на листе

    если это выгрузка с 1С и листов нет то такой запрос тоже пройдет

    «select * from [A0:IV100000] »

    а так респект и уважуха, плюс однозначно

    Reply
  26. Sigrlami

    (25) да, я знаю.Через пару недель когда пришлось плотнее работать,прочитал в MSDN, что можно так делать… 😉

    Спасибо за комментарий.

    Reply
  27. nikolaygorbunov

    Что такое «ТаблицаИспользуемыхКолонок»?

    Reply
  28. daho

    (27) nikolaygorbunov, Ну эт на всякий случай если не все колонки тебе нужно юзать..

    Автору спасибо, коротко и по теме!

    Reply
  29. Ed5550

    Подскажите пожалуйста:

    1) Появилась ошибка Ошибка при вызове метода контекста (Execute): Произошла исключительная ситуация (Microsoft JET Database Engine): Отсутствует значение для одного или нескольких требуемых параметров.

    Что не так делаю?

    2)Можно поподробнее что такое ТаблицаИспользуемыхКолонок.

    Заранее спасибо!

    Reply
  30. adva

    А через ADO возможно обновить лист excel? (определенные ячейки)

    Reply
  31. ChessCat

    Почему нельзя было описать структуру «ТаблицаИспользуемыхКолонок» чтобы люди читающие статью не ломали голову наш шарадами ?

    Reply
  32. ChessCat

    (29) Ed5550, «Произошла исключительная ситуация (Microsoft JET Database Engine)»

    это означает что вы пытаетесь открыть файл более старшей версии Excel. У автора в примере Excel 8.0

    Для Excel 2007 и выше надо использовать «Provider=Microsoft.ACE.OLEDB.12.0»;

    Reply
  33. lev6975

    Для каждого Лист ИЗ axCatalog.Tables Цикл

    Если счЛиста=ЭлементыФормы.НомерЛиста.Значение Тогда

    Как — то, странно, у меня четвертый лист в файле(визуально) идет по этой процедуре первым,не тот лист хавает, в общем. А, первый лист в файле (визуально)- седьмой

    а, я, и, думаю, почему у меня запрос

    Command.CommandText =»SELECT COUNT(*)FROM [«+ИмяТаблицы+»]»;,

    показывает 27 колонок(полей), когда у меня их 60+…

    Это зависит от чего — то?От конкретного компа, винды, версии ёкселя?

    Чтобы, у клиентов номера листов определял так же как и у меня

    Reply
  34. lev6975

    ёксель 2010 стоит

    у клиентов пока не знаю какой

    Reply
  35. lev6975

    Хотя, чё я парюсь, лист можно словить по его имени,а, не по номеру))))

    Reply
  36. fzt

    (30) adva, Insert и Update для файлов Excel существуют. По крайней мере с OLEDB я точно этим пользовался.

    Reply
  37. fzt

    (7) YRAtomic, всяко быстрее. Это лишь подлючение DLL. Даже устанавливать Excel на сервер не нужно ну и покупать.

    А вот Application — открывает полновесный Excel, открывается дольше, жрет много ресурсов.

    Reply
  38. fzt

    (33) lev6975, может потому, что нумерация в массиве идет с 0?

    Reply
  39. lev6975

    (38) fzt, Возможно. И, с конца.Всего листов пять(пятый — пустой, его пропускает). Тогда,четвертый — нулевой, а, следующая итерация, если оттрассировать процедуру — снова четвертый, только с допиской xlnm#_FilterDatabase

    Тогда, если, по две итерации на лист, получается, что, седьмая итерация, как раз,зацепит первый лист…

    Я, наверно, все — таки, сделаю по именам — загружу имена на форму в список значений, чтобы, выбирались, именно, по именам…

    ЗЫ,и, правда,таблицу с килострокой записей ёкселя на 60 полей(всего 60000 полей), грузит меньше секунды! Теперь, узким местом будет раскидка всего этого по документам и справочникам и запись в базу))

    Все, понял — он сортирует листы по именам по алфавиту — сначала,выводит листы с именем на латинице, потом, на кириллице. Нужный лист, как раз, с кириллическим именем,остальные на латинице, вот, его и выкидывает последним))

    Reply
  40. lev6975

    (22) Leoway, как раз, у меня такое было — листы переименованы и сортируются в алфавитном порядке. Если, листы обозваны лист1,лист2, лист3 и т.д. тогда, будут так как в файле

    Reply
  41. lev6975

    Вопрос, как читать поля с датами — ADO их преобразовывает(причем, через раз) к какому — то числу непонятному(номер дня с 01.01.1900года)… Меня бы устроило, чтобы считал строкой,»что вижу то и читаю», без каких — то мудростей. в 1С я сам приведу все к дате.Метод Cdate возвращает дату,но, читает не все поля колонки, по каким — то причинам пропускает

    Reply
  42. fzt

    (39) lev6975, косяк нюанс есть.

    «если список листов получаешь через Connection.OpenSchema(adSchemaTables), то при сложном форматировании там могут быть не только листы, но и области листа. А в результате может быть такой замечательный эффект: данные как бы получаешь. Но не всегда все :)» © fisher

    С датами. ADO может рисовать null, если в одной колонке разные типы данных. Что легко достижимо форматирование шапки таблицы например.

    За сим я бы как раз обращался только к области листа, в которой нужные данные, т.е. искал конкретно область таблицы (достижимо когда ексельку формируют «свои», или предварительно форматировал файлы (когда файл формируют «чужие»), выкинув все кроме данных.

    Ещё можно поставить это:

    https://www.microsoft.com/en-us/download/details.aspx?id=23734

    и попробовать

    «Provider=Microsoft.ACE.OLEDB.12.0; Data Source=» + ПараметрыПодключения.ИмяФайла + «; Extended Properties=»»Excel 12.0; HDR=NO; IMEX=1;»»»

    Reply
  43. lev6975

    (42) спасибо, попробую. Заказчик на месяц, к счастью слинял и от меня отвязался,не висит над душой, я закинул проект на потом. На той недельке вернусь к нему, попробую и отпишусь в этой ветке

    Reply
  44. lev6975

    (42) fzt, Приветствую, недавно вспомнил за мой вопрос… Я понял, почему из файла затягивается белиберда — файл во время ADO запроса не должен быть открыт в excel !!!)))

    Не знаю, почему, но, если файл не открыт, затягивается все идеально. Если висит в ёкселе — начинает неясным образом кривить — плывут не ток даты(как я писал выше), но и числа, тож, грузятся криво… Попросить клиентов схлопывать файл перед затягиванием его в базу я могу, поэтому, в общем, все ОК, спасибо за участие!!! А,так, в общем — то,интересно, почему эксёл таким загадочным образом влияет на запрос…

    Может, кстати, кривой драйвер на моем компе, и, его надо обновить с Макрософта… Но, вот, у клиента я этого сделать не смогу, поскоку, клиента, скорее всего, и, ни разу не увижу)))…если ток заказчика просить. Проще, уж, приучить к закрыванию файла перед погрузкой

    Или, О!Проверять, открыт ли файл в момент запроса и делать отказ, если открыт…так и сделаю

    Reply
  45. fzt

    (44) lev6975, скопировать во временный каталог, а потом удалить?

    Reply
  46. lev6975

    (45) fzt, Да, тоже идея!!Отвязать копию от ёкселя. Надо попробовать…плохо то что не знаю что у конечного клиента на компе творится, надо в темп кидать

    Reply
  47. hroa

    всё-таки вопрос из (22) не раскрыт до конца. Стоит задача: взять данные с первого листа, файлы будут приходить разные и листы в них будут разные.

    Для каждого Лист ИЗ axCatalog.Tables Цикл

    КонецЦикла;

    решило бы эту проблему, но оно почему-то выдает листы в алфавитном порядке. Можно ли вообще найти название первого листа?

    Reply
  48. SHiCK

    Все классно и автору спасибо. но вот вы меня простите конечно, а зачем коменты без пробелов писать

    во вторых в коде идут ссылки на переменные из формы и еще от куда-то. было бы здорово это поправить.

    Reply
  49. Serge_ASB

    Добрый день.

    У меня при попытке соединения ругается на Провайдера. А как можно понять, какие провайдеры в системе установлены, или что нужно установить, чтобы всё работало, «как написано»?

    Reply
  50. Serge_ASB

    (42) Приведенная ссылка на microsoft.com не работает — объект удален. А что, собственно, там было?

    Reply

Leave a Comment

Ваш адрес email не будет опубликован. Обязательные поля помечены *