egorius: (Default)

Удивляюсь, почему в интерактивной среде? терминальном клиенте? интерпретаторе командной строки? — короче, в psql (и в sql*plus!) нет конструкций для циклов.

SQL, как и его процедурные расширения типа PL/pgSQL или PL/SQL, не могут напрямую общаться с пользователем; это правильно. А клиенты, типа psql или sql*plus, — наоборот. И действительно, оба умеют задать пользователю вопрос, узнать ответ и использовать его дальше в запросах. Но ни у одного нет даже примитивных конструкций для циклов, ну и для ветвлений. Да хоть goto какой-нибудь! Но нет.

А мне — для целей обучения — хотелось бы иметь возможность писать примитивные приложения прямо на psql, не привлекая другие языки программирования. Кто его знает, чем владеет человек (может студент, а может и школьник)? А с psql ему все равно придется разбираться. Но получается, что и скрипт написать можно, и интерактивным его сделать можно, но он всегда будет тупо линейным и никак иначе.

Короче, хочется уметь заскриптовать такой диалог:

сервер: Угадай число от 1 до 100?
человек: 42
сервер: Ни фига, 28. Играем еще?
человек: да
сервер: Угадай число от 1 до 100?
...

Или я чего-то не догоняю? Может есть какой-нибудь лайфхак?

egorius: (Default)

Удивительная штука, этот Постгрес.

Давным давно, прослушав в Универе курс Кузнецова по базам данных, напросился я стажером в Протвинский РДТеХ. Мне вручили талмуд «Oracle CASE*Method» про ER-диаграммы и моделирование, с которым я коротал время в электричках. А еще осваивал Oracle Designer/2000, и даже перевел с английского и прочитал учебный курс по этому чудо-продукту.

Разве мог я тогда предположить, что двадцать лет спустя РДТеХ будет читать наш курс по Постгресу?

А сколько городов необъятной родины довелось посетить за каких-то полгода, и в скольких еще предстоит оказаться? В ближайших планах Казань и Новосибирск.

Да и в самой Москве полно интересных мест. На конференции выступил (первый раз после Универа, ага). Предоставился повод навестить ВМК, а там суперкомпьютер «Ломоносов», от которого с улицы видна система охлаждения. Снова заглянул в Яндекс, где было прожито несколько сложных, но интересных лет. Опять же ГАИШ (будем себя хорошо вести, может и в телескоп дадут глянуть).

А ведь мог бы так и просидеть со своим OёBSом. Удивительная штука, эта жизнь.

egorius: (Default)

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

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

Дальше пока непонятно: идей разных много, а времени мало...

egorius: (Default)

Задумался тут. Оракл в 12c придумал супер-фичу pluggable databases, носится с ней и всем рассказывает, как это круто.

А тем временем в Постгресе один экземпляр СУБД как работал от рождения с несколькими базами данных, так и продолжает. Никого это не удивляет, все привыкли и принимают как должное.

Вот она, сила маркетинга-то.

egorius: (Default)

Столкнулся с проблемой: периодически комп наглухо подвисал на несколько минут, не реагируя ни на какие раздражители. Потом все рассасывалось и можно было работать до следующего затыка. По ощущением подвисание происходило в момент обращения к диску. Со временем проблема усугублялась: задержки выросли до 5 минут, а частота появления — до нескольких раз в час.

Ок, оптимизация производительности — штука универсальная. Надо составить профиль использования ресурсов, выявить слабое звено и придумать ему лекарство. Но, скажем, в Оракле для этого есть понятные инструменты (трассировки и динамические статистики), а вот как быть с виндоузом?

Правильный вопрос — уже половина ответа. Немного покопавшись, выяснилось следующее.

Старый добрый Task Manager. Вкладка Performance была единственным известным мне местом, где можно что-то посмотреть про производительность. Но если в XP это довольно бесполезная игрушка, которая рисует картинки и самые общие цифры, то в семерке там появилась кнопка, запускающая Resource Monitor, который в онлайне показывает весьма подробную информацию про процессоры, память, диски и сеть. Даже картинки там более интересные, например, видна длина очереди к диску.

Но что толку от онлайна, когда машина глухо виснет? Хочется посмотреть лог. Оказывается, теперь есть и такое счастье. Оно называется Performance Monitor, а добраться до него можно разными путями, например, правый клик на «моем компьютере» – Manage – System Tools – Performance. Как и в Оракле, в его распоряжении трассировка событий и счетчики (кто бы мог подумать). Порядок работы такой. Сначала настраиваем Data Collector Set, где определяем, какие трассы и счетчики нам нужны. Я не мудрил и воспользовался готовым набором, только увеличил время с одной до пятнадцати минут. Затем запускаем сбор данных и нагружаем систему. После этого смотрим сформированный отчет и делаем выводы.

Увиденное соответствовало субъективным ощущениям. С памятью и процессором все было неплохо, а вот диск «порадовал»:

  • Avg. Disk Queue Length: mean = 11
  • Disk Bytes/sec: max = 45 811 759
  • Avg. Disk sec/Read: mean = 0.055
  • Avg. Disk sec/Transfer: mean = 0.072
  • Avg. Disk sec/Write: mean = 0.242
  • Physical Disk Percent Idle Time: 0.682

По-русски, запросы к диску выстраиваются в длинные очереди (средней длины 11), диск молотит без передыху (меньше 1 % незанятого времени), время доступа велико, а диск справляется с 45 MB в секунду.

Итак, слабое звено понятно. В свое время купил Western Diginal Caviar Green (5400 rpm) — и прогадал.

Как лечить? Ну, во-первых, можно попробовать сократить нагрузку. На свежепереустановленной системе все работает прилично, проверял. Но со временем процессов становится все больше и диск начинает захлебываться, а заниматься периодической прополкой установленных программ и мониторингом процессов я как-то не готов.

Во-вторых, можно построить RAID 0, благо дисков две штуки одинаковых. Но ну его нафиг возиться.

В-третьих, можно купить более производительный винт, что и было в итоге проделано. Вот что показывает отчет для нового двухтерабайтного Seagate Desktop на похожей нагрузке:

  • Avg. Disk Queue Length: mean = 1
  • Disk Bytes/sec: max = 83 032 295
  • Avg. Disk sec/Read: mean = 0.003
  • Avg. Disk sec/Transfer: mean = 0.003
  • Avg. Disk sec/Write: mean = 0.011
  • Physical Disk Percent Idle Time: 59

Эти цифры нравятся мне гораздо больше.

* * *

Открыл для себя Speedfan и отказался от штатной асусовской EPU4 Engine в его пользу. Утилитка умеет показывать температуру чипов и дисков, читать параметры S.M.A.R.T. и управлять частотой вращения кулера. Правда, к ней надо приноровиться: заточить конфигурацию под свою маму (можно получить с сайта автора) и суметь зарядить в автозапуск (получилось только с помощью scheduler-а). Зато потом можно забыть.

* * *

Ну и заодно добавил оперативной памяти до 6 GB, чтобы спокойно держать открытыми виртуалку с макосью, фотошоп, пикасу, браузер, и что там еще взбредет в дурную голову. Теперь у меня две ненужные планки DDR2 по гигабайту, пристроить бы куда.

* * *

Видимо, это последний апгрейд, потому что DDR2 уже уступил место DDR3. Ну хоть SATA 3 совместим с SATA 2, и на том спасибо. Дальше придется менять маму, а с ней наверняка и все остальное.

egorius: (Default)

