Saturday, November 7, 2009

Установка Management Pack for Oracle GoldenGate

Всем добрый день!
Сегодня отличный день чтобы написать очередную заметку. Посвящена она будет довольно популярному продукту от Oracle (теперь:) под названием GoldenGate. Установка самого GG отлично описана вот тут. Меня же заинтересовал процесс установки подсистемы Management Pack for Oracle GoldenGate (в прошлом GoldenGate Director). В ходе инсталляции этой программы, у меня возникла очень неприятная ошибка, которая как раз и побудила меня написать данный пост. Поэтому основное внимание будет уделено именно ей. Лично у меня на решение этой проблемы ушло несколько часов, поэтому я надеюсь, что смогу сэкономить вам довольно много времени в случае возникновения аналогичной проблемы. Итак.
Director состоит из двух частей. Серверной и клиентской. Клиентскую часть я использовал виндовую и с ее установкой проблем не возникло. Установить серверную часть оказалось значительно сложнее.
Итак, для установки серверной части GG Director(Management Pack for Oracle GoldenGate), на сервере вам потребуется наличие WebLogic. Для его установки мне пришлось скачать и поставить JDK и JRE версии 1.6. После этого:
1.Прописываем путь к папке jre/bin в PATH
export PATH=/u04/app/jre_1.6/bin:$PATH
2. Прописываем переменную DISPLAY
export DISPLAY=yourip:0.0
3. Запускаем X-сервер. Лично я предпочитаю Reflection-X
4. Запускаем установку WebLogic
java -jar wls1031_generic.jar
Ничего сложного. В конце установки отказываемся от QuickStart.

Теперь ставим GG Director.
Скачиваем дистрибутив, распаковываем.
1. Делаем файл исполняемым
chmod +x ggdirector-serversetup_v2.0.0.2_001.sh
2. Запускаем инсталлятор
./ggdirector-serversetup_v2.0.0.2_001.sh
3. Потом все просто. Указываем сначала директорию, куда установили weblogic.
4. Когда инсталлятор потребует ввести директорию для домена, я создал и указал ему папку в директории weblogic:
mkdir -p user_projects/domains
Домен назовем GGDomain
5. Когда нас спросят про подключение к базе, я использовал Oracle.
6. Вот тут самое инетересное. Когда он просит указать нас SID, сервер и порт, то вне зависимости от того, какой порт мы укажем, при установке будет использоваться порт 1521. Например, мне надо было использовать порт 1522.
Если не предпринимать никаких действий, то мы получим ошибку:
unable to run run-cds-tool script file.
На эту ошибку у меня сегодня ушло очень много времени. Но я придумал одну хитрость как это обойти :)
7. Продолжаем процесс инсталяции. Когда статусная строка будет подходить к концу, необходимо нажать cancel. Установка запаузится.
8. В параллельной сессии заходим в папку $weblogic/wlserver_10.3/server/bin. Здесь мы должны обнаружить файл db-config.cds. Если его нет, значит на кнопку cancel нажали слишком рано.
9. Правим в файлике порт, на нужный.
10. В окне устанвки отказываемся от отмены установки. Установка продолжается и успешно завершается.
11. Если вы все сделали правильно, то серверная часть будет установлена. Но работать ничего не будет :)
При запуске сервера будет следующая ошибка:

<Nov 6, 2009 4:18:04 PM MSK> <Error> <Security> <BEA-000000> <[Security:090737]An exception occurred while getting connection to validate the configuration settings
java.sql.SQLException: [Security:090735]The DBMS connection was not usable
at weblogic.security.providers.
authentication.DBMSSQLAuthenticatorDelegateImpl.getManageableConnection(DBMSSQLAuthenticatorDelegateImpl.java:923)
at weblogic.security.providers.
authentication.DBMSSQLAuthenticatorDelegateImpl.validateConfiguration(DBMSSQLAuthenticatorDelegateImpl.java:153)
at weblogic.security.providers.
authentication.DBMSSQLAuthenticatorDelegateImpl.<init>(DBMSSQLAuthenticatorDelegateImpl.java:78)
at weblogic.security.providers.
authentication.DBMSAuthenticatorDelegateImpl.getInstance(DBMSAuthenticatorDelegateImpl.java:459)
at weblogic.security.providers.
authentication.DBMSSQLAuthenticationProviderImpl.initialize(DBMSSQLAuthenticationProviderImpl.java:56)
Truncated. see log file for complete stacktrace
>
<Nov 6, 2009 4:18:06 PM MSK> <Notice> <Security> <BEA-090082> <Security initializing using security realm ggRealm.>
<Nov 6, 2009 4:18:06 PM MSK> <Error> <Security> <BEA-000000> <[Security:090735]The DBMS connection was not usable>
<Nov 6, 2009 4:18:06 PM MSK> <Critical> <Security> <BEA-090402> <Authentication denied: Boot identity not valid; The user name and/or password from the boot identity file (boot.properties) is not valid. The boot identity may have been changed since the boot identity file was created. Please edit and update the boot identity file with the proper values of username and password. The first time the updated boot identity file is used to start the server, these new values are encrypted.>

12. Заходим в папку $weblogic/user_projects/domains/GGDomain/config/jdbc
13. Редактируем файл ggds-datasource-jdbc.xml, заменяя номер порта на нужный.
14. Запускаем $weblogic/user_projects/domains/GGDomain/startWebLogic.sh
15. При подключении указываем admin/admin. Также после имени сервера через двоеточие уазываем порт. Порт задавался при установке GG Director. По умолчанию 7001.

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

UPD!!!

Как выяснилось, проблема с портом листенера возникает только при использование Reflection-X в качестве X сервера. С XMing все проходит на ура.

Posted by GOster in 20:33:27 | Permalink | Comments (1) »

Thursday, May 7, 2009

Любимая ORA-08103

Наконец, выдалась свободная минутка написать.

 

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

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

ORA-12801: error signaled in parallel query server P007
ORA-
08103: object no longer exists

И возникала в процессе исполнения MERGE куска данных в таблицу. Около 20 млз в 160 млз. На таблице был построен набор битмап индексов. Табличное пространство ASSM.

В нашей практике данная ошибка встречается довольно часто. Oracle имеет ряд багов на эту тему.

Обычно это лечится пересозданием битмап индексов. Думаю, что как вариант можно сделать им rebuild online.

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

1.      В каком-то обновляемом блоке были проблемы, однако при переносе в другое ТП, они были исправлены.

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

Вот так.

Posted by GOster in 15:03:48 | Permalink | No Comments »

Monday, April 20, 2009

Merge, insert, update

