среда, 31 августа 2011 г.

Справочники и классификаторы

Редкая крупная база данных и программная система на её основе сегодня обходится без использования справочников и классификаторов. Ситуация с применением этих терминов очень интересна благодаря тому, что по существу определения им никто толком дать не может. Кто-то говорит, что справочник - особый тип классификатора, кто-то - что классификатор это подтип справочника. Так что же это такое и в чем разница?

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

Справочник содержит перечень объектов, не занимаясь при этом классификацией. В то же время он также может быть иерархическим - например, простой справочник регионов РФ. Сравните - иерархический справочник регионов, имеющий структуру типа федеральные округа -> области -> населенные пункты, и классификатор экономических регионов, хранящий отдельно разные группы с разными экономическими показателями.

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

понедельник, 29 августа 2011 г.

Oracle Database: получить поля таблицы и комментарии к ним


Проектная документации на разработку программных систем, имеющих в основе базу данных, подразумевает наличие описания структур БД. Как правило, в техническом проекте приводится схема БД, получаемая обратной разработкой (reverse engineering) готовых таблиц, и информация об атрибутах таблиц. Для составления схем можно использовать бесплатный SQL Developer Data Modeler (как пользоваться), для описания атрибутов таблиц же удобно составить следующий SQL запрос:

SELECT col.table_name,
  col.column_name,
  col.data_type,
  col.data_length,
  col.data_precision,
  col.data_scale,
  col.nullable,
  com.comments
FROM ALL_TAB_COLUMNS col
LEFT JOIN user_col_comments com
ON (col.table_name  = com.table_name
AND col.column_name = com.column_name)
WHERE col.TABLE_NAME = 'PUO_FAIP'
ORDER BY table_name,
  column_name;

Как видим, запрос возвращает поля таблицы PUO_FAIP (column_name), тип данных в поле (data_type), длину данных для текстовых типов (data_length), точность и масштаб для числовых типов (data_precision и data_scale соответственно), возможность поля хранить значение NULL (nullable) и, наконец, комментарий (comments). Полученные данные легко экспортировать в текстовый редактор для оформления документации.

PS. При создании таблиц рекомендуется сразу создавать и комментарии к ее полям, это просто:
-- комментарий к полю
COMMENT ON COLUMN SCHEMA_NAME.TABLE_NAME.COLUMN_NAME IS
   'Текстовый комментарий к полю таблицы';

-- комментарий к таблице
COMMENT ON TABLE SCHEMA_NAME.TABLE_NAME IS
   'Текстовый комментарий к таблице';

понедельник, 15 августа 2011 г.

ExtJS: разработка интерфейса независимо от источников данных. Кросс-доменный AJAX для загрузки хранилищ

Удаленные источники для хранилищ ExtJS - это источники, расположенные в другом домене или, как их еще называют, кросс-доменные источники. Вероятно, вы знаете о том, что традиционные XMLHttpRequest запросы (известные в народе как AJAX запросы) имеют ряд ограничений, связанных в первую очередь с безопасностью - в частности, XMLHttpRequest запросы могут быть посланы только к адресу с одноименным протоколом, доменом и портом по отношению к запрашивающей странице. Таким образом, нельзя сделать традиционный AJAX запрос с сайта site1.com к сайту site2.com.

Тем не менее, такая необходимость часто возникает, в таких случаях используется так называемый cross-domain  (кросс-доменный) AJAX. Стоит отметить, что непосредственно к AJAX эти технологии уже не относятся, название в данном случае - не более, чем дань традиции. О методах, используемых в кросс-доменных запросах, можно почитать, например, в статье на javascript.ru.

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

myStore = new Ext.data.JsonStore({
    url:    'getUsers',
    method: 'POST',
    root: 'items',
    fields: [{name:"id"},{name:"name"}]
});

URL, указанный первым параметром, ссылается на страницу того же домена, в котором должна находиться разрабатываемая страница, иначе AJAX запрос на загрузку данных не сможет быть произведен. Таким образом, разрабатываемая html страница и javascript код в ее составе должны быть частью домена-источника данных. Это не всегда удобно и не всегда возможно.

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

