<?php // Полная загрузка сервисных книжек, создан 2024-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='\
А где можно ознакомиться с практическими примерами данной методики? И есть ли более развернутый пример с оптимизацией кода (в т.ч. запроса) обработки «Поиск и замена дублей» ?
На каком основании вы делаете допущение что все ссылки одной секции всегда уникальны по отношению к ссылкам других секций? Если не обосновать этот момент то после применения «оптимизированного» алгоритма придется вновь вызывать нормальный
(2) Alien_job, когда вы выбираете первую тысячу записей, в ней не будет ссылок из второй тысячи записей ))
(1) Ganjubas,http://www.dudin.by/tehnodemo.zip
(2) Alien_job, Ну как бы выбирает из одного индексированного источника …
А вообще не до конца понятно как же оптимизиорован запрос … конечный результат нужно было в статью … и с кодом
Что я понял: в запросе можно
1) поделить исходную таблицу на секции приемом «ВЫБРАТЬ ПЕРВЫЕ 100 Х ИЗ (ВЫБРАТЬ ПОСЛЕДНИЕ 1000 Х)»,
2) обработать секции отдельными подзапросами,
3) а затем собрать результат юнионами.
Можно надеяться, что при этом СУБД использует параллелизм и будет быстрее.
Что я не понял:
Зачем нужен запрос:
Если та же задача решается запросом:
(3) Хм.. я имел ввиду другое. Вы же дубли удаляете — уникальность ПО T1.НазначениеСостояния = Т2.НазначениеСостояния. У вас в результате оптимизированной обработки в каждой выбранной секции назначения состояния будут разными. Но после объединения результатов итоговая таблица будет полна «дублей» ПО T1.НазначениеСостояния = Т2.НазначениеСостояния примерно по 28 на каждое назначение состояния
Очень хочется поставить сразу много плюсов за идею.
подпишусь
Некорректно сравнивать время работы нормального и «оптимизорованного» подхода т.к. результат разный.
Длиннотекст. Суть, видимо в «если мы разделим первоначальную таблицу на секции, которые помещаются в память SQL-сервера, оптимизатор SQL каждое уникальное сравнение внутри секции будет выполнять по упрощенной схеме«. И непонятно, будет ли выигрыш на алгоритмах с линейной сложностью.
Суть статьи в том, чтобы программисты использовали многопоточность. Это однозначный плюс. Минус в том, что идиотский алгоритм поиска дублей оставили идиотским и не смогли найти решения, которое бы вместо поиска дублей сравнивало не все со всем, а только с правильным. Для 139к Х 2 состояния нужно всего лишь 139к Х 2 сравнения с правильными значениями, которыми и будут заменены все остальные. Для этого достаточно выбрать различные по наименованию ((если у нас наименование является определителем уникальности) — в итоге будет 2 значения, потом выбрать максимумы или минимумы ссылок — без разницы — с данными наименованиям и считать их верными. Дальше найти все неверные и заменить. Никаких 18ккк сравнений не нужно. И это уже показывает «силу» команды внедрятелей, которые дальше совершенно правильных мыслей, которые можно было вычитать даже в моей статье о многопоточности, где я говорил о наличии мощностей на серверах 1С и SQL, которые можно и нужно утилизировать, не пошли. Алгоритмическое мышление — это то, что неприсуще многим специалистам 1С, но ведь надо как-то самосовершенствоваться, учиться, развиваться…
(8) Alien_job, именно так. Но сложность задачи уже уменьшена
(6) ValeriTim, Боюсь, формат доклада для аудитории не подразумевал многостраничных текстов. Весь проект можно скачать:http://www.dudin.by/Technodemo.zip
(12) v3rter, идея комплексная, не упрощайте
(7) ildarovich, первая часть кода — написана разработчиками 1С. Действительно, и зачем так писать? )))
(7) ildarovich, по второй части кода рекомендую скачать проект и попробовать применить ваш вариант кода
(13) starik-2005, вы описываете нерабочий алгоритм. По ВЫБРАТЬ РАЗЛИЧНЫЕ вы получите ПЯТЬ наименований. И что? ))))
(14) Конкретно для ваших данных (тысячи дублей одного и того же) такой подход уменьшит сложность, но если дубли распределены равномерно (по 5 дублей на каждое НазначениеСостояния ) то такой подход ничего не даст
(13) starik-2005, применяя МАКСИМУМ или МИНИМУМ ссылок вы можете получить более одной записи в таблице. В случаях когда ссылки получены из разных узлов РИБ максимум и минимум возвращает количество строк идентичное количеству узлов исходной таблицы. Глюк-с платформы, однако, все вопросы разработчикам…
(20) Alien_job, безусловно
(20) Alien_job, хотя, можно сгенерировать и проверить ))
(18) скачать проект — это как? и зачем это мне?
Меня такой ответ категорически не устраивает. Да и ссылка (17) на разработчиков 1С тоже не в кассу. На то мы и внедренцы, чтобы кастомизировать решения на всякие частные случаи типа 139 тысяч дублей.
Криворукость предыдущей команды программистов попытались исправить чуть менее криворукие. Получилась красивая диаграмка.
То есть пока получается эмпирическая оптимизация запроса разбивкой по группам строк и, исходя из размера таблицы, количество процессоров, объем выделенной SQL памяти, предложить относительно оптимальную разбивку не получится?
П.С.
Читайте ildarovich , кланяйтесь, и благодарите.
+(24) Кстати, по ссылке из (15) вместо проекта — сообщение
Запрашиваемый ресурс перемещен, переименован либо временно недоступен.
(28) ildarovich, на 10-й раз загрузка пошла. Если постараетесь, возможно получится скачать
(27) bulpi, спасибо на добром слове, но вот кланяться точно не нужно. Идеи должны проходить апробацию, строго критиковаться, без оглядки на авторитет. Иначе большой риск допустить ошибку, которая надолго останется в коде и будет портить жизнь нам и нашим клиентам.
(28) ildarovich, ошибся, бывает
http://www.dudin.by/Tehnodemo.zip
(25) bulpi, видимо Ваш гений программирования не имеет желания публиковать свой единственной ровный вариант решения этой проблемы. Зачем что-то обсуждать, если можно обгадить? ))
(21) минимум или максимум возвращают ссылку, которая в SQL — суть число. Какие-то отсылы к РИБ тут совершенно неуместны, ибо это число для разных узлов в принципе одинаково, либо это разные ссылки. И даже если «ВЫБРАТЬ РАЗЛИЧНЫЕ Наименование ИЗ Справочник.Статусы» дает нам пять значений — это не повод сравнивать все со всем, ибо в статье описана четкая задача замены дублей ссылок. Тут не нужны 18ккк сравнений если хоть немного подумать мозгом, но, как я понял, авторы мозгом думать не желают.
(33) starik-2005, вы имеете ввиду такое решение?
«ВЫБРАТЬ
| СостоянияСогласования.НазначениеСостояния,
| МИНИМУМ(СостоянияСогласования2.Ссылка) КАК СсылкаМин
|ИЗ
| Справочник.СостоянияСогласования КАК СостоянияСогласования
| ЛЕВОЕ СОЕДИНЕНИЕ Справочник.СостоянияСогласования КАК СостоянияСогласования2
| ПО СостоянияСогласования.НазначениеСостояния = СостоянияСогласования2.НазначениеСостояния
|
|СГРУППИРОВАТЬ ПО
| СостоянияСогласования.НазначениеСостояния
|
|ИМЕЮЩИЕ
| КОЛИЧЕСТВО(РАЗЛИЧНЫЕ СостоянияСогласования2.Ссылка) > 1″;
(26) v3rter, так как я не ставил себе задач по коммерческому применению методики, то да, оптимизировано под конкретный сервер.
(34) если поле «НазначениеСостояния» является уникальным, то и представленное Вами решение может подойти, полагаю, для чего-нибудь. Но если задача — это найти все «неправильные» статусы и заменить их на произвольно выбранные «правильные», то даже такого не надо — достаточно на первом шаге отобрать уникальные, на втором шаге найти минимум или максимум ссылки, чтобы привязать правильное к одной ссылке из множества. На третьем шаге найти все, отличные от «правильных» и заменить их на «правильные», сопоставив по полю уникальности. Агрегация тут при наличии множества ссылок на одинаковые статусы не нужна, на выходе (139к — количество_уникальных_статусов) неверных ссылок, которые уже можно искать в системе и менять. Дальше можно разбить их на примерно равные части и скормить приемлемому количеству потоков для изменения. Если скармливать постепенно, то можно получить примерно равнораспределенную нагрузку, если скормить сразу все, поделив случайным образом, то можно ожидать, что часть заданий отработают раньше что приведет к замедлению обработки.
(36) starik-2005, в данной задаче, на практике справочник был заполнен заново двумя элементами, и их ссылки записаны в объекты. Поднятый вопрос — адаптация стандартного ПоискИЗаменаДублей для универсального применения, но в более вменяемые сроки, для того чтобы сопровождение проекта не нагружало отдел разработки типовыми решениями.
В любом случае новый приём оптимизации — это хорошо )
Дубли, считаю, неудачный пример, особенно если «задублившееся» поле индексировано.
(40) v3rter, считаем, что оно не индексируемое. И таблица не сортированная
(39) Dach, рассуждения нормальные, но только вслед за автором вы пытаетесь слегка поправить решение, дополнить его, решив проблему остающихся дублей. Тогда как, по моему мнению, нужно было кардинально поменять основу — сам алгоритм решения.
Пусть у вас есть пачки банкнот (лотерейных билетов), среди которых есть фальшивые. Номера фальшивых повторяются. Они — дубли.
Мало кому придет в голову сравнивать номер каждой купюры с номерами всех других купюр. Чаще всего люди в данном случае либо сортируют купюры, когда их немного, либо заводят таблицу, в которой отмечают встретившиеся номера.
Трудоемкость последней операции, очевидно, пропорциональна числу купюр. Такой способ и предлагается в (7).
Ошибка автора, вероятно, в том, что гибкий универсальный настраиваемый алгоритм поиска дублей, нацеленный на разные варианты выявления относительно немногочисленных дублей по похожести наименований, сравнения по равенству разных реквизитов он применил к очень редкому частному случаю, на который наверняка не рассчитывали разработчики.
(42) ildarovich, постойте-постойте.
Я оперирую низкоуровневым понятием «проход». Это нечто даже более детальное, чем операторы SQL-инструкций. Это именно проход в цикле.
Скажем так, я подразумеваю, что наиболее вероятным и чаще всего применяемым является алгоритм вложенных циклов.
«Чаще всего люди в данном случае либо сортируют купюры, когда их немного, либо заводят таблицу, в которой отмечают встретившиеся номера.»
Ок. Начинаю перебирать номера. Номер 12345 — встретился 1 раз. Записываю в таблицу.
Номер 234 — встретился 1 раз.
*****
спустя 500 шагов цикла
Номер 12345! Как узнать, сколько раз он мне встретился? Абстрагируйтесь, плз, от всяких кэшей, соответствий, индексированных таблиц, ибо чтобы ответить на этот вопрос, все равно придется
перебрать от начала до конца (ну или до тех пор, пока не встретится) весь второй список!
Так вот, ИМХО, ключевая фраза тут «до тех пор, пока не встретится».
Как Вы думаете, как сервер на физическом уровне выполняет сортировку? А как группировку? Думаю, что да — как Вы верно подметили удачной аналогией, «записывая номер».
Но это далеко не дает гарантий, что количество проходов будет существенно меньше, чем в моем способе. Да к тому же это намного более трудно прогнозируемо, тогда как как мой способ при известных входящих данных куда более легко просчитать.
(43) +
поэтому я не подпишусь, что в задаче поиска дублей в большой таблице более 100 тыс строк запрос вида
ВЫБРАТЬ
НазначениеСостояния
ИЗ Справочник.НазначениеСостояния
СГРУППИРОВАТЬ ПО
НазначениеСостояния
ИМЕЮЩИЕ КОЛИЧЕСТВО(РАЗЛИЧНЫЕ Ссылка) > 1
отработает быстрее, чем алгоритм, предложенный автором. К слову, мне кажется, он не просто так порекомендовал опробировать на реальных данных. Доберусь вечером до ноута — сам проверю в удовольствием
(43) Dach,
Будет существенно меньше. Проход будет ровно один. С гарантией. Посмотрите запрос в (7). Он решает ту же самую задачу. Приведу его еще раз.
Вы думаете, что расчет агрегата КОЛИЧЕСТВО(РАЗЛИЧНЫЕ потребует нескольких проходов по таблице и поиска? — Тогда вы ошибаетесь.
(44) Отработает ГОРАЗДО БЫСТРЕЕ — на два порядка, меньше, чем за секунду, скорее всего, то есть быстрее примерно в 200 раз.
(45) ildarovich, один проход по таблице-источнику — это ок, согласен.
Для того, чтобы узнать, сколько раз в таблице-источнике встретился дубль — мы заносим анализируемое поле во второй список. Со временем второй список растет.
Ответьте на один вопрос, когда встречается дубль — для того, чтобы понять — дубль он (или «трубль» или «четверубль» ;))) ) — Вы будете заглядывать во второй список? Вы будете его перебирать или нет? Мне кажется да, потому что как иначе? Ну и в чем тогда принципиальное отличие от джойна на физическом уровне?
Да только во фразе «буду перебирать весь второй список, пока не найду искомый элемент, чтобы понять, сколько он раз встречался в первой таблице»
То есть там в цикле стоит «Прервать», грубо говоря. Вот и все.
Ну и если во втором списке наша строка сидит в самом конце — разве мне его весь не придется просмотреть?
Разве не логично рассуждаю?
(46) Dach, агрегатные функции считаются с использованием хэшей. Грубо говоря, найденные номера купюр заносятся в соответствие. В этом случае нет необходимости перебора списка ранее встретившихся элементов. Обращение ксоответствию — это простая и быстрая операция.
(47) ildarovich, то есть второй список хэшируется и индексируется? Ну, тогда вопросов нет.
Однако, способ рабочий, если возможностей использовать соответствие нет
(48) Dach,
соответствие я привел для удобства объяснения. План запроса может и сортировку использовать и индекс-скан. Но все это будет еще быстрее или не медленнее. Квадратичной зависимости в этой задаче не должно быть, если специально не накосячить.
Способ рабочий? — А есть ли вообще этот способ, если нет задачи?
(9) e.kogan, этой идее сто лет в обед.