Г. М. Адельсон-Вельский, В. Л. Арлазаров, А. Р. Битман, М. В. Донской, «Машина играет в шахматы» (1983 г.)

Авторы «Каиссы» делятся опытом и соображениями о том, какие алгоритмы лежат или могут лежать в основе шахматной программы. По большому счету, книга о борьбе за оптимизацию перебора. Для обычных шахматистов она, конечно, неинтересна, но для программистов-шахматистов с археологическим уклоном представляет интерес. В конце концов, у оптимизации перебора вариантов и оптимизации запроса к базе данных можно усмотреть нечто общее.

Любопытно, что про альфа-бета-отсечение нам на ВМК рассказывали, но имя Александра Брудно при этом не упоминалось, хотя авторы утверждают, что это его изобретение (есть мнение, что к нему причастны многие). Ну или взять программирование в содержательных обозначениях того же Брудно: не помню, чтобы об этом хоть что-то говорилось, хотя идея сугубо практичная и полезная. Зато говорили про операторный метод Ляпунова, а кто применял его на практике? Говорили про историю вычислительной техники, но не затрагивали Бессонова с его релейной машиной. Такое ощущение, что разные школы и разные миры, и железный занавес между ними. А может, мне просто кажется.

Пара вырезок на память:

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

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

...

Наш опыт показывает, как часто поиск ошибок в программе приводит к признанию ее правоты: просто то, что на первый взгляд кажется ошибкой, при внимательном изучении оказывается необходимым следствием установок программистов, которые они собирались реализовать. Известным примером служит партия «Дачесс»—«Каисса»... «Каисса» сыграла..., отдавая ладью, после чего, естественно, проиграла партию. После часа исследования в поисках ошибки она сумела доказать, что была права, показав в ответ на другие ходы мат при помощи красивой комбинации...

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

Если не ошибаюсь, Кайт писал, что всевозможные средства трассировки Оракла съедают чуть не 10 % времени его работы. Но без них «последняя была бы невозможной».

Александр Марков, «Рождение сложности»

Берем картину мироздания, да! И тупо смотрим, что к чему. (вместо эпиграфа)

Книга про реалии современной биологии. Без знаний в органической химии и генетике читать сложно, но все равно безумно интересно. Все оказалась намного хитрее и забавнее, чем мне дилетантски представлялось. Вот и автор признается:

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

...

Это характерный «почерк» эволюции, совсем не похожий на разумное проектирование, а похожий скорее на самосборку чего получится из чего попало.

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

egorius: (Default)

Иерархии традиционно вызывают много вопросов. Обычно довольно простых, например, связанных с порядком выполнения частей запроса, особенно когда иерархия строится над несколькими таблицами. Но встречаются задачи и поинтереснее.

Предметная область состоит из сотрудников, каждый из которых работает в некотором отделе. Отделы образуют оргструктуру, то есть иерархию. Некоторым (но не всем) отделам приписан адрес.

      1   <-- адрес A
     / \
    2   3   <-- адрес B
       / \
      4   5   <-- адрес C

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

Например, для картинки выше, адреса отделов должны быть такими:

DEP_ID ADDRESS 
------ ------- 
     1 A       
     2 A       
     3 B       
     4 B       
     5 C       

Задача состоит в написании запроса, который выведет всех сотрудников с указанием их отделов и адресов.

Вот тестовые данные.

create table dep(
  id        number primary key,
  address   varchar2(1000)
);
create table dep_hier(
  parent_id references dep(id),
  child_id  references dep(id)
);
create table emp(
  id        number primary key,
  dep_id    number references dep(id)
);
insert into dep values(1, 'A');
insert into dep values(2, '');
insert into dep values(3, 'B');
insert into dep values(4, '');
insert into dep values(5, 'C');
insert into dep_hier values(1, 2);
insert into dep_hier values(1, 3);
insert into dep_hier values(3, 4);
insert into dep_hier values(3, 5);
insert into emp values(101, 1);
insert into emp values(102, 2);
insert into emp values(103, 3);
insert into emp values(104, 4);
insert into emp values(105, 5);
commit;

Поехали )

egorius: (Default)

А дядюшка, ракетчик-офицер,
Расскажет про язык военный Ada.
«От C к C++»

 

Из каких-то соображений Oracle хранит молчание про появление и развитие языка PL/SQL. Редкие источники, в которых можно найти что-то вразумительное про технологическую историю компании, обходят этот вопрос стороной. Особенно это огорчает на фоне неплохо документированной истории языка SQL.

Достоверно известно немногое. Язык впервые появился в версии 6 (1988 год); в версии 7 (1992 год) с реализацией хранимых процедур его поддержка стала полноценной:

Ken Jacobs: In Oracle 6, we dealt with stored procedures. The kernel, the engine, would execute a PL/SQL procedure, but it didn’t have the ability to store it. So, you would have client side programs that would create these packages of multiple statements with procedure logic around them and submit it to the database as an executable unit. And, so stored procedures came in version 7, but PL/SQL was in version 6.
RDBMS workshop: Oracle, p. 26

Прообразом для PL/SQL послужила Ада — «официальный» язык МО США. Почему? Возможно, таково было пожелание госзаказчиков, однако точной информации на этот счет нет. Сам факт родственных связей очевиден и из простого сравнения двух языков, и заявлен в PL/SQL Language Reference, Appendix C:

PL/SQL is based on the programming language Ada. As a result, PL/SQL uses a variant of Descriptive Intermediate Attributed Notation for Ada (DIANA), a tree-structured intermediate language. It is defined using a metanotation called Interface Definition Language (IDL). DIANA is used internally by compilers and other tools.

Как видим, языки похоже не только внешне: транслятор PL/SQL переводит код в промежуточное представление DIANA — атрибутированное синтаксическое дерево, — которое было разработано в начале 1980-х для использования компиляторами Ады. Это наводит на мысль, что в основу реализации PL/SQL был положен уже готовый компилятор Ады.

 

Другой артефакт, позволяющий строить догадки, хорошо спрятан на самом видном месте — пакет standard. Кажется, первым на него обратил внимание Пит Финниган: он заметил, что булевский тип объявлен в standard как

type BOOLEAN is (FALSE, TRUE);

В PL/SQL такой конструкции нет, зато она есть в Аде. Пит сделал вывод о том, что Oracle реализовал перечислимые типы «невзаправду»:

Why do Oracle use syntax available to them only in the STANDARD package and not available to us? — well, my educated guess would be that they have only implemented this syntax in a very narrow way, i. e. to fulfill a  particular case and not much more. They must have made sure it compiles the BOOLEAN correctly but not tested or implemented much else hence we cannot use it.
Undocumented Oracle — Using ENUM's in PL/SQL

На мой взгляд, Пит ошибается. На самом деле это еще один аргумент в пользу того, что разработчики PL/SQL имели на руках готовый компилятор Ады: какой смысл в поддержке перечислимых типов ради одного только boolean? Хватило бы числового типа и немного хардкода.

А в том, что произвольные перечислимые типы без проблем работают, легко убедиться самостоятельно, ну например:

type color is (red, green, blue);

В обычном пакете получим PLS-00505: User Defined Types may only be defined as PLSQL Tables or Records, а внутри standard это компилируется и потом прекрасно работает, включая (как и полагается в Аде) функции сравнения и вхождения:

SQL> set serveroutput on
SQL> declare
   2    c color;
   3  begin
   4    c := red;
   5    dbms_output.put_line(case when c < green then 'yes' else 'no' end);
   6    dbms_output.put_line(case when c in (blue,green) then 'yes' else 'no' end);
   7  end;
   8  /