myStore = new Ext.data.JsonStore({
    proxy: new Ext.data.ScriptTagProxy({
            url:    'http://vasilij.com/getUsers',
    }),
    method: 'POST',
    root: 'items',
    fields: [{name:"id"},{name:"name"}]
});

В код сервлета необходимо добавить отслеживание GET параметра callback. Дело в том, что при использовании ScriptTagProxy, ExtJS добавляет в строку запроса параметр callback, таким образом, что в нашем случае запрос будет иметь вид:

http://vasilij.com/getUsers&callback=stcCallback10

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

String callback = request.getParameter("callback");
if (callback == null){
  response.setContentType("text/javascript");
  out.println(jsonString);
}
else{
  response.setContentType("application/x-json");
  out.println(callback.concat("(").concat(jsonString).concat(")"));
}

Таким образом, при отсутствии callback параметра в строке запроса возвращается обычный JSON, в противном случае JSON оборачивается в функцию.

После внесения в код приложения описанных изменений можно разрабатывать интерфейс абсолютно независимо от всей остальной части приложения; сервлеты-поставщики данных могут находиться в любом домене, и даже простая html страница на рабочем столе с подключенным ExtJS сможет получать данные и корректно их обрабатывать и отображать. После окончания процесса разработки и тестирования можно заменить Ext.data.ScriptTagProxy на Ext.data.HttpProxy для использования AJAX запросов.

Приятного кодинга вам!

четверг, 11 августа 2011 г.

Pagination SQL query for Oracle database

Pagination is a process of dividing information into pages, it's daily task for web developer independently of used technology. Pagination is often described with respect to LAMP technologies (PHP and MySQL sites), for Oracle database it is not very different, but we must write other SQL query because Oracle don't support LIMIT and OFFSET parameters, actual for MySQL. In case of MySQL pagination query is:

SELECT id, name FROM users ORDER BY name LIMIT start, limit

This query for Oracle is shown below:

SELECT *
FROM
  (SELECT a.*,
    rownum AS rn
  FROM
    (SELECT id, name FROM users ORDER BY name
    ) a
  WHERE rownum < (start+limit)+1
  )
WHERE rn >= start+1;

Rownum is number of current row in sampling, note that it starts with 1. ORDER is necessary parameter, otherwise rownum will not have sence.

If input data is page number and page size, Oracle query is:

SELECT * FROM
(
    SELECT a.*, rownum rn
    FROM
    (
        SELECT id, name from users ORDER BY name
    ) a
    WHERE rownum < ((pageNumber * pageSize) + 1 )
)
WHERE rn >= (((pageNumber-1) * pageSize) + 1)

среда, 3 августа 2011 г.

Использование dbms_output в SQL*Plus и производных продуктах

При разработке на PL/SQL в SQL*Plus или продуктах, его использующих, часто возникает необходимость использования вывода результатов не в табличном, а простом, текстовом виде. Для этого используется PL/SQL пакет dbms_output.

Например, чтобы вывести строку 'Hello, World!', необходимо написать следующий код:

dbms_output.put_line('Hello, World!');

Код корректен - однако, приветствие миру вы, вероятно, не увидите. Для включения вывода пакета dbms_output необходимо добавить следующую строку:

set serveroutput on

Теперь можно использовать dbms_output на всю катушку:

set serveroutput on
begin
  for x in (select id, name from authors) loop
    dbms_output.put_line('Автор ' || rpad(x.name, 30) || ' имеет идентификатор ' || x.id);
  end loop;
end;

При выводе больших строк, возможно, вы столкнетесь с ошибкой buffer overflow, limit of 20000 bytes, сообщающую о переполнении буфера в 20000 байт. Для увеличения размера буфера можно использовать следующую команду:

DBMS_OUTPUT.ENABLE(500000);

Удачного дебага!