Так как на данный момент я работаю над проектом хранилища данных, часто приходится проводить любопытные исследования, которые открывают нам интересные факты, неочевидные для разработчиков OLTP систем. Иногда приходится производить измерение характеристик производительности различных операций. Так, например, увеличение производительности операции на 15% зачастую может дать нам выгоду в загрузке в несколько часов, если не дней. К сожалению, хранилища данных тема не столь уж популярная у отечественных разработчиков и действительно ощущается недостаток статей технического характера, особенно на русском языке. Для большинства, думаю, приведенная информация покажется очевидной. Какие-то моменты, наверняка можно будет опровергнуть. Но это каждый уже решит для себя сам. Сейчас мне бы хотелось поделиться опытом и обратить внимание начинающих разработчиков на эксперименты, показывающие особенности построения ХД на базе ORACLE. Основной упор мне хотелось бы сделать на экспериментальный подход данных исследований.
Несколько лет назад мы проводили нагрузочное тестирование одной из первых версий нашего хранилища на довольно мощном оборудовании. В том числе на одной из наиболее производительных конфигураций HP 9000 Superdome. Признаться, эти серверы произвели на меня очень сильное впечатление. 256 процессов считывали таблицу и исполняли запрос с невероятной скоростью. 512 Гб темпа заканчивались за считанные минуты (если не секунды). В общем, это было весело и впечатляюще. Про сам процесс тестирования и его качество комментировать я не буду, но мы увидели некоторые интересные особенности работы. На тот момент мы использовали самый простой механизм загрузки таблиц фактов. Упрощенно он выглядел так. Процесс Extraction забирал данные из источника и складывал их в приемник. На этапе Transformation, данные из приемника забирались, и производился merge в следующую таблицу. В merge’е указывались условия для update и для insert. В процессе испытаний мы знали, что данных для обновления нет, и поэтому попробовали заменить оператор merge на insert. Мы ожидали увеличения производительности, но в реальности результаты нас удивили – настолько значительным оно было. После этого мы решили провести эксперимент. К сожалению, эксперимент производился уже на домашнем оборудовании, но и он дал свои результаты.
Суть этого эксперимента заключается в следующем:
  • Создается 3 таблицы
- Данные для merge(update): t_update

- Данные для insert: t_insert

- Данные для merge(insert/update): t_merge

  • Устанавливаются наборы количества записей для update и insert. Таким образом, чтобы можно было сформировать пространство производительности. По первой оси будет кол-во записей для update. По второй – кол-во записей для insert. По третьей – время исполнения операции.
  • Заполняем данными обновляемую таблицу t_uim, в которую будем производить insert, update и merge.
  • Заполняются данными таблицы t_update и t_insert. Данные для заполнения берутся таким образом, чтобы в таблице t_uim присутствовали записи с ключами из t_update и отсутствовали записи с ключами t_insert.
  • Производим insert из таблицы t_insert в таблицу t_uim и замеряем время.
  • Производим merge с условием WHEN MATCHED THEN UPDATE, производя, таким образом, update записей в таблице t_uim на значения из таблицы t_update и замеряем время операции.
  • Обновляем таблицу t_uim.
  • Таблица t_merge заполняется объединением union all таблиц t_update и t_insert.
  • Производится полноценный merge данных из таблицы t_merge в таблицу t_uim и замеряем время операции.
  • Повторяем для следующих значений кол-ва записей insert и update.
Сначала о данных. Т.к. эксперимент проводился на не очень мощном оборудовании, я постарался ограничиться минимально показательным кол-вом записей, которое на мой взгляд составляет 10 млн записей. Другими словами, я провожу все эксперименты именно с таким порядком количества записей и считаю, что это вполне показательная цифра для одной элементарной операции.
Итак:
  • Таблица t_uim содержала 100 млн. записей.
  • Таблицы t_insert и t_update заполнялись записями в следующих количествах:1.000, 10.000, 100.000, 1.000.000, 10.000.000
  • Операции производились серийно
  • В операциях insert и merge использовался хинт APPEND
  • В операциях update и merge использовался хинт USE_HASH
  • Производилось 3 запуска всего алгоритма для исключения влияния внешних факторов

T(Miu)=tjx(Ni+Nu+Ns)+trx(Ni+Nu+Ns)+tuxNu+tixNi
T(Mu)=tjx(Nu+Ns)+trx(Nu+Ns)+tuxNu
T(I)= trxNi+tixNi
Таким образом
T(Miu)-(T(Mu)+ T(I)) =
=tjx(Ni+Nu+Ns)+ trx(Ni+Nu+Ns)+tuxNu+tixNi – (tjx(Nu+Ns)+trx(Nu+Ns)+tuxNu+trxNi+tixNi)=
=tjx(Ni+Nu+Ns) – tjx(Nu+Ns) = tjxNi
Где:
T(Miu) – время операции merge(i/u)
T(Mu) – время операции merge(u)
T(I) – время операции insert
tj – усредненное время джойна для одной записи
tr – усредненное время чтения одной записи
tu – усредненное время update одной записи
ti – усредненное время insert одной записи
Ni – количество записей для insert
Nu – количество записей для update
Ns – количество записей в обновляемой таблице

То есть, merge(i/u) должен быть медленнее на время джойна количества записей для insert.
Это в теории. На практике совершенно иначе. Даже если количество записей для insert равно 0 (Ni=0), то время merge(i/u) значительно превышает время merge(u). Следовательно, гипотеза неверна.


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

Объяснение этого можно найти, если сделать 10104 (hash join statistics) трассировку выражений merge(i/u) и merge(u). Пример этих трассировок можно найти в приложении. Также это можно увидеть в представлении v$session_longops, вырезка из которой приведена в таблице 1. Ситуация следующая: при указании в операторе merge условий и insert, и update, при выполнении оператора, производится два джойна подряд. Также интересно, что оба джойна являются OUTER JOIN. При этом, каждый их этих джойнов проходит значительно быстрее джойна в merge(update). По результатам наших экспериментов, время двух джойнов в выражении merge(i/u) почти равно времени джойна в merge(u). Но необходимо не забывать, что в merge(i/u) все таблицы, участвующие в выражении читаются два раза.

Таким образом:
T(Miu)= tjx(Ni+Nu+Ns)+2xtrx(Ni+Nu+Ns)+tuxNu+tixNi
T(Miu)-( T(Mu)+ T(I))= tjxNi+ trx(Ni+Nu+Ns)

Это выражение уже гораздо ближе к реальности.
Безусловно, измерение в секундах, имеет огромное количество недостатков. Гораздо важнее показатели CPU, IO и т.п. Но, повторюсь, сейчас ставлю перед собой задачу исключительно исследовать тенденцию. Для любителей же поанализировать статистику, приведу частичный пример для i,u(0,1000000) сравнение операций merge(u) и merge(i/u) в таблице 3.

Также стоит отметить, что была попытка запуска алгоритма при Ni = 0 и Nu = 100.000.000. В этом случае merge(u) выполнялся около 3-х часов, а merge(i/u) около 7-и часов. Т.е. в случае отказа от использования merge(u/i), на нашем оборудовании можно было бы выиграть около 4-х часов в случае обновления порядка 100 млн. записей.