yes
no

PL/SQL procedure successfully completed.

К слову, скомпилировать standard просто так не получится: полезут блокировки и внутренние ошибки, в результате чего база загнется чуть менее, чем насмерть. Для 9i (и, по слухам, 10g) можно поднять базу в режиме migrate (это отключает триггеры), скомпилировать пакет и перекомпилировать инвалидные объекты:

$ sqlplus / as sysdba
SQL> shutdown abort
SQL> startup migrate
SQL> @stdspec
SQL> @?/rdbms/admin/utlrp

Для 11g это уже не прокатило, как обходиться с ней — не знаю.

Итак, зачем Ораклу поддержка перечислимых типов? Напомню, что в версии 6 была переписана примерно половина СУБД:

Jacobs: Version 5 was pretty successful but it had some serious problems. It still had table-level locking. It had no real scalability. You didn’t need it with table locking. You couldn’t do much anyway. So we set out in about 1986 and made a fundamental decision to rewrite half of the product. We threw away, and literally deleted the directories for the lower half of the database. We kept the SQL layer but re-architected the process model, the storage format, the logging, the locking, the multi-threadedness.
RDBMS workshop: Oracle, p. 19

На фоне этих изменений появление PL/SQL несколько меркнет. Были ли у компании силы делать в это время лишнюю работу? Сомнительно. Скорее, сил как раз не хватало, ведь хранимых процедур пришлось ждать еще четыре года.

 

Ну хорошо, реализация перечислимых типов не выглядит очень сложной, могли и сделать. Однако заглянем внутрь standard чуть глубже:

type VARCHAR2 is NEW CHAR_BASE;

Что такое new? В PL/SQL такого нет, он будет ругаться PLS-00504: type CHAR_BASE may not be used outside of package STANDARD, а если заменить тип, скажем, на number, то PLS-00103: Encountered the symbol "NUMBER" when expecting one of the following...

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

Если эта возможность достается даром (вместе с компилятором Ады), то почему бы ей не воспользоваться? Но реализовывать концепцию производных типов с нуля, если они не нужны в языке? Не верю.

Кстати, попытка объявить аналогичный тип varchar3 внутри standard не увенчалась успехом. Standard компилируется без проблем, но использовать новый тип невозможно:

SQL> declare
   2    v3 varchar3(100);
   3  begin
   4    null;
   5  end;
   6  /
   v3 varchar3(100);
      *
ERROR at line 2:
ORA-06550: line 2, column 6:
PLS-00566: type name "VARCHAR3" cannot be constrained

Если же не указывать ограничение, то код компилируется, но с грохотом падает при выполнении:

SQL> declare
   2    v3 varchar3;
   3  begin
   4    null;
   5  end;
   6  /
declare
*
ERROR at line 1:
ORA-06550: line 0, column 0:
PLS-00801: internal error [74402]

 

Самый убойный аргумент нашелся почти случайно в форуме. В 9i (а судя по написанному, и в 10g) без ошибок компилируется такой код:

SQL> create or replace package test as
  2    procedure set_n(x number);
  3  private
  4    n number;
  5  end;
  6  /

Package created.

Что такое private в спецификации? В Аде это слово позволяет описать данные, к которым нельзя обратиться непосредственно — это механизм для создания абстрактных типов. А вот в PL/SQL развесистую систему типов Ады существенно упростили и в нем слово private лишено всякого смысла.

Вообще система типов — тема для отдельного поста, но откуда private попал в PL/SQL, как не из компилятора Ады? Если бы язык писали с нуля, в него не стали бы добавлять абсолютно ненужную конструкцию.

Еще интересный момент: приведенный выше код компилируется, но не работает. Если добавить тело и попробовать вызвать процедуру (или обратиться напрямую к переменной), получим:

SQL> create or replace package body test as
  2    procedure set_n(x number)
  3    is
  4    begin
  5      n := x;
  6    end;
  7  end;
  8  /

Package body created.

SQL> begin
  2    test.set_n(42);
  3  end;
  4  /
begin
*
ERROR at line 1:
ORA-03113: end-of-file on communication channel

Возможно, внутренняя ошибка возникает из-за конфликта старой и новой модели сокрытия данных, а из парсера private просто забыли убрать (в 11g это уже подчистили).

 

Итак, мое мнение состоит в том, что для PL/SQL изначально был взят код компилятора Ады. Часть возможностей вырубили топором, часть присыпали листвой (из-под которой, как мы видели, кое-что просвечивает), и уже на этом готовом фундаменте вырастили новый транслятор.

 

P. S. для пытливых умов. С Адой у Oracle также связаны такие продукты, как Ada*Precompiler (в седьмой версии) и SQL*Module (в восьмой). Оба они, по всей видимости, успешно загнулись, однако идея использовать Аду для доступа к данным не умирает.

egorius: (Default)

Нередки случаи, когда сама постановка задачи подталкивает разработчика к процедурному решению. Вот пример из практики. В кадровый отчет по сотруднику требуется выводить тип родства, ФИО и дату рождения ближайшего родственника (например: «Отец — Эпиктетов Полуэкт Полуэктович, 01.04.1900»).

Модель данных упрощенно выглядит следующим образом. Есть таблица people с информацией о людях (как сотрудниках, так и их родственниках):

create table people(
  person_id         number,
  employee_flag     varchar2(1) check(   employee_flag = 'Y' -- yes
                                      or employee_flag = 'N' -- no
                                     ),
  full_name         varchar2(240),
  marital_status    varchar2(1) check(   marital_status = 'M' -- married (or null)
                                     ),
  sex               varchar2(1) check(   sex = 'M' -- male
                                      or sex = 'F' -- female
                                     ),
  date_of_birth     date
);

И есть таблица, связывающая людей с родственниками (связь односторонняя):

create table contact_relationships(
  person_id         number,
  contact_person_id number,
  contact_type      varchar2(1) check(   contact_type = 'C' -- child
                                      or contact_type = 'P' -- parent
                                      or contact_type = 'S' -- spouse
                                     ) 
);

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

Если в поле «Статус» = Состоит в зарегистрированном браке и Отношение = Супруг(а), то выводить данные супруга\супруги.
Если в поле «Статус» значение отличное от значения «Состоит в зарегистрированном браке», то выводим в отчет родственника в зависимости от приоритета.
Сначала проверяется наличие детей (Тип отношения = ребенок), причем если детей несколько, то выводить самого старшего.
Если детей нет, то выводить мать (тип отношения = родитель, пол = жен.)
Если матери нет, то выводить отца (тип отношения = родитель, пол = муж.).

Алгоритм определения ближайшего родственника сформулирован в терминах «если-то» и напрашивается процедурное решение. Напишем функцию, получающую person_id сотрудника и возвращающую... что? Логично было бы вернуть запись из трех полей (тип родства, ФИО и дата рождения), но что с ней потом делать в запросе? Можно написать три отдельных функции, а чтобы не ухудшать в три раза производительность, приделать кэширование. Но не будем усложнять: пусть функция будет одна и пусть она возвращает конкатенацию полей. Она могла бы выглядеть следующим образом:

запасаемся тухлыми помидорами )
egorius: (Default)

Вот еще задача из реальной жизни: округление копеек. Путь имеется отчет, показывающий какие-то денежные показатели; суммы надо выводить с точностью до копеек.

Для наглядности возьмем что-нибудь совсем тривиальное. Например, распределим общие затраты (расходы на электроэнергию) на все подразделения компании пропорционально их численности:

with depts(dept_id, quantity) as (
  select 1, 300 from dual union all
  select 2, 300 from dual union all
  select 3, 100 from dual union all
  select 4, 100 from dual union all
  select 5, 100 from dual
),
expenditures(amount) as (
  select 1000 from dual
),
report(dept_id,amount) as( 
  select   d.dept_id,
           e.amount * ratio_to_report(d.quantity) over ()
  from     depts d,
           expenditures e
)
select   dept_id,
         round(amount,2) amount
from     report;

DEPT_ID AMOUNT 
------- ------ 
      1 333,33 
      2 333,33 
      3 111,11 
      4 111,11 
      5 111,11

Какая досада, копеечка-то потерялась! Бухгалтер, работающий с РСБУ, этого не переживет.

Чтобы получилось правильно, надо учесть ошибки округления. Обычно их собирают вместе и добавляют к строке с максимальной суммой.

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

declare
  total     number;
  new_total number;
  max_dept  number;
begin
  select   round(sum(amount),2)
  into     total
  from     tmp;

  update   tmp
  set      amount = round(amount,2);
  
  select   sum(amount)
  into     new_total
  from     tmp;

  if total = new_total then
    return;
  end if;

  select   dept_id
  into     max_dept
  from     tmp
  where    amount = (
             select max(amount) from tmp
           )
       and rownum = 1;
       
  update   tmp
  set      amount = amount + total - new_total
  where    dept_id = max_dept;
end;
/

А одной командой SQL? )
egorius: (Default)

Посмотрим на пример из жизни. В разных вариациях он встречался мне раза три как минимум.

Пусть имеется некое подобие складского учета, в котором ведутся только транзакции поступления позиций на склад и выбытия позиций со склада. Атрибутами транзакций являются:

  • собственно позиция (item_id) — стол офисный, кадка с фикусом и т. п.
  • стоимость одной штуки (cost)
  • количество штук (quantity) — положительно для поступлений, отрицательно для выбытий
  • дата (trx_date)

При этом так получилось, что для поступлений мы знаем стоимость позиций (сами же покупали), а для выбытий — нет (кладовщик выдает первую попавшуюся подходящую позицию; неизвестно, когда и по какой стоимости она закупалась).

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

На примере (для одной позиции):

  1. Поступление 5 штук по цене 1001 руб.
  2. Поступление 4 штук по цене 1010 руб.
  3. Выбытие 3 штук
    ценапоступиловыбылоосталось
    1001532
    1010404
  4. Выбытие 3 штук
    ценапоступиловыбылоосталось
    100153+20
    1010413
  5. Поступление 2 штук по цене 1100 руб.
    ценапоступиловыбылоосталось
    100153+20
    1010413
    1100202

Итак, отчет должен показать 3×1010 + 2×1100 = 5230.

Лирическое отступление. Числа в примерах лучше подбирать так, чтобы по результату можно было сразу сказать, откуда он взялся. 5230 читается справа налево: осталось 0 от первого поступления, 3 от второго и 2 от третьего, 5 — контрольная цифра. А вот попробуйте понять, что такое 2000 при цене поступлений 1000 и 500.

Процедурное решение вполне очевидно. Например, можно сделать так:

create table transactions(
  item_id     number,
  quantity    number,
  cost        number,
  trx_date    date
);

create or replace type trx_rec is object(
  item_id     number,
  quantity    number,
  cost        number
);
/
create or replace type trx_tab is table of trx_rec;
/

-- транзакции из примера
insert into transactions values(1, +5, 1001, to_date('01.02.2014','DD.MM.YYYY'));
insert into transactions values(1, +4, 1010, to_date('02.02.2014','DD.MM.YYYY'));
insert into transactions values(1, -3, null, to_date('05.02.2014','DD.MM.YYYY'));
insert into transactions values(1, -3, null, to_date('07.02.2014','DD.MM.YYYY'));
insert into transactions values(1, +2, 1100, to_date('09.02.2014','DD.MM.YYYY'));
-- добавим еще вторую позицию
insert into transactions values(2, +3,  300, to_date('03.02.2014','DD.MM.YYYY'));
insert into transactions values(2, -1, null, to_date('04.02.2014','DD.MM.YYYY'));

set serveroutput on
declare
  trx trx_tab := trx_tab();
  qty_so_far number;
begin
  -- читаем поступления в массив (в порядке дат)
  for i in (
    select   t.item_id,
             t.quantity,
             t.cost
    from     transactions t
    where    t.cost is not null
    order by t.trx_date
  )
  loop
    trx.extend;
    trx(trx.count) := trx_rec(i.item_id, i.quantity, i.cost);
  end loop;
  -- обработка выдач
  for i in (
    select   t.item_id,
             sum(t.quantity) quantity
    from     transactions t
    where    t.cost is null
    group by t.item_id
  )
  loop
    qty_so_far := -i.quantity;
    for n in 1 .. trx.count loop
      if trx(n).item_id = i.item_id and trx(n).quantity > 0 then
        if trx(n).quantity >= qty_so_far then
          trx(n).quantity := trx(n).quantity - qty_so_far;
          exit;
        else
          qty_so_far := qty_so_far - trx(n).quantity;
          trx(n).quantity := 0;
        end if;
      end if;
    end loop;
  end loop;
  -- результат
  for i in (
    select   t.item_id,
             sum(t.cost * t.quantity) cost
    from     table(trx) t
    group by t.item_id
    order by t.item_id
  )
  loop
    dbms_output.put_line(i.item_id||': '||i.cost);
  end loop;
end;
/

Небольшая оптимизация состоит в том, чтобы списывать сразу общую сумму, а не возиться с каждым выбытием отдельно. Чтобы не засорять код сверх меры, я все-таки перепоручил заключительную часть — группировку сумм по позициям  — SQL, обратившись к коллекции как к таблице (select from table()).

А как сделать на чистом SQL? )
egorius: (Default)

А вот попробуем решить на SQL какую-нибудь типично процедурную задачу. Скажем, поиск пути в лабиринте, чтобы было не скучно.

Для этого придумано много разных алгоритмов, один другого лучше. Для примера сошлюсь на волновой алгоритм; там и код программы приведен: циклы, циклы и еще раз циклы.

Как подойти к проблеме с реляционной стороны? Во-первых, лабиринт надо представить так, как мы делали в прошлый раз с матрицей: в виде отношения, содержащего номер строки, номер столбца и значение в ячейке.

create table maze_relation(
  linenum number,
  colnum  number,
  cell    varchar2(1)
    check(   cell = ' ' -- пусто
          or cell = '#' -- стена
          or cell = 's' -- начало
          or cell = 'e' -- конец
         )
);

Залог успеха — формулировка решения в терминах множеств. Итак, начинаем с множества начальных ячеек. Далее действуем рекурсивно: на каждом следующем этапе добавляем ячейки, соседствующие с уже имеющимися в множестве. Из полученных вариантов нас будет интересовать тот, в котором содержится конечная ячейка, и у которого наименьшая длина пути.

Такого рода рекурсивные алгоритмы прекрасно соответствуют иерархическим запросам SQL. Условия первой итерации попадают во фразу start with, условия следующих итераций — в connect by (с указанием nocycle, чтобы алгоритм не ходил кругами). Отбор подходящих вариантов задается в where. Восстановление пути волнового алгоритма превращется в простой вызов функции sys_connect_by_path: если представить иерархический запрос как рекурсивный алгоритм, то эта функция возвращает не что иное, как стек возвратов.

