Без сомнения, бывалые 1С-ники знают, что всеми любимая семерка не справится легко с обработкой справочника в сто тысяч элементов. А в триста или пятьсот тысяч, и при этом, чтобы база не разрасталась в гигабайты, а занимала несколько дискет в архиве?..
Первый клиент
Это был наш первый крупный клиент. Компания, занимающаяся лотерейным бизнесом, хорошо известная в Поволжье. Почему они обратились к программистам с тогда еще малоизвестного сайта? Думаю, они поступили, как и должны были поступить люди из их среды, с одной стороны – риск, а с другой – точный расчет. С риском понятно, а в чем был расчет? Да все просто: если мы – люди толковые, то справимся с их задачей не хуже именитых программистов, а возьмем за это, сами понимаете, как и подобает дебютантам.
Китайский дессерт
Задача была сформулирована просто: сделать программу по учету рапространения лотерейных билетов в офисе и через агентов, программу, которая позволяла бы проводить розыгрыш в реальном времени с трансляцией картинки с монитора компьютера по телеканалу, программу, которая бы при выпадании очередного шара мгновенно расчитывала бы количество выигравших билетов с учетом проданных, и сумму выигрыша по каждому туру, программу, которая бы позволяла самим определять правила розыгрыша для разных тиражей, наконец, программу, где бы учитывались выплаты по выигравшим билетам, велся бы учет комиссионных распространителей, хранилась информация по предыдущим тиражам… И на дессерт: в каждом тираже участвует от ста до пятисот тысяч билетов, которые могут быть проданы наполовину, на четверть, как угодно, в разнобой. Считайте: запись в справочнике в 20 байт, умножаем на 500000 записей, получаем мегабайт. А что такое, 20 байт? Это только наименование в 20 символов. А ведь надо вести учет по продажам, по розыгрышу. То есть один тираж грозился разростись в десятки и сотни мегабайт. В месяц же планировалось проводить по нескольку тиражей. База в пару гигобайт чистой информации, без индексных файлов за промежуток в пол года – это что-то, конечно. И если еще вспомнить, что одно из основных требований – это молниеносность статистики во время розыгрыша, то получается головоломка не хуже китайской: как это все организовать, чтобы работало быстро, надежно, да еще занимало мало места на диске.
Дебют длиною в месяц
Не знаю, кто бы из сегодняшних программистов взялся за такую задачу, сколько бы он запросил и как долго делал… Не знаю этого и про себя сегодняшнего. Но тогда был четко определен срок: один месяц. Я же провозился полтора, по поводу чего клиент не скрывал своего недовольства. А уж сколько денег я за это получил… Вспоминать сейчас стыдно, но дебют есть дебют.
Ключ боевой техники
Меня предупредили, что лотерейные билеты распространяются пачками по сто штук. Номер каждого билета в пачке состоит из номера серии (= пачки), скольких-то нулей и номера билета в пачке, начиная с первого, по сотый. Большой опыт написания на С++ разных трансляторов помог мне с выбором основной единицы хранения информации. Им должен стать 1 бит. Для чистых 1С-ников: любой символ представляет обычный байт информации, то есть, состоит из 8 бит. У любого билета есть 2 состояния: продан (1) или не продан (0). Таким образом, информацию о продаже 100 билетов я могу поместить в строку длиной 15 символов. Как? Просто: 15 * 7 = 105. Почему семь, а не весемь? Чтобы исключить из строки нули, которые 1С, как любой нормальный транслятор, воспринимает концом строки, я заполняю ее символами (2)00000001. И в каждый символ я теперь могу вписать информацию о продаже семи билетов. На пример, если были проданы первый, третий и пятый билеты, то первый быйт из 15-ти будет выглядеть, как (2)00101011. Чтобы указать, что был продан десятый билет, я должен буду сделать второй байт из пятнадцати таким: (2) 00001001 и т.д. И так, мой справочник тиража содержит два поля: код из 6 символов, что соответствует номеру серии и поля-строки в 15 символов, которое хранит информацию о продаже 100 билетов этой серии. В конце статьи я помещу процедуры работы с одной записью справочника.
Прессинг не запрещен
Вы скажете, что 5000 записей в справочнике с такой вот сжатой структурой данных по-прежнему, трудно обрабатывать. Согласен. Поэтому, я взял во внимание еще один факт: предполагается, что большинство билетов к моменту тиража будет распродано. Это соображение дало мне простое, но очень эффективное правило построения моего списка лотерейных билетов: предполагается, что если в справочнике отсутствует запись по какой-то серии, то это означает, что все билеты в ней проданы.
Почти микропроцессор
Кто немного знаком с технологией построения процессоров, тот знает, что большинство из них работают не с битами, а именно с байтами информации. Иными словами, чтобы получить разложение байта по битам, процессор должен сделаеть восемь последовательных операций «И»: Бит1=Байт&0xA0, Бит2=Байт&0x80 и т.д. Я тоже решил особо не мудрить и написал процедуры работы с байтом, основанные на похожей технологии.
Перем РазлБайта[8] Экспорт; Процедура РазложитьБайт(Знач Байт) Экспорт Если Число(Байт)>127 Тогда Байт= Байт - 128; РазлБайта[8]= 1; Иначе РазлБайта[8]= 0; КонецЕсли; Если Число(Байт)>63 Тогда Байт= Байт - 64; РазлБайта[7]= 1; Иначе РазлБайта[7]= 0; КонецЕсли; Если Число(Байт)>31 Тогда Байт= Байт - 32; РазлБайта[6]= 1; Иначе РазлБайта[6]= 0; КонецЕсли; Если Число(Байт)>15 Тогда Байт= Байт - 16; РазлБайта[5]= 1; Иначе РазлБайта[5]= 0; КонецЕсли; Если Число(Байт)>7 Тогда Байт= Байт - 8; РазлБайта[4]= 1; Иначе РазлБайта[4]= 0; КонецЕсли; Если Число(Байт)>3 Тогда Байт= Байт - 4; РазлБайта[3]= 1; Иначе РазлБайта[3]= 0; КонецЕсли; Если Число(Байт)>1 Тогда Байт= Байт - 2; РазлБайта[2]= 1; Иначе РазлБайта[2]= 0; КонецЕсли; РазлБайта[1]= Число(Байт); КонецПроцедуры Процедура УстановитьБит(Байт, Знач Бит, Знач Валуе) Экспорт РазложитьБайт(Байт); К= 0; Т= 1; Пока К<Бит Цикл Т= Т +Т; К= К + 1; КонецЦикла; Если (Валуе=1) и (РазлБайта[Бит+1]=0) Тогда Байт= Число(Байт) + Т; КонецЕсли; Если (Валуе=0) и (РазлБайта[Бит+1]=1) Тогда Байт= Число(Байт) - Т; КонецЕсли; КонецПроцедуры Функция ИзменитьБит(Байт, Знач Бит) Экспорт РазложитьБайт(Байт); К= 0; Т= 1; Пока К<Бит Цикл Т= Т +Т; К= К + 1; КонецЦикла; Если РазлБайта[Бит+1]=0 Тогда Байт= Число(Байт) + Т; РазлБайта[Бит+1]= 1; Иначе Байт= Число(Байт) - Т; РазлБайта[Бит+1]= 0; КонецЕсли; Возврат( РазлБайта[Бит+1]); КонецФункции Функция СложитьБайт() Экспорт Бит=1; Т=1; Рез=0; Пока Бит<=8 Цикл Если РазлБайта[Бит]=1 Тогда Рез=Рез+Т; КонецЕсли; Т= Т +Т; Бит= Бит + 1; КонецЦикла; Возврат Симв(Рез); КонецФункции
Теперь, чтобы узнать, был ли продан, скажем 34 билет какой-либо серии, мне достаточно найти в справочнике запись по этой серии, вычислить номер байта, содержащего информацию по нужному билету, разложить этот байт и прочитать значение нужного бита:
Функция БылЛиПродан(Серия, Номер) Экспорт Билеты=СоздатьОбъект(«Справочник.Билеты»); Если Билеты.НайтиПоКоду(Серия)=0 Тогда // Если серии нет, то она продана вся Возврат Перечисление.Булево.Да; КонецЕсли; НомерБайта=1+Цел(Номер/7); НомерБита=2+Номер%7; // помним, что первый – всегда 1 и не участвует в анализе Байт=Сред(Билеты.Номера,НомерБайта,1); РазложитьБайт(Байт); Возврат ?(РазлБайта[НомерБита]=1, Перечисление.Булево.Да, Перечисление.Булево.Нет); КонецФункции
То, что остается между строк
Разумеется, приведенный пример работы со справочником билетов является, как говорится, решением в лоб. Он лишь нагладно показывает технологию работы. Для достижения максимальной скорости работы описанный метод обращения к справочнику не подойдет, особенно, если программа работает в сети. Для того, чтобы все летало мы … Впрочем, это будет очередной из тысячи нюансов, использованных при создании той программы. А обо всем ведь, как известно, не напишешь.
Ленточки, шампанское и теория бума
Никогда не забуду тот день, когда моя программа впервые засияла на мониторах не только моего компьютера. На первый розыгрыш благоразумно не пригласили телевидение, впрочем, в гостях недостатка не было. Более того, розыгрыш проходил в стенах … нашего епархиального управления, и среди почетных гостей восседал сам Архиепископ. Похоже, администрация той конторы посчитала, что иерарх Церкви предстанет тем незаинтересованным лицом, которое подтвердит, что при розыгрыше не было ни каких подтасовок. Действительно, шары вынимались из закрытого мешка разными людьми, а девушка под моим чутким руководством вносила очередную цифру в мою программу, которая мгновенно (что и требовалось) выплевывала число выигравших билетов и сумму выигрыша очередного тура. Хочу заметить, что в нашей Вселенной действует, по видимому, еще не открытый, объективный закон, который гласит, что самые невероятные и неожиданные ошибки всплывают на этапе первой сдачи программы. Все шло хорошо, до тех пор, пока девушка-оператор вдруг вместо двойки, выпавшей на очередном шаре, не ввела по ошибке тройку. И поскольку на беду эта цифра оказалась последней в этом туре, то программа, не задумываясь, посчитала число выигравших (???) билетов, только с тройкой. Я же с ужасом понял, что не предусмотрел откат последней цифры. И правильно, на этапе тестирования мы не вынимали щары из мешка, а просто вводили цифры, подчас, спонтанно, но всегда без ошибок. А теперь возникла заминка, и я представил, что бы было, если бы наш миллионный город сейчас следил за работой моей программы по телевизорам, точнее, как я лезу в конфигуратор, что-то там исправляю (я ведь до этого отключил возможность переигрывания тура по просьбе заказчика!), как потом мы заново вбиваем всю информацию по уже выпавшим шарам. Благо – наша публика оказалась не притязательна. Точнее, всем особо не было дела до того, что там происходит за компьютером. Владыко не имел лотерейных билетов, и ему хотелось только одного, чтобы все это поскорее закончилось. Поэтому, на второй минуте паузы он начал беспокойно ерзать и спрашивать, когда, наконец, мы будем продолжать. И мы продолжили.
Пост что-то
После многих лет работы с 1С, я по-прежнему, рассматриваю тот свой первый проект, как удавшееся, законченное решение. Думаю, описанный метод компановки данных можно применять не только в программе по учету лотерейных билетов. На пример, ЖКХ, медицина... В этих сферах учета также справочники представляются достаточно объемными. Но с другой стороны, очень часто статистическая информация по ним может быть представлена двумя или несколькими ответами: да – нет, платил – не платил, прошел – не прошел и т.д. И в этом случае о представлении символа в качестве набора байтов можно вспомнить, чтобы все стало оптимально и быстро.
С работами, созданными по нашим секретным технологиям, можно ознакомиться в салоне продаж "Белка": Бизнес Электроника ( http://www.belkamag.ru ), либо на нашей домашней странице
____________________________________
НУ ШО, РЕЙТИНГ ПЛЮСАНЕМ, ИЛИ КАК???
www.infostart.ru и www.its-spb.ru