Еще свел список некоторых исправленных багов на металинке в выражении MERGE в таблице 2.

Ниже приведены графики времени выполнения соответствующих опреаций, а также график преимущества операций I+Mu над Miu.

Пакет при помощи которого производился эксперимент и прочие файлы вышлю по запросу на почту egor.osipov[цобачко]billing.ru

Вырезка из v$session_longops Таблица 1

Opname

Target

Target_desc

TotalWork

Units

Elapsed_seconds

Information

Merge completed

0

info

0

Hash Join

469575

Blocks

786

Table Scan

T_ium

371476

Blocks

479

Table Scan

T_merge

32688

Blocks

35

Hash Join

469575

Blocks

628

Table Scan

T_ium

371476

Blocks

365

Table Scan

T_merge

32688

Blocks

39

Information

Merge started

0

info

0

Information

Update Completed

0

info

0

Hash Join

469575

Blocks

1194

Table Scan

T_ium

371476

Blocks

262

Table Scan

T_merge

32688

Blocks

31

Information

Update started

0

info

0

Список некоторых багов по MERGE на metalink.oracle.com Таблица 2

Bug N

Description

< 9.2.0.7

2670601

MERGE does not work correctly for HASH clusters (wrong result)

3447792

Multi-table insert can fail with ORA-24335

3469121

OERI from MERGE statement with database link and sequence

< 9.2.0.8

2619895

Dump / OERI:kcbgcur_9 using PLSQL in SET clause of MERGE .. UPDATE

2692199

ORA-12805 from PQ MERGE with update clause with a functional index

3413826

ORA-904 from MERGE SQL across database link

4206556

ORA-2002 and OERI:kzasps1 when not auditing system privileges

4321292

LogMiner can fail for MERGE SQL on LOBs

4572043

DML monitoring is incorrect for MERGE / MTI

< 10.1.0.5

3413826

ORA-904 from MERGE SQL across database link

4252999

OERI:12333 from JDBC MERGE SQL with a batch size > 1

< 10.2.0.2

4395296

MERGE with APPEND hint dumps instead of giving ORA-30926

4602031

Block corruption from UPDATE or MERGE into compressed table

4630549

ORA-904 / dump from MERGE SQL

< 10.2.0.3

4644798

Dump (kkmups) using MERGE SQL

4685163

Merge SQL uses more CPU in 10.2 than 10.1

5049941

Direct path merge records incorrect information in ALL_SUMDELTA

< 10.2.0.4

4216668

Dump from INSERT / MERGE on internal columns

4311273

ORA-2064 using MERGE statement over a database link

4406211

Dump (insLoadRowInfo) from MERGE SQL with false condition

4572043

DML monitoring is incorrect for MERGE / MTI

4771851

Dump (kxccsrw) from INSERT .. AS SELECT / MERGE

5181547

Index corruption after insert-only merge /*+ append */ or PDML into table

5387478

ORA-28132 for user with EXEMPT ACCESS POLICY privilege

5609096

MERGE / UPDATE to IOT can cause corruption

5731952

MERGE statement does not insert rows if source is a (inline) view with parallel

6379123

EXPDP/IMPDP ORA-39083 due to changes in MERGE privilege between 10.1 and 10.2

< 11.1.0.7

5990542

OERI[kxfqenq1] / Dump (kxfqgky) from MERGE on one table partition

6318678

ORA-904 from MERGE SQL over database link

6379123

EXPDP/IMPDP ORA-39083 due to changes in MERGE privilege between 10.1 and 10.2

6694370

Update phase of serial direct path merge writes more redo than an update

INSERT

Сумма по полю Sec

Ins

Upd

0

1000

10000

100000

1000000

10000000

10000000

0,003

0,07

0,176

0,122

1,094

9,59

1000000

0,002

0,017

0,021

0,115

1,048

9,749

100000

0,002

0,014

0,019

0,116

1,039

9,595

10000

0,002

0,024

0,032

0,108

1,072

9,926

1000

0,002

0,033

0,036

0,144

0,967

10,051

0

0,002

0,041

0,024

0,139

1,068

9,842

UPDATE

Сумма по полю Sec

Ins

Upd

0

1000

10000

100000

1000000

10000000

10000000

1409,773

1345,477

1527,403

1369,143

1515,165

1624,855

1000000

132,465

105,988

105,018

103,534

99,019

200,827

100000

42,853

50,684

42,962

48,343

39,869

79,14

10000

32,491

29,843

39,321

42,173

29,327

40,483

1000

26,531

26,341

27,136

26,617

27,257

46,443

0

0,005

0,003

0,003

0,004

0,003

0,003

MERGE

Сумма по полю Sec

Ins

Upd

0

1000

10000

100000

1000000

10000000

10000000

2245,225

2377,532

2375,714

2256,942

2307,594

2794,332

1000000

180,662

179,926

182,774

214,046

447,038

1060,22

100000

83,182

111,373

108,911

120,021

121,979

1168,561

10000

70,292

59,273

61,198

86,044

114,056

419,1

1000

54,088

52,992

55,005

74,97

106,443

412,714

0

0,009

55,482

55,265

72,643

106,133

383,344

M-(U+I)

Сумма по полю Sec

Ins

Upd

0

1000

10000

100000

1000000

10000000

10000000

2245,22

2377,391

2375,362

2256,698

2305,407

2775,152

1000000

180,657

179,892

182,732

213,816

444,942

1040,721

100000

83,178

111,346

108,873

119,788

119,902

1149,37

10000

70,288

59,224

61,134

85,829

111,912

399,248

1000

54,084

52,926

54,932

74,681

104,508

392,612

0

0,004

55,399

55,217

72,364

103,998

363,66

Posted by GOster in 15:30:23 | Permalink | No Comments »

Tuesday, March 24, 2009

OMB+ Скрипты. deploy, undeploy, delete

И еще несколько удобных скриптов. Стоит заметить, что все скрипты, требующие вызова CONTROL CENTER’а выполняются достаточно долго. Одним из методов их ускорения может являться (в случае деплоя или андеплоя группы маппингов)  групповая процедура. Для этого необходимо при создании плана указать сразу все необходимые маппинги.

Скрипт деплоя маппинга. Запускается при нахождении в модуле. В этом скрипте интересным моментом является то, что при ошибке в деплоее, не генерируется исключения. Т.е. нельзя использовать стандартный механизм if { [catch {….}  erm ] > 0 } {.…}. Решение проблемы видно в скрипте.

OMBCC ‘/project/module’;

OMBCONNECT CONTROL_CENTER;

 