with solutions as (
  select  sys_connect_by_path(m.linenum||','||m.colnum,' - ') path,
          level len
  from    maze_relation m
  where   m.cell = 'e'
  start with
          m.cell = 's'
  connect by nocycle
          m.cell != '#'
      and (
            (m.linenum = prior m.linenum + 1 and m.colnum = prior m.colnum) or
            (m.linenum = prior m.linenum - 1 and m.colnum = prior m.colnum) or
            (m.linenum = prior m.linenum     and m.colnum = prior m.colnum + 1) or
            (m.linenum = prior m.linenum     and m.colnum = prior m.colnum - 1)
          )
)
select  min (path) keep (dense_rank first order by len)
from    solutions;

Получив в подзапросе solutions все пути, мы отобраем из них самый короткий. Это можно сделать с подзапросом, а можно воспользоваться keep dense_rank. Смысл конструкции, изрядно затуманенный синтаксисом, прост: отобрать значение path, соответствующее минимальному значению len (если таких будет несколько, останется только одно благодаря min).

Собственно, все. По-моему, весьма поучительно сравнить объем кода с тем же волновым алгоритмом.

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

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

Но развлекаться — так развлекаться. Давайте сделаем решение этой задачи наглядным!

продолжаем разговор )
egorius: (Default)

Часто приходится видеть, как разработчик, столкнувшись с задачей, первым делом берется за привычный процедурный инструмент, даже не вспомнив про мантру Кайта:

You should do it in a single SQL statement if at all possible.

Откуда такая мантра? Во-первых, процедурный подход оперирует циклами, а SQL — множествами. База данных может работать с множествами на порядок эффективнее, чем с помощью циклов; это лишь один способ из целого арсенала, которым располагает СУБД. Во-вторых, декларативный подход описывает желаемый результат, а процедурный — точный способ достижения этого результата. Поэтому декларативная программа зачастую оказывается короче и проще.

Почему же предпочтение отдается PL/SQL? Тут можно было бы порассуждать об эффективности, но, на мой взгляд, реальная причина проще: декларативный подход требует смены парадигмы программирования, а это дается нелегко.

Хочу начать эту серию заметок с простого примера, эффективно взрывающего процедурно настроенный мозг, а именно с умножения матриц.

Напомню, что произведением матрицы A(L×M) на матрицу B(M×N) является матрица С(L×N), элементы которой ci,j = Σk = 1...M  ai,k×bk,j. Для иллюстрации процедурного подхода возьмем следующие определения (я использовал язык Си):

int a[L][M];
int b[M][N];
int c[L][N];

Алгоритм традиционен и хорошо иллюстрирует вышеизложенную мысль про циклы:

int i, j, k;
for (i=0; i<L; i++)
  for (j=0; j<N; j++)
    for (k=0; k<M; k++)
      c[i][j] += a[i][k] * b[k][j];

А сможете ли вы сделать это на SQL? )
egorius: (Default)

Развлекался тут с Advanced Queueing и нашел чудесную окаменелость.

Dequeue Options

navigation
The navigation attribute specifies the position of the dequeued message. If FIRST_MESSAGE is specified, then the first available message matching the search criteria is dequeued. If NEXT_MESSAGE is specified, then the next available message matching the search criteria is dequeued (the default).

Проявляется это, например, если пытаться выбирать сообщения из очереди, прикидываясь разными подписчиками:

ORA-25242: cannot change subscriber name from SUBSCRIBER1 to SUBSCRIBER2 without FIRST_MESSAGE option
*Cause:    An attempt was made to change the subscriber name while using the
           NEXT_MESSAGE or NEXT_TRANSACTION option for dequeuing.
*Action:   To use a subscriber name that is different from the previous
           dequeue call, reset the dequeuing position by using the
           FIRST_MESSAGE navigation option.

Ведь что получается? В недрах реляционки сидит, особо не скрываясь, пережиток навигационных баз данных и заставляет нас думать в терминах FIND FIRST, FIND NEXT!

Будь я постарше, мог бы испытать примерно то же, что испытал однажды, сохраняя файл на КПК. А так просто в тихом шоке.

пощупать исходный код )
egorius: (Default)

Лео Багров, «История картографии»

Меня картография в основном интересует с художественной стороны и с точки зрения представления информации (Тафти приводит карты как пример визуального представления большого объема информации на ограниченной площади). К сожалению, в этой книге автор предпочел показать общий вид карт, пожертвовав деталями. Тем не менее, и исторический аспект тоже оказался весьма интересен.

Когда-то у нас был атлас автодорог, в котором каждая трасса была показана в виде длинной ленты, нарезанной на части. Оказывается, такой тип карт известен с древних времен под именем Пейтингеровой таблицы.

Еще один занятный момент связан с картиной Гольбейна, которая названа в тексте «Посольство к Великому Моголу, 1314 г.»:

Небольшая видимая часть изображенного глобуса содержит анахронизм: очертания Африки на глобусе относятся ко времени на полтора столетия позже изображенного на картине события.

Проблема в том, что картина называется «Послы», а не «Посольство к Великому Моголу», и изображает не «событие», а французских послов при дворе английского короля Генриха VIII. Так что никакого анахронизма тут нет, Гольбейн изобразил современный ему глобус, возможно, Иоганна Шенера, изготовленный ок. 1530 г., как указывает автор, поскольку картина датируется 1533 годом. К тому же, империя Великих Моголов существовала с XVI века, так что в 1314 году никакого посольства не могло быть в принципе. Так одна фраза может испортить впечатление от всей книги.

Занятно, что про ту же картину и тот же глобус писал и Джон Бергер в «Искусстве видеть»:

Глобус на нижней полке — новый, учитывающий ... путешествие Магеллана. Гольбейн также разместил на глобусе название поместья во Франции, принадлежащего послу слева. Рядом с глобусом положены книга по арифметике, псалтырь и лютня. Чтобы колонизировать новую землю, нужно было обратить ее жителей в христианство и научить счетоводству, доказав им таким образом, что европейская цивилизация — самая передовая в мире.

Спрашивается, почему французские послы в Англии должны быть озабочены колонизацией? Вывод, по-моему, прост и банален: люди видят ровно то, что хотят увидеть.

Дональд Кнут, «Сюрреальные числа»

«Бином» ни с того, ни с сего издали книгу 1974 года про сюрреальные числа. Это произведение (как и все, о чем пишет Кнут) написано живо и нескучно, но читать его надо (как и все, о чем пишет Кнут) с ручкой и бумагой. Может быть, на пенсии...

Кстати, текст книги, как ему и положено, сверстан в TeXе, а вот с обложной биномовцы облажались:

Если бы не поленились заглянуть во «Все про TeX», то узнали бы, что эмблема TeXа определена в нем как
\def\TeX{T\kern-.1667em \lower.5ex\hbox{E}\kern-.125em X},
иными словами, буква E сдвинута вниз и влево (это называется кернингом), но сама она того же кегля, что и остальные буквы:

Ну а про эмблему Метафонта я вообще молчу.

Кстати, теперь уже мало кто помнит, а ведь в свое время название RDTeX гордо подчеркивало связь фирмы с первым изданием Кнута и использованием TeXа для набора переводимой оракловой документации. Не то, что нынешний безликий «РДТЕХ».

egorius: (Default)

Владимир Кричевский, «Идеальный дизайн»

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

Джо Селко, «Стиль программирования на SQL»

C одной стороны, любой труд, призывающий мыслить на SQL в терминах множеств, а не циклов, и обращающий внимание на важность типографики в программировании — безусловное благо.

C другой — слишком во многом мы расходимся с автором, чтобы безоговорочно рекомендовать эту книгу. Например, мне совершенно не близка мотивация писать переносимый код; здесь я принимаю сторону Тома Кайта: если уж заплатили за СУБД, глупо не пользоваться всеми ее возможностями. Тем более, что наезды автора на Оракл выдают в нем человека, с Ораклом не работавшего (а наезжает он и на другие коммерческие системы тоже).

В общем, читать можно, но осторожно.

Карл Левитин, «Прощание с Алголом» (1989)

Вот академик Андрей Петрович Ершов. Как же, каюсь, не любил я в детстве эту фамилию! Во-первых, из-за школьного алгоритмического языка. Не понимал тогда, да и сейчас не понимаю, зачем переводить Паскаль на русский язык. Были и до этого попытки, вот скажем Эль-76, автокод Эльбруса, тоже базируется на русском, но то были другие времена и нам было еще чем гордиться. Во-вторых, из-за информатики, с 1985 года усилиями Ершова введенной в школьную программу. Не понимал тогда, да и сейчас не понимаю, зачем понадобилось это слово. Вот передо мной три учебника информатики 1988–91 годов. Один объявляет, что компьютерная грамотность — это умение читать и писать, считать и рисовать, а также искать информацию, применяя для этого ЭВМ, после чего обрушивает на бедного школьника материал, по широте охвата не уступающий программе ВМК, от p-n-переходов до программирования на Прологе. Другой не мудрствует: вот блок-схемы, вот расчет на калькуляторе, а вот — на Бейсике. Третий пытается быть авторитетным и школьник узнает из него, что кэш-память — это разновидность стека, а Паскаль — Philips Automatic Sequence CALculator. Хаос, полный хаос царит в голове горе-информатиков. Они понимают, что уметь программировать нужно не всем, но что такое компьютерная грамотность без программирования — не понимают. А что понимают современные учителя информатики, это мне страшно даже представить.

Вот конференция «Диалог человек — ЭВМ», которая проводилась в 1983 году у нас в Протвино. А ведь у меня на полочке стоит сборник материалов, папа принес в свое время из Института. Кого только не было на этой конференции, не считая «компьютерщиков»: дизайнеры, социологи, биологи, психологи... Тихомиров из Универа: Существенным является разделение потребностей пользователя на предусмотренные разработчиком и непредусмотренные... Ко второму классу относятся многие разновидности потребностей в общении: потребность в соревновании с другими пользователями..., потребность в уважении коллег..., потребность быть членом социальной общности. Нынешние веб-разработки эксплуатируют это наблюдение по полной. Но ведь в целом концепция диалога с ЭВМ как обмена сообщениями на естественном языке полностью провалилась. Ершов предполагал, что удастся осилить хотя бы деловую переписку, канцелярит, но и это не удалось. Вместо этого диалог превратился в клацанье мышью — интересно, в Xerox PARC психологи тоже работали? Приведу еще цитату из книги Льва Николаевича Королева: Стремление упростить взаимоотношения пользователя с PC привело к созданию ... оболочки, в которой текстовое меню в основном заменено графическими символами... Вообще, наблюдающийся переход от текстов к иероглифам весьма симптоматичен и интересен с философской точки зрения.

Вот всплывают разные знакомые имена. В 1979 году в Узбекистане, на родине Аль-Хорезми, проводился симпозиум «Алгоритм в современной математике и ее приложениях». Инициатива принадлежит Ершову и... Дональду Кнуту — оказывается, они были знакомы. А на заре вычислительной техники к компьютерам Ершова привел Евгений Андреевич Жоголев — он вел у на курс технологий программирования. Или промелькнул Виктор Брябрин — а на полочке стоит его «Программное обеспечение персональных ЭВМ».

Казалось бы, небольшая старая книга, а какой пласт воспоминаний!

Из Ершова:

  • У меня есть одно существенное свойство — доводить дело до конца и стараться его исчерпать.
  • У меня обязательно бывает такой период времени, когда видимой цели работы нет. Мысли начинают растекаться... именно в это время рождаются непредвзятые идеи.
  • Сегодня оценка степени достоверности программы — личное дело каждого программиста. А нынешние правила приемки результатов их труда, при всей их кажущейся строгости, носят поверхностный характер, не затрагивающий существа самого программного продукта, да при этом еще постоянно выхолащиваются формальными требованиями соблюдения плановых сроков... — знакомо, да? Это 1983 год.
  • — Какова главная опасность нашего труда?
    — Потеря интереса к своему делу, ибо профессию программиста менять не на что.
  • Настоящий, врожденный, истинный программист — это тот, кто не сможет успокоиться, пока дело рук его не примет вполне завершенный вид. Контраст между почти сделанной и полностью сделанной работой для него непереносим чисто физически. Эта стопроцентная закономерность — источник трудности и в то же время глубочайшего удовлетворения...
  • Мне посчастливилось в жизни встретить нескольких программистов, обладающих поистине исключительным набором качеств. Это люди, как правило, резко выраженной индивидуальности и даже экстравагантности, но они вносили огромный вклад в общее дело, особенно в трудных ситуациях. Поэтому когда я слышу нередко раздающиеся призывы, что надо кончать с «примадоннами» в программировании, то никогда не поддерживаю эту ошибочную, на мой взгляд, точку зрения.
  • ...стоит ему [программисту] всерьез задуматься о философии своей профессии, как он сразу же начинает чувствовать себя мамонтом, которому грозит неизбежное, хотя, быть может, и не немедленное вымирание.

egorius: (Default)

Eric Redmond, Jim R. Wilson, «Seven Databases in Seven Weeks»

Купил для расширения горизонта сознания, а то некоторые астрологи считают, что будущее за NoSQL, а вот что это вообще такое? (Кстати, вышел русский перевод, но об этом я узнал уже после покупки.) В книге идет речь о разных СУБД: основные концепции, как осмысленно поиграться с базой, какая-нибудь клевая фича для интереса и куда копать дальше — вполне адекватный подход для быстрого знакомства с новой технологией.