proc deploy_mapping {map} {

  log “Creating deploy plan for $map”;

  set cdir_t [OMBDCC];

  regexp “/(\[A-Z\]|\[0-9\]|\/|\_)*” $cdir_t cdir;

  if { [catch {OMBCREATE TRANSIENT DEPLOYMENT_ACTION_PLAN '$map.CREATE' ADD ACTION '$map.CREATE' SET PROPERTIES (OPERATION) VALUES ('CREATE') SET REFERENCE MAPPING '$cdir/$map' } erm] > 0 } {

    log “*** ERROR ***”;

    log “$erm”;

  }

  log “Deploying mapping $map”;

  catch {OMBDEPLOY DEPLOYMENT_ACTION_PLAN ‘$map.CREATE’} erm;

  if { [ regexp "ORA" $erm ] > 0 || [ regexp "OMB" $erm ] > 0  || [ regexp "OALL8" $erm ] > 0 } {

    log “*** ERROR ***”; log “$erm”;

  }

}

 

Скрипт андеплоя маппинга. Запускается при нахождении в модуле. Применяется для удаления пакета из БД.

OMBCC ‘/project/module’;

OMBCONNECT CONTROL_CENTER;

 

proc undepl_mapping {map} {

  set cdir_t [OMBDCC];

  regexp “/(\[A-Z\]|\[0-9\]|\/|\_)*” $cdir_t cdir;

  log Preparing for undeploying $map”;

  if { [OMBLIST MAPPINGS '$map'] == “$map” } {

    log “Creating drop plan for $map”;

    if { [catch {OMBCREATE TRANSIENT DEPLOYMENT_ACTION_PLAN '$map.DROP' ADD ACTION '$map.DROP' SET PROPERTIES (OPERATION) VALUES ('DROP') SET REFERENCE MAPPING '$cdir/$map' } erm] > 0 } {

      log “*** ERROR ***”; log “$erm”;

    }

    log “Undeploying mapping $map”;

    if { [catch {OMBDEPLOY DEPLOYMENT_ACTION_PLAN '$map.DROP'} erm] > 0 } {

      log “*** ERROR ***”; log “$erm”;

    }

  }

}

 

Ну и просто удаление маппинга. Запускается при нахождении в модуле.

OMBCC ‘/project/module’;

 

proc delete_mapping {map} {

  set cdir_t [OMBDCC];

  regexp “/(\[A-Z\]|\[0-9\]|\/|\_)*” $cdir_t cdir;

  log “Preparing for dropping $map”;

  if { [OMBLIST MAPPINGS '$map'] == “$map” } {

    log “Droping mapping $map”;

    if { [catch {OMBDROP MAPPING '$map'} erm] > 0 } {

      log “*** WARNING ***”; log “$erm”;

    }

  }

}


Posted by GOster in 15:06:09 | Permalink | Comments (2)

OMB+ Скрипты. Проверка модели

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

При выгрузке модели из OWBмы установили ряд требований к этим проектам. Требования диктовались внутренней спецификой, однако, вот они:

1.   Business name всегда должно соответствовать physicalname.

Причина этого заключается в том, что часто маппинги создаются методом копирования из другого маппинга и некоторой модификацией его. Результатом такой практики становится то, что в итоге появляются объекты с businessname соответствующим physical name уже существующего объекта. Эта проблема возникает, когда необходимо сделать обновление нескольких маппингов на конечной базе. В любом случае, стоит быть уверенным, что вы контролируете названия своих объектов.

2.   Target Load Ordering на маппингах должен быть выключен.

Так как эта опция входит только в EEверсию, мы от нее отказались. И соответственно, использовать ее мы не имеем права. Таким образом, есть необходимость проверять отсутствие ее на маппингах.

3.   В модели должны отсутствовать индексы и партиции в таблицах.

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

Итак, задача стоит написать процедуру, на вход которой передается название проекта, а она проверяет соответствующие характеристики и выдает предупреждение, если что-то не соответствует нашим требованиям. Также процедура в случае необходимости должна исправлять найденные ошибки.  Процедура запускается перед экспортом, и при необходимости в нее можно добавить необходимые проверки. Стоит обратить внимание, что везде для вывода сообщение используется процедура log. Её можно безболезненно заменить процедурой puts, если вывод должен осуществляться только на экран. Однако, гораздо интереснее, использовать процедуру, которая будет вскоре описана здесь.

 

Таким образом, можно написать следующую процедуру:

#mode  all,bn,tlo,tbl

#fix       yes|no

proc check_model { prj {mode “all”} {fix “no”} } {

  set mlist [split $mode "[,;:]“]

  OMBCC ‘/$prj’;

  foreach mod [OMBLIST ORACLE_MODULES] {

    OMBCC ‘$mod’;

    foreach map [OMBLIST MAPPINGS] {

      #Проверка Business Names

      if { [lsearch $mlist "all"] >= 0 || [lsearch $mlist "bn"] >= 0 } {

        set bn [OMBRETRIEVE MAPPING '$map' GET PROPERTIES(BUSINESS_NAME)];

        if { $map != $bn } {

          log “WARNING!!!: Mapping $map”;

          log “Business name is    $bn”;

          if { $fix == “yes” } {

            if { [ catch {

              OMBALTER MAPPING '$map' SET PROPERTIES(BUSINESS_NAME) VALUES('$map');

              log "FIXED!!!"; } err ] > 0 } {

              log “ERROR: $err”;

            }

          }

        }

      }

      #Проверка Target Load Ordering

      if { [lsearch $mlist "all"] >= 0 || [lsearch $mlist "tlo"] >= 0 } {

        set tlo [OMBRETRIEVE MAPPING '$map' GET PROPERTIES(TARGET_LOAD_ORDERING)];

        if { $tlo != “false” } {

          log “WARNING!!!: Mapping $map”;

          log “Use Target Load Ordering flag is enabled!”;

          if { $fix == “yes” } {

            if { [ catch {

              OMBALTER MAPPING '$map' SET PROPERTIES(TARGET_LOAD_ORDERING) VALUES('false');

              log "FIXED!!!"; } err ] > 0 } {

              log “ERROR: $err”;

            }

          }

        }

      }

    }

    #Проверка таблиц

    foreach tab [OMBLIST TABLES] {

      if { [lsearch $mlist "all"] >= 0 || [lsearch $mlist "tbl"] >= 0 } {

        set prts [OMBRETRIEVE TABLE '$tab' GET PARTITIONS];

        set idxs [OMBRETRIEVE TABLE '$tab' GET INDEXES];

        if { $prts != “” } {

          log “WARNING!!!: Table $tab”;

          log “Exists partitions : $prts”;

          if { $fix == “yes” } {

            if { [ catch {

              foreach prt $prts {

                OMBALTER TABLE '$tab' DELETE PARTITION '$prt';

                log "Fixing: Partition $prt deleted";

              }

              log "FIXED!!!"; } err ] > 0 } {

              log “ERROR: $err”;

            }

          }

        }

        if { $idxs != “” } {

          log “WARNING!!!: Table $tab”;

          log “Exists indexes    : $idxs”;

          if { $fix == “yes” } {

            if { [ catch {

              foreach idx $idxs {

                OMBALTER TABLE '$tab' DELETE INDEX '$prt';

                log "Fixing: Index $idx deleted";

              }

              log "FIXED!!!"; } err ] > 0 } {

              log “ERROR: $err”;

            }

          }

        }

      }

    }

    OMBCC ‘..’;

  }

}

 

Posted by GOster in 15:02:51 | Permalink | No Comments »

OMB+ Скрипты. Логирование

Хотелось бы рассмотреть чуть подробнее процедуру логирования. В принципе, ничего сложного как всегда нет.  Итак, вот какие процедуры использую я.

proc createLogFile {flnm} {

  global workdir

  set LogFileName $workdir\$flnm.log

  if { [catch {set fileHandle [open $LogFileName w]} FileError] } {

    puts “Could not open $LogFileName for writingn$FileError”

  exit 1

  }

  return $fileHandle

}

 

proc log {info} {

  global LogFile

  puts “$info”

  if { [lsearch [info globals] “LogFile” ] >= 0 } {

    puts $LogFile “$info”

    flush $LogFile

  }

}

 

proc closeLogFile {} {

  global LogFile

  close $LogFile

  unset LogFile

}

 

А вот пример их использования:

log “log only to screen”

set workdir {C:\Logs}

set LogFile [createLogFile "TESTLOG" ]

log “log to screen and log file :)