Узелки на память:

  • PostgreSQL
    Тут все понятно, честная реляционка, рассматривается в качестве «единичного отрезка». Пара примеров показались странными — то ли из-за особенностей Оракла, то ли авторы намерено упрощали материал.
  • Raik
    Сильнораспределенное хранилище пар ключ-значение, построенное на простых равноправных узлах, без сложных настроек объединяющихся в отказоустойчивый кластер. Красиво настраивается баланс между скоростью, надежностью и согласованностью (на сколько узлов дублировать данные; от скольки узлов дождаться подтверждения записи; сколько узлов опросить при чтении): характерные варианты (N, 1, N) — согласованость при чтении, (N, N, 1) — согласованость при записи, (N, N/2, N/2) — кворумная согласованость. Позволяет выполнять mapreduce-запросы. При чтении можно получить несколько версий данных и разрешение конфликта лежит на клиенте.
    Технологии: REST, Erlang, JavaScript.
  • HBase
    Ориентация на распределенную обработку больших объемов, фича: произвольно большое число столбцов. Не понял, что тут принципиально несовместимо с SQL. Так 40 лет назад вручную программировали запросы, только теперь вместо Кобола предлагается делать это на модном JRuby.
    Технологии: JRuby, Ruby, Thrift, экосистема Apache (Hadoop, HDFS, Zookeeper, Whirr).
  • MongoDB
    В отличие_от, MongoDB понимает структуру хранимых JSON-документов и позволяет писать adhoc-запросы, вкючая агрегатные функции. Имеются индексы, explain plan, хранимые процедуры — все это несколько сближает MongoDB с традициями реляционных СУБД. При этом он остается распределенной документо-ориентированной СУБД с поддержкой mapreduce. Данные могут реплицироваться (replica sets, обеспечивается согласованность при записи) и распределяться (sharding) на несколько узлов, имеется встроенная распределенная файловая система.
    Технологии: JSON, JavaScript.
  • CouchDB
    Хранит JSON-документы, но, в отличие от MongoDB, не поддерживает adhoc-запросы. Вместо этого для доступа к данным предлагаются материализуемые, индексируемые и динамически обновляемые представления на основе mapreduce-функций. Имеется API для получения изменений в базе (polling). При обновлениях используется «оптимистическая блокировка»: если данные успели измениться, разрешение конфликта ложится на клиента. Репликация всех данных (без шардинга) между равноправными узлами, конфликты при репликации также разрешает клиент.
    Технологии: JSON, JavaScript, REST, Node.js, Erlang.
  • Neo4j
    База данных для хранения произвольных направленных графов; вершины и ребра могут содержать информацию в виде пар ключ-значение. Запросы по сути представляют собой обход графа. Есть индексы, но их надо явно указывать и учитывать — все это столь же коряво, как в древних навигационных БД. Впрочем, альтернативные языки Gremlin и Cypher придают запросам декларативность. Поддерживаются ACID-транзакции, но не при работе в кластере.
    Технологии: Java, Gremlin/Groovy, Cypher, REST, JSON, Lucene, Blueprint, Zookeeper.
  • Redis
    Очень быстрая база данных, предоставляющая не только хранение пар ключ-значение, но и реализацию стандартных структур данных (хэш-таблицы; списки, стеки и очереди; множества; отсортированные множества; битовые операции), псевдотранзакционность путем отложенного выполнения серии операций, механизм устаревания данных, подписку на события — мешанина свойств, из которых что-нибудь да окажется полезным. Вся БД хранится в ОЗУ и ограничена его объемом, но данные можно сбрасывать на диск — частота настраивается. Поддерживается репликация.

Авторы считают, что на смену реляционно-центрическим системам приходят объединения многих синхронизированных NoSQL-баз данных, каждая из которых используется в той области, в которой она сильна (они называют это выражением polyglot persistence). А для меня стало понятно, что NoSQL — это в первую очередь веб, это скорость в обмен на согласованность и надежность. Финансовым приложениям со стороны NoSQL ничего не угрожает.

Узнал про фильтры Блума. Оракл использует в механизме хэш-соединений штуку, которая почти, но все-таки не совсем непохожа_на.

Л. Н. Королев, «Архитектура ЭВМ»

Книга Льва Николаевича, завкафедрой ВМК и моего научного руководителя. Поэтому именно так, по-академически: Л. Н. Ну и книга сама тоже такая академически-заунывная. Жаль,что среди сухих фактов не нашлось места живым эмоциям. Все равно ведь в каждой главе тянет поговорить о БЭСМ-6, так почему нет? И черт бы со всеми этими интелами и спарками, про них и в других книгах почитать можно.

А еще т. н. издательство не заморачиваясь напечатало рукопись без какой-либо правки, во всей кривизне ее набора и верстки, со всеми опечатками и убогими вордовыми иллюстрациями. Человек, не знакомый с автором, будет держаться от такой книги подальше. Обидно это.

«Стив Джобс о бизнесе»

Понадергали цитат да и издали. Есть и хорошие, вот, например:

  • Простое дается труднее, чем сложное: нужно много работать, чтобы мысль стала ясной и простой.
  • Люди говорят, что вы должны страстно любить то, что делаете, и это истинная правда. А причина в том, что это настолько трудно, что без страсти любой разумный человек бросил бы это дело.
  • Я считаю, что одним из самых ценных ресурсов, которые есть у всех нас, является свободное время.

На каждом развороте вместе с переводом напечатал и оригинал; это интересно и хорошо.

egorius: (Default)

Не в силах вынести искажения исторической действительности, задал Кайту вопрос:

Tom,
you said that the first Oracle version was written in Fortran.
In “The 1995 SQL Reunion” paper I found the following words of Roger Bamford: Version 2 had been written in assembly language for PDP-11.
I'm sure you have access to information about early days of Oracle. Could you please clarify this point?

Через пару дней пришел ответ:

I went back and checked some notes....
....While much of the system was written in PDP-11 assembler language, parts were developed in the emerging new language C....
so, I’ve updated my slide—it was pdp-11 assember and C and was from there ported to VAX.
thanks for the followup on that detail!

Я считаю, это большой успех отечественной археологии.

egorius: (Default)

В минувший вторник в Москву приезжал Том Кайт, и как-то почти случайно вышло, что вслед за [livejournal.com profile] hardsign и я попал на этот семинар. Кайт рассказывал про новые фичи грядущего 12c (а их немало и среди них есть занятные), ну и кое-то из интересного про Оракл вообще. Не то, чтобы какие-то уникальные знания — про многое я читал или слышал раньше — но всегда приятно послушать действительно умного человека, тем более «в подлиннике». Теперь-то я знаю, як вони наш варчар вимовляють: вэркэр.

На вопрос из зала «В чем вы видите основную проблему баз данных» Кайт подумал и сказал: «Вы знаете, основная проблема не в технологиях, а в людях...». Порадовало.

Единственное, в чем Том заблуждается, так это в том, что первая версия Оракла была написана на Фортране. На ассемблере она была написана, это я вам как археолог заявляю.

egorius: (Default)

Начало: 1. Необходимая предыстория, назад: История SQL. 9. System R, Phase Two.

На протяжении всей серии я вольно обращался с темой «история SQL», то и дело отвлекаясь на соседние, интересные мне темы. Отвлекусь и сейчас, потому что история Оракла занятно переплетается с историей System R.

Вообще техническую информацию о ранних годах Оракла найти неизмеримо сложнее. Это не удивительно, ведь System R была исследовательским проектом и до 1979 года никаких ограничений на публикацию статей не имелось, а Оракл — изначально фирма коммерческая и закрытая. Что-то знают все, какие-то сведения менее известны, а некоторые еще ждут своего археолога. Пойдем, однако, по порядку.

В 1977 году Ларри Эллисон, имевший опыт работы над навигационной базой данных (для ЦРУ, проект назывался ORACL), прочитал статью Кодда и проникся реляционной идеей. Он основал компанию Software Development Laboratories вместе с Эдом Оатсом и Бобом Майнером, а вскоре к ним еще присоединился Брюс Скотт. Прочитав статьи про System R и язык Sequel (вспомним, что в статьях было все, вплоть до БНФ-синтаксиса), коллеги решили, что смогут достаточно быстро сделать свою реализацию реляционной СУБД. Ларри хотел сделать это раньше, чем IBM выпустит свою коммерческую реализацию, но при этом быть совместимым. Вспоминает Дон Чемберлин:

Он узнал о System R и хотел, чтобы его продукт был полностью совместим, вплоть до кодов ошибок. Мы спросили Франка [Кинга]: «Можем ли мы дать коды ошибок этому Эллисону?», но он сказал: «Нет, это конфиденциальная информация».

Тут я нарушу ход истории своим изысканием. Задумывались ли вы когда-нибудь, почему в Оракле исключение no_data_found имеет код +100, в то время, как остальным исключениям присвоен отрицательный номер? Фейерштейн пишет, что это, дескать, ANSI standard error number, но первый стандарт появился только в 1986 году! А теперь посмотрим внимательно на упоминавшийся ранее отчет «Support for Repetitive Transactions and Ad-hoc Query in System R» 1979 года. В нем промелькнул следующий фрагмент кода:

$SELECT DESCRIP,QOH,QOO INTO $DESCRIP,$QOH,$QOO
     FROM PARTS WHERE PARTNO=$PARTNO;
IF SYR_CODE = 0 THEN
     Write DESCRIP, QOH, QOO on terminal;
ELSE IF SYR_CODE = 100 THEN
     Write 'THERE IS NO SUCH PART' on terminal;
ELSE CALL TROUBLE('SELECT');

Полагаю, что дело было так. Ларри, очевидно, читал все материалы по System R и видел этот фрагмент. Пусть ему не удалось получить все коды System R, но +100 ему ничего не могло помешать использовать. А уже потом стандарт «узаконил» получившуюся странность. В одном документе есть фрагмент диалога о трудностях стандартизации между Доном Чемберлином от IBM и Кеном Якобсом от Оракл (оба входили в комитет ANSI):

Чемберлин: — У нас также были проблемы с кодами ошибок.
Якобс: — Стало быть, они были не только у Оракла?

Кстати, вот такое определение кодам ошибок дается в ANSI SQL-1992 (на Аде):

type SQLCODE-TYPE is range bsc .. tsc;
subtype SQL_ERROR is SQLCODE-TYPE range SQL-TYPE'FIRST .. -1;
subtype NOT_FOUND is SQLCODE-TYPE range 100 .. 100;

В тему будет привести слова Роджера Бэмфорда, участнику команды System R, перешедшему затем в Оракл:

Насчет влияния System R на Оракл: некоторые идеи пришли из Esvel, некоторые из System R. Но исходный код выглядел так, словно они прочитали статью, описывавшую язык, сели за компьютер и начали программировать. И было понятно, как писали код: все структуры данных напрямую отображали язык в аппаратуру безо всяких промежуточных слоев. «Так, вот у нас блок запроса, вот у него часть select, а вот то-то и то-то».

Однако возвратимся к повествованию. Спустя два года, в 1979-м, фирма изменила название на Relational Software, Inc и выпустила первый релиз системы. Он получил название Oracle version 2, поскольку Ларри полагал, что первую версию никто не купит. Система была написана на ассемблере DEC PDP-11 и занимала порядка 100 КБ оперативной памяти (из 128).

В 1982 году компания переименовалась в Oracle Systems Corporation, и с тех пор, несмотря на последующие изменения названия, слово Oracle уже не покидало ее имя.

Третья версия появилась в 1983 году. Чтобы облегчить портирование СУБД на другую аппаратуру, весь код был переписан на C, тогда еще не слишком популярном языке. Выбор оказался правильным и с тех пор доступность базы на разных платформах стала одним из коньков Оракла. Версия 3 была написана преимущественно Брюсом Скоттом. Правда, он ушел из Оракла до выпуска релиза, так что часть работы доделывал Боб Майнер. Слово Роджеру Бэмфорду:

Когда я пришел, они были на третьей версии, практически завершенной парнем по имени Брюс Скотт... Он переписал ее и создал действительно красивый, компактный и хорошо структурированный код; многое из этого кода сохранилось и сейчас.

Кстати, Роджер — не единственный из System R, кого Ларри звал в свою команду (Дон Слац не принял предложения, а Франко Путцолу присоединился позже).

Система во времена версии 3 была нестабильной. Снова Роджер:

В то время использовать Оракл можно было единственным образом: каждый день экспортировать все данные, ждать, пока база накроется, и загружать данные обратно. И все были довольны. То есть, конечно заказчики были не в восторге, но не придавали этому большого значения, потому что СУБД не использовалась как транзакционная система.

Примерно тогда же был написан командный интерпретатор, который не долго думая назвали так же, как в System R: UFI — User Friendly Interface. Позже его переименовали в SQL*Plus.

Уйдя из Оракла, Брюс довольно оригинально увековечил память о себе: все знают аккаунт scott/tiger (правда, не все догадываются, что это тот самый Брюс Скотт).

В 1984 году была выпущена четвертая версия, интересная прежде всего появлением согласованных чтений.

Пятую пропустим (заметив в скобках, что в 1987 году в Оракле началась работа над Applications, ныне OEBS), зато остановимся на шестой, увидевшей свет в 1988 году. Ее ведущим архитектором был Роджер Бэмфорд и в этом релизе была переписана часть, отвечавшая за доступ к данным, а это грубо говоря половина всей системы. Был полностью изменен низкоуровневый формат данных и механизм согласованный чтений, появились журнализация, восстановление, блокировки уровня строк:

Строки в версиях 3, 4, 5 были просто конкатенированы в блоках, байт за байтом, безо всяких индексов или указателей. Если вам надо было попасть на строку 12, вы начинали с начала блока и сканировали столбцы, строки... и да, со временем попадали именно туда, куда вам было нужно. А как изменять строку, если значение в столбце увеличивается? Ну, вы брали и сдвигали остаток блока вправо. Поэтому в версии 6 мы все это поменяли. ... С тех пор оно и работает без кардинальных изменений.

До этого Оракл обеспечивал согласованные чтения без механизма мультиверсионности, сохраняя при изменениях образ всего блока (past image). Интересно, что похожая техника используется сейчас для Flashback Database.

Еще версия 6 интересна тем, что в ней впервые появился процедурный язык поверх SQL. PL/SQL основан на синтаксисе языка Ада. Ада была современным языком на пике популярности и к тому же поддерживалась на государственном уровне, так что в целом выбор выглядит логичным, вот только мне не удалось найти никакой достоверной информации о том, как принималось это решение. По-видимому, для PL/SQL был разработан свой собственный компилятор, хотя и в строгом соответствии с имевшимися опубликованными наработками (так же, как было и с SQL). К этому заключению приводят две мысли. Во-первых, многие идеи, заложенные в Аду, в PL/SQL не попали, хотя специально выбрасывать их поддержку было бы странно (особенно это коснулось типизации данных). Во-вторых, от Ады все-таки был унаследован не только синтаксис, но и внутреннее устройство компилятора. Вот что сообщает нам PL/SQL User’s Guide and Reference:

PL/SQL is based on the programming language Ada. As a result, PL/SQL uses a variant of Descriptive Intermediate Attributed Notation for Ada (DIANA), a tree-structured intermediate language. It is defined using a meta-notation called Interface Definition Language (IDL). DIANA is used internally by compilers and other tools.
At compile time, PL/SQL source code is translated into machine-readable m-code. Both the DIANA and m-code for a procedure or package are stored in the database. At run time, they are loaded into the shared memory pool. The DIANA is used to compile dependent procedures; the m-code is simply executed.

То есть, код PL/SQL компилируется в то же внутреннее представление, что и Ада (DIANA), а затем в m-код, исполняемый PL/SQL-машиной. DIANA представляет собой атрибутированное дерево разбора и записывается в виде IDL (в базе это представление можно увидеть в таблицах sys.idl_%$).

Ну что ж, на этом археология уступает место новейшей истории, а мой рассказ заканчиваются.

Почитать и посмотреть:

Profile

egorius: (Default)
egorius

March 2025

M T W T F S S
      1 2
34 567 89
1011 121314 1516
17181920212223
24252627 28 29 30
31      

Syndicate

RSS Atom

Most Popular Tags

Style Credit

Expand Cut Tags

No cut tags
Page generated Jun. 28th, 2025 09:48 am
Powered by Dreamwidth Studios