closeLogFile

Posted by GOster in 14:56:55 | Permalink | No Comments »

Saturday, November 1, 2008

О MODEL несчастном замолвите слово

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

MODEL – выражение позволяющее представлять результат запроса в виде многомерной модели и позволяющее производить с ней операции характерные для многомерных сред. Суть его очень проста – выражение MODEL позволяет нам представить результат запроса в виде многомерного куба и задавать выражения для расчета его произвольных ячеек. Выражение SQL MODEL очень сильно напоминает по смыслу объект MODEL в Oracle Olap – также задающего правила расчета ячеек куба. Поскольку, я расскажу лишь об основах этого выражения, за всеми подробностями обращаться прямо в документацию (http://download-uk.oracle.com/docs/cd/B19306_01/server.102/b14223/sqlmodel.htm)

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

Начнем с простых вещей. Для начала определим многомерную модель.

Многомерная модель определяется следующим образом: указываем, какие колонки запроса рассматривать как измерения(dimensions), а какие как показатели(measures).

MODEL

   DIMENSION BY col1, col2, …

   MEASURES col3, col4, …

При необходимости, куб может быть партиционирован(partitioned) по набору колонок:

MODEL

   PARTITION BY col0, …

   DIMENSION BY col1, col2, …

   MEASURES col3, col4, …

Стоить заметить, что возможность партиционирования особенно полезна при параллельной обработке.

В этих трех полях должны быть перечислены все колонки, участвующие в SELECT. Интересной особенностью здесь является возможность определения исходно несуществующих измерений. Это означает, что возможна такая ситуация:

MODEL

   DIMENSION BY col1, 777 NEW_DIM

   MEASURES col3, col4, …

Которая означает что будет создано еще одно измерение, с единственным элементом значение которого – 777. То же самое касается показателей.

Здесь же измерениям назначаются алиасы(псевдонимы), и в дальнейшем работа с ними ведется уже через алиасы.

 

Теперь перейдем к правилам(rules).

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

Теперь рассмотрим выбор ячеек. Каждая ячейка уникально определяется уникальным набором меток – элементов измерения и задается следующим выражением:

meas[dim1, dim2, …]

 Т.е. для указания одной ячейки нужно по каждому измерению выбрать один элемент. Порядок указания ссылок на элементы измерений определяется порядком измерений в выражении DIMENSION BY. Существует два типа меток  позиционные и символические.

1. Позиционные(positional) метки. Позиционная ссылка – это константа, уникально определяющая элемент измерения. Например, meas[‘Элемент1’,…]. В данном случае ‘Элемент1’ – это позиционная метка. При использовании этого типа меток выбираются ячейки у которых соответствующее измерение = значению ссылки.

2. Символические(symbolic) метки. Символическая метка – это выражение, имеющее логический тип. При использовании этого типа меток будут выбраны только те ячейки, для которых результат выражения равен true. Например, meas[dim1 > 10,…]. В данном случае будут выбраны те ячейки, у которых значение элемента измерения > 10. В общем случае, не обязательно указывать выражение относительно именно соответствующего измерения. Это может быть абсолютно любое логическое выражение. Например, допустимо следующее: meas[15 > 10,…].

 

Набор меток и показатель – является ссылкой. Ссылки делятся на одиночные и множественные.

1. Одиночные(single) ссылки – это такие ссылки, которые уникально определяют ячейку. Например, ссылки, измерения которых определяются исключительно позиционными метками – всегда одиночные. Также ссылка остается одиночной, если кроме позиционных меток, используются уникальные символические метки. Например: meas[10 , dim2=’Элемент2, …].

2. Множественные(multi-cell) ссылки – это такие ссылки, которые определяют набор ячеек куба. Ссылка может быть позиционной, если применяются символические метки, определяющие более одного элемента измерения. Например: meas[dim1 > 10,  dim2=’Элемент2, …].

 

 Рассмотрим пример простейшего правила:

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

Sales

Shop1

Shop2

FirstGood

1

 

SecGood

6

 

Т.е. продажи определены только для Shop1. Тем не менее, мы знаем, что в магазине Shop2 каждого товара было продано в два раза больше. Используя выражение MODEL эту задачу можно решить следующим образом:

MODEL

    DIMENSION BY Shops, Goods

    MEASURES (Sales)

RULES (

Sales[‘Shop2’, ‘FirstGood’] = 2 * Sales[‘Shop1’, ‘FirstGood’] ,

Sales[‘Shop2’, ‘SecGood’] = 2 * Sales[‘Shop1’, ‘SecGood’]

)

Результат будет следующим:

Sales

Shop1

Shop2

FirstGood

1

2

SecGood

6

12

Все хорошо пока товаров 2. Если их 100 – писать подобные правила быстро надоест. В таком случае на помощь приходит функция CV() current value - текущее значение. Эта функция используется в правой части правила и возвращает текущее значение элемента измерения в левой части. Без параметров она возвращает текущее значение того измерения, в позиции которого она указана. Пример: meas[CV(), …]. Здесь CV() вернет текущее значение первого измерения. В качестве параметра функции CV() можно задавать название измерения, текущее значение которого необходимо вернуть. Здесь может быть не совсем понятно, что означает текущее измерение. Давайте рассмотрим на примере.

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

Sales

1

2

3

4

FirstGood

3

5

1

7

SecGood

 

 

 

 

При этом известно, что SecondGood  во всех магазинах кроме 1-го продано в 4 раза больше чем FirstGood. В перовом же магазине – в 5 раз больше.

Sales[‘SecGood’, Shops > 1]  =  4 * Sales[‘FirstGood’, CV()],

Sales[‘SecGood’, 1]  = 5 * Sales[‘FirstGood’, CV()]

Сначала, разберем первое правило. Как видно – в левой части стоит множественная ссылка, которая указывает на набор ячеек, у которых товар = SecGood, а номер магазина > 1. В таком случае (если в левой части выбирается насколько ячеек), каждой из выбранных ячеек по очереди присваивается значение, вычисляемое в правой части. Т.е. это выражение можно представить в итерационной форме следующим образом:

FOR (i = 2; i<= 4; i++) {

            Sales[‘SecGood’, i] = 4* Sales[‘FirstGood’, i];

}

Таким образом, функция CV() – возвращает текущее значение выбранного поля на данной итерации. Соответственно в результате таблица будет выглядеть слеудющим образом:

Sales

1

2

3

4

FirstGood

3

5

1

7

SecGood

15

20

4

28

 

Также, CV() может принимать в качестве аргумента – название измерения. Тогда возвращать она будет значение не текущего измерения, а переданного в качестве параметра.

Если же необходимо указать однородное по измерению правило,  т.е. такое правило, где для элементов со всеми значениями измерения, необходимо указывать значения, стоит использовать выражение ANY. Также допустимо выражение вида dim1 IS ANY.  Стоит только помнить, что ANY является символической ссылкой, так как фактически интерпретируется как dim IS NULL or dim IS NOT NULL, даже, если оно указывается позиционно.

Если переформулировать предыдущую задачу в следующий вид:

При этом известно, что SecondGood  во всех магазинах кроме 1-го продано в 4 раза больше чем FirstGood.

То правило будет выглядеть следующим образом:

Sales[‘SecGood’, ANY]  =  4 * Sales[‘FirstGood’, CV()]

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

 

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

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

Sales

2004

2005

2006

TotalYears

Shop1

3

5

1

 

Shop2

6

10

2

 

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

DIMENSION BY (Shops, Years)

MEASURES (Sales)

Rules(

Sales[ANY, ‘TotalYears’]= Sum(Sales)[CV(), ANY]

)

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

DIMENSION BY (Shops, Years)

MEASURES (Sales)

Rules(

Sales[ANY, ‘TotalYears’]= Sum(Sales)[CV(), Years != ‘TotalYears’]

)

Таким образом, на выходе мы получим:

Sales

2004

2005

2006

TotalYears

Shop1

3

5

1

9

Shop2

6

10

2

18

 

Вообще, что касается правой части, то тут могут стоять практически любые выражения. Например, NVL, CASE, DECODE  и т.д.

 

Posted by GOster in 08:00:43 | Permalink | No Comments »

Wednesday, February 27, 2008

OMB+ Скрипты. Операторы в маппингах.

Проанализировав свои OMB+ скрипты, остался несколько разочарован – в основном они ориентированы на нашу систему и не имеют ценности применения вне ее. Тем не менее, я выделили часть скриптов для публикации.
Скрипты старался оформалять ввиде процедур. Так, чтобы было максимально удобно их использовать в своем коде в дальнейшем.
Также хотелось бы услышать интересные задачи для OMB+. Пишите в комментариях.
Если у Вас есть свой набор часто применяемых OMB+ скриптов то пишите на мыло gostest[масямба]gmail[dot]com.
Итак, сначала несколько скриптов для работы с оператормаи в маппингах.

Первый скрипт IOC просто копирует все входящие аттрибуты оператора из одной группы в другую. Это удобно для построения выражений.

 proc IOC {map oper {ingrp “INGRP1″} {outgrp “OUTGRP1″} } {
 set inpa [OMBRETRIEVE MAPPING '$map' OPERATOR '$oper' GROUP '$ingrp' GET ATTRIBUTES]
 set outa [OMBRETRIEVE MAPPING '$map' OPERATOR '$oper' GROUP '$outgrp' GET ATTRIBUTES]
 foreach inatr $inpa {
  if {[lsearch $outa $inatr]==-1} {
  set datatype [OMBRETRIEVE MAPPING '$map' OPERATOR '$oper' GROUP '$ingrp' ATTRIBUTE '$inatr' GET PROPERTIES(DATATYPE)]
  OMBALTER MAPPING ‘$map’ ADD ATTRIBUTE ‘$inatr’ OF GROUP ‘$outgrp’ OF OPERATOR ‘$oper’
  SET PROPERTIES(DATATYPE) VALUES (‘$datatype’)
  catch {
  set length [OMBRETRIEVE MAPPING '$map' OPERATOR '$oper' GROUP '$ingrp' ATTRIBUTE '$inatr' GET PROPERTIES(LENGTH)]
  OMBALTER MAPPING ‘$map’ ADD ATTRIBUTE ‘$inatr’ OF GROUP ‘$outgrp’ OF OPERATOR ‘$oper’
  SET PROPERTIES(LENGTH) VALUES (‘$length’)
  }
  catch {
  set precision [OMBRETRIEVE MAPPING '$map' OPERATOR '$oper' GROUP '$ingrp' ATTRIBUTE '$inatr' GET PROPERTIES(PRECISION)]
  OMBALTER MAPPING ‘$map’ ADD ATTRIBUTE ‘$inatr’ OF GROUP ‘$outgrp’ OF OPERATOR ‘$oper’
  SET PROPERTIES(PRECISION) VALUES (‘$precision’)
  }
  catch {
  set scale [OMBRETRIEVE MAPPING '$map' OPERATOR '$oper' GROUP '$ingrp' ATTRIBUTE '$inatr' GET PROPERTIES(SCALE)]
  OMBALTER MAPPING ‘$map’ ADD ATTRIBUTE ‘$inatr’ OF GROUP ‘$outgrp’ OF OPERATOR ‘$oper’
  SET PROPERTIES(SCALE) VALUES (‘$scale’)
  }
  }
 }
} ;#/*Создание выходных аттрибутов оператора на основе входных аатрибутов*/

Скрипт CPEXPR позволяет скопировать выражение из одного маппинга в другой.

 proc CPEXPR {from to {name “EXPRESSION”} {new_name “”}} {
if {$new_name == “”} {set new_name $name}
OMBALTER MAPPING ‘$to’ ADD EXPRESSION OPERATOR ‘$new_name’
set ingrp [OMBRETRIEVE MAPPING '$from' OPERATOR '$name' GET INPUT GROUPS]
set outgrp [OMBRETRIEVE MAPPING '$from' OPERATOR '$name' GET OUTPUT GROUPS]
OMBALTER MAPPING ‘$to’ MODIFY GROUP ‘INGRP1′ OF OPERATOR ‘$new_name’ RENAME TO ‘$ingrp’
OMBALTER MAPPING ‘$to’ MODIFY GROUP ‘OUTGRP1′ OF OPERATOR ‘$new_name’ RENAME TO ‘$outgrp’
foreach grp [OMBRETRIEVE MAPPING '$from' OPERATOR '$name' GET GROUPS] {
 foreach attr [OMBRETRIEVE MAPPING '$from' OPERATOR '$name' GROUP '$grp' GET ATTRIBUTES] {
  OMBALTER MAPPING ‘$to’ ADD ATTRIBUTE ‘$attr’ OF GROUP ‘$grp’ OF OPERATOR ‘$new_name’
  
  set datatype [OMBRETRIEVE MAPPING '$from' OPERATOR '$name' GROUP '$grp' ATTRIBUTE '$attr' GET PROPERTIES(DATATYPE)]
  OMBALTER MAPPING ‘$to’ MODIFY ATTRIBUTE ‘$attr’ OF GROUP ‘$grp’ OF OPERATOR ‘$new_name’
  SET PROPERTIES(DATATYPE) VALUES (‘$datatype’)
  catch {
  set length [OMBRETRIEVE MAPPING '$from' OPERATOR '$name' GROUP '$grp' ATTRIBUTE '$attr' GET PROPERTIES(LENGTH)]
  OMBALTER MAPPING ‘$to’ MODIFY ATTRIBUTE ‘$attr’ OF GROUP ‘$grp’ OF OPERATOR ‘$new_name’
  SET PROPERTIES(LENGTH) VALUES (‘$length’)
  }
  catch {
  set precision [OMBRETRIEVE MAPPING '$from' OPERATOR '$name' GROUP '$grp' ATTRIBUTE '$attr' GET PROPERTIES(PRECISION)]
  OMBALTER MAPPING ‘$to’ MODIFY ATTRIBUTE ‘$attr’ OF GROUP ‘$grp’ OF OPERATOR ‘$new_name’
  SET PROPERTIES(PRECISION) VALUES (‘$precision’)
  }
  catch {
  set scale [OMBRETRIEVE MAPPING '$from' OPERATOR '$name' GROUP '$grp' ATTRIBUTE '$attr' GET PROPERTIES(SCALE)]
  OMBALTER MAPPING ‘$to’ MODIFY ATTRIBUTE ‘$attr’ OF GROUP ‘$grp’ OF OPERATOR ‘$new_name’
  SET PROPERTIES(SCALE) VALUES (‘$scale’)
  }
  catch {
  set expresn [OMBRETRIEVE MAPPING '$from' OPERATOR '$name' GROUP '$grp' ATTRIBUTE '$attr' GET PROPERTIES(EXPRESSION)]
  regsub -all “‘” $expresn “”” res
  regsub -all {[{}]} $res “” expresn
  OMBALTER MAPPING ‘$to’ MODIFY ATTRIBUTE ‘$attr’ OF GROUP ‘$grp’ OF OPERATOR ‘$new_name’
  SET PROPERTIES(EXPRESSION) VALUES (‘$expresn’)
  }
 }
}
} ;# /*Копирует выражение из одного маппинга в другой*/

Скрипт UPDNAME просто позволяет произвести замену по рег.выражению названий аттрибутов.

 proc UPDNAME {map from to} {
 foreach oper [OMBRETRIEVE MAPPING '$map' GET OPERATORS] {
  foreach grp [OMBRETRIEVE MAPPING '$map' OPERATOR '$oper' GET GROUPS] {
   foreach atr [OMBRETRIEVE MAPPING '$map' OPERATOR '$oper' GROUP '$grp' GET ATTRIBUTES] {
    if {[regsub $from $atr $to ares ] > 0} {
     OMBALTER MAPPING ‘$map’ MODIFY ATTRIBUTE ‘$atr’ OF GROUP ‘$grp’ OF OPERATOR ‘$oper’ RENAME TO ‘$ares’
    }
   }
  }
 }
} ;# /*Замещает в тексте всех аттрибутов from на to*/

Скрипт SYNCR позволяет в текущем модуле синхронизировать все таблицы с таблицами из указываемого модуля.
Это может пригодиться в случае наличия нескольких модулей с таблицами и необходимости синхронизации с ними.

 proc SYNCR {srcmod} {
 set error_recs {}
 foreach map_name [OMBLIST MAPPINGS] {
  foreach tab_oper [OMBRETRIEVE MAPPING '$map_name' GET TABLE OPERATORS]  {
   set bound_name [OMBRETRIEVE MAPPING '$map_name' OPERATOR '$tab_oper' GET PROPERTIES (BOUND_NAME)]
   if {[catch {
    OMBSYNCHRONIZE MAPPING '$map_name' OPERATOR '$tab_oper' TO TABLE '$srcmod$bound_name' USE (RECONCILE_STRATEGY 'REPLACE',  MATCHING_STRATEGY 'MATCH_BY_OBJECT_NAME')
   }]} {lappend error_recs “$map_name/$tab_oper”}
  }
 }
 puts “Error in table operators:”
 foreach er $error_recs {
  puts $er
 }
};#/*Автоматическая синхронизация всех таблиц в мэппингах и вывод непривязанных таблиц*/

Posted by GOster in 16:59:29 | Permalink | No Comments »

OMB+ Скрипты. Описание объектов.

В этом разделе хочу разместить скрипт, которые я использую для просмотра свойств объектов.

Параметры таблицы в маппинге

 proc DSCRT {map {tabname “”}} {
 set outp {}
 set tabs {}
 if {$tabname != “”} { set tabs $tabname} else {set tabs [OMBRETRIEVE MAPPING '$map' GET TABLE OPERATORS]}
 foreach tab_name $tabs {
  lappend outp “nnFor table $tab_name:n”
  set prp [OMBDESCRIBE CLASS_DEFINITION 'TABLE_OPERATOR' GET PROPERTY_DEFINITIONS]
  foreach pr_val $prp {
   catch { set res [OMBRETRIEVE MAPPING '$map' OPERATOR '$tab_name' GET PROPERTIES($pr_val)]
   lappend outp “$pr_val:t $resn”}
  }
 }
 puts “nn $outp”
} ;#/*Получение описаний параметров таблицы в мэппинге*/

 

Параметры представления в маппинге

 proc DSCRV {map {tabname “”}} {
 set outp {}
 set tabs {}
 if {$tabname != “”} { set tabs $tabname} else {set tabs [OMBRETRIEVE MAPPING '$map' GET VIEW OPERATORS]}
 foreach tab_name $tabs {
  lappend outp “nnFor table $tab_name:n”
  set prp [OMBDESCRIBE CLASS_DEFINITION 'VIEW_OPERATOR' GET PROPERTY_DEFINITIONS]
  foreach pr_val $prp {
   catch { set res [OMBRETRIEVE MAPPING '$map' OPERATOR '$tab_name' GET PROPERTIES($pr_val)]
   lappend outp “$pr_val:t $resn”}
  }
 }
 puts “nn $outp”
} ;#/*Получение описаний параметров представления в мэппинге*/

Параметры аггрегатора в маппинге

 proc DSCRAG {map {aname “”}} {
 set outp {}
 set tabs {}
 if {$aname != “”} { set aggs $aname} else {set aggs [OMBRETRIEVE MAPPING '$map' GET AGGREGATOR OPERATORS]}
 set prp [OMBDESCRIBE CLASS_DEFINITION 'AGGREGATOR_OPERATOR' GET PROPERTY_DEFINITIONS]
 foreach agg_name $aggs {
   lappend outp “nnFor oper $agg_name:n”
   foreach pr_val $prp {
    catch { set res [OMBRETRIEVE MAPPING '$map' OPERATOR '$agg_name' GET PROPERTIES($pr_val)]
    lappend outp “$pr_val:t $resn”}
   }
 }
 puts “nn $outp”
} ;#/*Получение описаний параметров аггрегатора в мэппинге*/

Параметры ПРЕ- и ПОСТ- маппинг процедур.

 proc DSCRPMP {map} {
 set outp {}
 set tabs {}
 catch {
 set aggs [OMBRETRIEVE MAPPING '$map' GET PREMAPPING_PROCESS OPERATORS]
 set prp [OMBDESCRIBE CLASS_DEFINITION 'PREMAPPING_PROCESS_OPERATOR' GET PROPERTY_DEFINITIONS]
 foreach agg_name $aggs {
   lappend outp “nnFor oper $agg_name:n”
   foreach pr_val $prp {
    catch { set res [OMBRETRIEVE MAPPING '$map' OPERATOR '$agg_name' GET PROPERTIES($pr_val)]
    lappend outp “$pr_val:t $resn”}
   }
 } }
 catch {
 set aggs [OMBRETRIEVE MAPPING '$map' GET POSTMAPPING_PROCESS OPERATORS]
 set prp [OMBDESCRIBE CLASS_DEFINITION 'POSTMAPPING_PROCESS_OPERATOR' GET PROPERTY_DEFINITIONS]
 foreach agg_name $aggs {
   lappend outp “nnFor oper $agg_name:n”
   foreach pr_val $prp {
    catch { set res [OMBRETRIEVE MAPPING '$map' OPERATOR '$agg_name' GET PROPERTIES($pr_val)]
    lappend outp “$pr_val:t $resn”}
   }
 } }
 puts “nn $outp”
} ;#/*Получение описаний параметров пре и пост процедур мэппинге*/

Параметры локейшнов

 proc DSCRLOC { con } {
 set outp {}
 set tabs {}
 set prp [OMBDESCRIBE CLASS_DEFINITION 'CONNECTOR' GET PROPERTY_DEFINITIONS]
 foreach pr_val $prp {
  catch { set res [OMBRETRIEVE CONNECTOR '$con' GET PROPERTIES($pr_val)]
  lappend outp “$pr_val:t $resn”}
 }
 puts “nn $outp”
} ;#/*Получение описаний параметров локейшна*/

Параметры аттрибутов

 proc DSCRAT {map {aname “”}} {
 set outp {}
 set tabs {}
 if {$aname != “”} { set aggs $aname} else {set aggs [OMBRETRIEVE MAPPING '$map' GET PREMAPPING_PROCESS OPERATORS]}
 set prp [OMBDESCRIBE CLASS_DEFINITION 'ATTRIBUTE' GET PROPERTY_DEFINITIONS]
 foreach agg_name $aggs {
  foreach atr [OMBRETRIEVE MAPPING '$map' OPERATOR '$agg_name' GROUP 'INOUTGRP1' GET ATTRIBUTES] {
   lappend outp “nnFor oper $agg_name attr $atr:n”
   foreach pr_val $prp {
    catch { set res [OMBRETRIEVE MAPPING '$map' OPERATOR '$agg_name' GROUP 'INOUTGRP1' ATTRIBUTE '$atr' GET PROPERTIES($pr_val)]
    lappend outp “$pr_val:t $resn”}
   }
  }
 }
 puts “nn $outp”
} ;#/*Получение описаний параметров аттрибута мэппинге*/

Параметры маппингов

 proc DSCRM {map} {
 set outp {}
 lappend outp “nnFor mapping $map:n”
 set prp [OMBDESCRIBE CLASS_DEFINITION 'MAPPING' GET PROPERTY_DEFINITIONS]
 foreach pr_val $prp {
  catch { set res [OMBRETRIEVE MAPPING '$map' GET PROPERTIES($pr_val)]
  lappend outp “$pr_val:t $resn”}
 }
 puts “nn $outp”
} ;#/*Получение описаний параметров мэппинга*/

Параметры модулей

 proc DSCRMOD {map} {
 set outp {}
 lappend outp “nnFor modul $map:n”
 set prp [OMBDESCRIBE CLASS_DEFINITION 'ORACLE_MODULE' GET PROPERTY_DEFINITIONS]
 foreach pr_val $prp {
  catch { set res [OMBRETRIEVE ORACLE_MODULE '$map' GET PROPERTIES($pr_val)]
  lappend outp “$pr_val:t $resn”}
 }
 puts “nn $outp”
} ;#/*Получение описаний параметров модуля*/

Posted by GOster in 16:58:45 | Permalink | No Comments »

OMB+ Скрипты. Свойства маппингов.

В этом разделе предполагаю размещать скрипты производящие различные манипуляции с маппингами.

Скрипт UPDMAPCONF позволяет в выбранном проекте и модуле для всех маппингов устанавливать необходимые свойства.
В данном скрипте устанавливаются следующие параметры:
GENERATION_LANGUAGE = PLSQL
GENERATION_MODE = SET_BASED
ENABLE_PARALLEL_DML = true
ANSI_SQL_SYNTAX = false
*В качестве названия проекта или модуля допускается ALL. В этом случае рассматриваются все проекты или модули соответственно.

proc UPDMAPCONF {project module} {
 OMBCC ‘/’
 foreach prj [OMBLIST PROJECTS] {
 if {$project == “ALL” || $project == $prj} {
  puts “Project: $prj”
  OMBCOMMIT
  OMBCC ‘/$prj’
  foreach mod [OMBLIST ORACLE_MODULES] {
  if {$module == “ALL” || $module == $mod} {
   puts “Module: $mod”
   OMBCOMMIT
   OMBCC ‘/$prj/$mod’
   catch {
    foreach map [OMBLIST MAPPINGS] {
     puts “Mapping: $map”
     OMBALTER MAPPING ‘$map’ SET PROPERTIES(GENERATION_LANGUAGE, GENERATION_MODE,  ENABLE_PARALLEL_DML, ANSI_SQL_SYNTAX)
     VALUES (‘PLSQL’, ‘SET_BASED’, ‘TRUE’, ‘FALSE’);
    }
   }
  } }
 } }
} ;#/*Устанавливает конфигурацию всех маппингов в модуле, позволяет выбрать проект и модуль*/
 

Posted by GOster in 16:52:20 | Permalink | No Comments »