Трассировка в Oracle

Для себя, чтобы не забыть как это делается.

Трассировка SQL:

1) Включаем трассировку ALTER SESSION SET SQL_TRACE = TRUE;
2) Выполняем запрос
3) Выключаем трассировку
4) Профилируем трассировочный файл (каталог в параметре user_dump_dest) командой
tkprof <in_file> <out_file> explain=plsql/oracle sort=<exeela/plsela/fchela>

Трассировка PL/SQL

0) Создаем под SYS’ом трассировочные представления (файл <home>rdbmsadmintracetab)
1)
ALTER SESSION SET PLSQL_DEBUG = TRUE;
2)
ALTER [PROCEDURE | FUNCTION | PACKAGE] <name> COMPILE DEBUG [BODY];
3) DBMS_TRACE.SET_PLSQL_TRACE(DBMS_TRACE.<level>
) где level: trace_<all/enabled>_calls, trace_<all/enabled>_exceptions, trace_<all/enabled>_sql, trace_<all/enabled>_lines
4) Выполняем код, который хотим трассировать
5) Выключаем трассировку процедурой DBMS_TRACE.CLEAR_PLSQL_TRACE
6) Смотрим представления SYS.PLSQL_TRACE_RUNS и SYS.PLSQL_TRACE_EVENTS

Борьба с мутантами в Oracle

Странно, что никто из читающих мой блог этого не заметил (а некоторые, я знаю точно, должны были). Дело в том что метод описанный мной в посте Оптимизация “исторического” отчета в Oracle вызывает мутации таблиц (ORA-4091). Далее я, в кратце опишу как с этим бороться (интересующиеся могут посмотреть решение у Тома Кайта тут, суть моего решения такая же, но немного отличается, в частности, я инициализирую пакетную переменную явно, а не присваиваю ей пустую переменную).

Триггер на котором происходит мутация:
create or replace trigger detail_table1_rest
after insert or delete or update on detail_table1
for each row
begin
  –MAIN_ID – ID из MAIN_TABLE в дочерних таблицах
  update main_table
    set rest=CalcRest(:new.main_id, trunc(sysdate)),  –Тут предполагается, что функция CalcRest ходит не только по дочерним таблицам, но и по MAIN_TABLE, отсюда и мутации
        rest_date=sysdate
    where id=:new.main_id;
end;

Пример функции CalcRest (очень сильно упрощенный вариант):
create or replace function CalcRest(pid in number, ReportDate in date) return number is
  Result number;
begin
  –Изначальная сумма
  select summa
    into vSumma
    from main_table
    where id=pid;
  –Собираем суммы потраченного
  select sum(summa)
    into vSumma2
    from detail_table
    where main_id=pid;
  Result:=vSumma-vSumma2;
  return(Result);
end CalcRest;

Добавляем пакет (только спецификацию, без тела) с публичной переменной:
create or replace package pck_rest is
  type rest_tbl is table of
number;
 
rests rest_tbl; –Табличная переменная в которую будем сохранять id записей в main_table для которых надо пересчитать
end pck_rest;

Добавляем statement-level триггер в котором инициализируем переменную:
create or replace trigger detail_table1_rest_bfr
  before insert or update or delete on detail_table1
begin
  pck_rest.rests:=pck_rest.rest_tbl(); –Явная инициализация переменной через конструктор
end detail_table1_rest_bfr;

Изменяем row-level триггер:
create or replace trigger detail_table1_rest
after insert or delete or update on detail_table1
for each row
declare
  vID number;
  vInitID number;
  function CheckID (pId in number) return number is –функция для проверки дубликатов в табличной переменной, нужно это для того, чтобы потом не пересчитывать и не обновлять одну и туже запись несколько раз
    Result number:=0;
  begin
     for i in 1..pck_rest.rests.count loop
      if pck_rest.rests(i)=pId then
        Result:=pId;
      end if;
    end loop;
    return Result;
  end;

begin
  if inserting then
    vInitID:=:new.main_id;
  else
    vInitID:=:old.main_id;
  end if;
  –Проверяем на дубли

  vID:=CheckID(vInitID);
  –Если дублей не найдено добавляем новое значение

  if vID=0 then
    pck_rest.rests.extend;
    pck_rest.rests(pcg_rest.rests.last):=vInitID;
  end if;
end;

Добавляем сохраняющий statement-level триггер:
create or replace trigger detail_table1_rest_aft
  after insert or update or delete on detail_table1
declare
  vRest number;
begin
  for i in 1..pck_rest.rests.count loop
    –Пересчитываем
    vRest:=CalcRest(pck_rest.rests(i),trunc(sysdate));
    –Сохраняем
    update main_table
        set rest=vRest,
             rest_date=trunc(sysdate)
        where id=pck_rest.rests(i);
  end loop;
end;

Мутанты побеждены!

Оптимизация “исторического” отчета в Oracle

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

CREATE TABLE MAIN_TABLE
(
  ID NUMBER NOT NULL,
  SUMMA NUMBER NOT NULL
,CONSTRAINT MAIN_TABLE_PK PRIMARY KEY (ID) ENABLE
);

У этой таблицы есть куча дочерних таблиц и функция расчета некоего остатка денег
CalcRest(id in number,ReportDate in date), собирающая информацию по всем дочерним
таблицам на определенную дату и довольно сложно ее обрабатывающая. Нам нужно получить
отчет содержащий ID, SUMMA и результат CalcRest.

Итерация 0

select id, summa, CalcRest(id,:ReportDate) rest
  from main_table;
Предположим, что в таком виде отчет считается 10 часов – все время уходит на запуск 
CalcRest для каждой из 10000 записей.

Итерация 1

Добавляем к таблице поле для сохранения последнего значения остатка и триггеры для всех 
дочерних таблиц для обновления этого поля:

ALTER TABLE MAIN_TABLE
  ADD ("REST" NUMBER DEFAULT 0 NOT NULL);

create or replace trigger detail_table1_rest
after insert or delete or update on detail_table1
for each row
begin
  –MAIN_ID – ID из MAIN_TABLE в дочерних таблицах
  update main_table
    set rest=CalcRest(:new.main_id, trunc(sysdate))
    where id=:new.main_id;
end;

Теперь запрос для отчета выглядит так:
select id, summa, 
(case when :ReportDate=trunc(sysdate) then rest
else CalcRest(id, :ReportDate) end) rest
from main_table;

Что существенно ускоряет работу отчета, если мы его формируем на текущую дату, для всех остальных дат – 10 часов.

Итерация 2

Добавляем поле с датой изменения остатка и модифицируем триггера:

ALTER TABLE MAIN_TABLE
  ADD ("REST_DATE" DATE DEFAULT sysdate NOT NULL);

create or replace trigger detail_table1_rest
after insert or delete or update on detail_table1
for each row
begin
  –MAIN_ID – ID из MAIN_TABLE в дочерних таблицах
  update main_table
    set rest=CalcRest(:new.main_id, trunc(sysdate)),
        rest_date=sysdate
    where id=:new.main_id;
end;

И запрос:

select id, summa,
(case when :ReportDate=trunc(sysdate) or :ReportDate>rest_date then rest
else CalcRest(id, :ReportDate) end) rest
from main_table;

В результате CalcRest вызывается только для записей у которых остаток менялся после той даты на которую мы формируем отчет.

PS: Получилось немного сумбурно, но данный алгоритм реально работает.

bash-like prompt in Windows PowerShell

На досуге развлекался переделкой PowerShell чтобы он был похож на Linux’овский bash 😉 Вот что получилось…
Вставляем следующий код в $PROFILE:

cd $env:userprofile

function prompt
{
    $homedir=$env:userprofile;
    $m=$homedir.length;
    $str = $pwd.Path;
    if ($str -eq $homedir)
    {
        $str="~";
    }
    else
    {
        if ($str.length -ge $m)
        {
            if ($str.substring(0,$m) -eq $homedir)
            {
                $str="~"+$str.substring($m);
            }
        }
    }

    Write-Host -NoNewline -ForeGroundColor Cyan "PS ";

    if ($env:username -eq "Администратор")
    {
        Write-Host -NoNewline -ForeGroundColor Gray "$env:computername:";
    }
    else
    {
        Write-Host -NoNewline -ForeGroundColor Gray "$env:username@$env:computername:";
    }   
    Write-Host -NoNewline -ForeGroundColor DarkYellow "$str";
    if ($env:username -eq "Администратор")
    {
        Write-Host -NoNewLine -ForeGroundColor Cyan " #";
    }
    else
    {
        Write-Host -NoNewLine -ForeGroundColor Cyan ">";
    }
    " "
}

Получаем результат:

Новый рисунок (2)

Реанимация Oracle Database Control 11g

Суть проблемы в том,
что Control вешается на IP-адрес. Т.е. при динамическом IP или отсутствии сети
становится недоступен. Естественное решение – повесить его на localhost.

 

Гасим службы Oracle.
Отрубаем сеть. Удаляем каталог ORACLE_HOME192.168.1.65_ORCL. Удаляем службу
OracleDBConsoleORCL. Запускаем оставшиеся службы Oracle. Запускаем
Configuration Assistant, ставим галку Configure Database Control. Создаются
служба OracleDBConsoleORCL и каталог <имя хоста>_ORCL. Но при этом
появляется ошибка про Enterprise Manager Repository с командами как исправить.
Пробуем. Не помогает – пароль не найден в репозитории.

 

Решение
нашлось
 здесь:

After an
installation of a new Oracle Home and complete restore of the database it is
nescesary to regenerate the Database Console repository to use the Enterprise
Manager.

[oracle@centos
~]$ . oraenv

ORACLE_SID
= [orcl] ? orcl

The
Oracle base for ORACLE_HOME=/u01/app/oracle/product/11.1.0/db_1 is
/u01/app/oracle

create a
password file:

orapwd
file=$ORACLE_HOME/dbs/orapw$ORACLE_SID password=<password>

and check
the parameter remote_login_password:

[oracle@centos
db_1]$ sqlplus / as sysdba

SQL*Plus:
Release 11.1.0.6.0 – Production on Thu Jan 10 19:15:47 2008

Copyright
(c) 1982, 2007, Oracle. All rights reserved.

Connected
to:

Oracle
Database 11g Enterprise Edition Release 11.1.0.6.0 – Production

With the
Partitioning, OLAP, Data Mining and Real Application Testing options

SQL>
show parameter password

NAME TYPE
VALUE

————————————
———– ——————————

remote_login_passwordfile
string EXCLUSIVE

SQL>

if this
is not exclusive, change it and restart the database. After this you can
connect as sysdba over tns.

It is
nescesary to recreate the repository because with the reintall of oracle we
lost the repository key. which is located in the oracle_home. First thing to do
is drop the repository:

[oracle@centos
db_1]$ emca -deconfig dbcontrol db -repos drop

STARTED
EMCA at Jan 10, 2008 8:12:36 PM

EM
Configuration Assistant, Version 11.1.0.5.0 Production

Copyright
(c) 2003, 2005, Oracle. All rights reserved.

Enter the
following information:

Database
SID: orcl

Listener
port number: 1521

Password
for SYSMAN user:

Do you
wish to continue? [yes(Y)/no(N)]: y

Jan 10,
2008 8:12:46 PM oracle.sysman.emcp.EMConfig perform

INFO:
This operation is being logged at
/u01/app/oracle/cfgtoollogs/emca/orcl/emca_2008_01_10_20_12_36.log.

Jan 10,
2008 8:12:48 PM oracle.sysman.emcp.util.DBControlUtil stopOMS

INFO:
Stopping Database Control (this may take a while) …

Jan 10,
2008 8:13:01 PM oracle.sysman.emcp.EMReposConfig invoke

INFO:
Dropping the EM repository (this may take a while) …

Jan 10,
2008 8:20:33 PM oracle.sysman.emcp.EMReposConfig invoke

INFO:
Repository successfully dropped

Enterprise
Manager configuration completed successfully

FINISHED
EMCA at Jan 10, 2008 8:21:04 PM

[oracle@centos
db_1]$

Now we
can recreate the repository.

[oracle@centos
db_1]$ emca -config dbcontrol db -repos create

STARTED
EMCA at Jan 10, 2008 8:24:25 PM

EM
Configuration Assistant, Version 11.1.0.5.0 Production

Copyright
(c) 2003, 2005, Oracle. All rights reserved.

Enter the
following information:

Database
SID: orcl

Listener
port number: 1521

Password
for DBSNMP user:

Password
for SYSMAN user:

Email
address for notifications (optional): <my mail>

Outgoing
Mail (SMTP) server for notifications (optional): centos.bekijkhet.com

—————————————————————–

You have
specified the following settings

Database
ORACLE_HOME ……………. /u01/app/oracle/product/11.1.0/db_1

Local
hostname ……………. centos.bekijkhet.com

Listener
port number ……………. 1521

Database
SID ……………. orcl

Email
address for notifications …………… <my mail>

Outgoing
Mail (SMTP) server for notifications …………… centos.bekijkhet.com

—————————————————————–

Do you
wish to continue? [yes(Y)/no(N)]: y

Jan 10,
2008 8:24:50 PM oracle.sysman.emcp.EMConfig perform

INFO:
This operation is being logged at
/u01/app/oracle/cfgtoollogs/emca/orcl/emca_2008_01_10_20_24_25.log.

Jan 10,
2008 8:24:54 PM oracle.sysman.emcp.EMReposConfig createRepository

INFO:
Creating the EM repository (this may take a while) …

Jan 10,
2008 8:38:28 PM oracle.sysman.emcp.EMReposConfig invoke

INFO:
Repository successfully created

Jan 10,
2008 8:38:57 PM oracle.sysman.emcp.EMReposConfig uploadConfigDataToRepository

INFO:
Uploading configuration data to EM repository (this may take a while) …

Jan 10,
2008 8:42:16 PM oracle.sysman.emcp.EMReposConfig invoke

INFO:
Uploaded configuration data successfully

Jan 10,
2008 8:42:25 PM oracle.sysman.emcp.util.DBControlUtil configureSoftwareLib

INFO:
Software library configured successfully.

Jan 10,
2008 8:42:25 PM oracle.sysman.emcp.EMDBPostConfig configureSoftwareLibrary

INFO:
Deploying Provisioning archives …

Jan 10,
2008 8:42:49 PM oracle.sysman.emcp.EMDBPostConfig configureSoftwareLibrary

INFO:
Provisioning archives deployed successfully.

Jan 10,
2008 8:42:49 PM oracle.sysman.emcp.util.DBControlUtil secureDBConsole

INFO:
Securing Database Control (this may take a while) …

Jan 10,
2008 8:43:17 PM oracle.sysman.emcp.util.DBControlUtil secureDBConsole

INFO:
Database Control secured successfully.

Jan 10,
2008 8:43:17 PM oracle.sysman.emcp.util.DBControlUtil startOMS

INFO:
Starting Database Control (this may take a while) …

Jan 10,
2008 8:45:47 PM oracle.sysman.emcp.EMDBPostConfig performConfiguration

INFO:
Database Control started successfully

Jan 10,
2008 8:45:47 PM oracle.sysman.emcp.EMDBPostConfig performConfiguration

INFO:
>>>>>>>>>>> The Database Control URL is https://centos.bekijkhet.com:1158/em
<<<<<<<<<<<

Jan 10,
2008 8:46:42 PM oracle.sysman.emcp.EMDBPostConfig invoke

WARNING:

************************ 
WARNING  ************************

Management
Repository has been placed in secure mode wherein Enterprise Manager data will
be encrypted.  The encryption key has been placed in the file:
/u01/app/oracle/product/11.1.0/db_1/centos.bekijkhet.com_orcl/sysman/config/emkey.ora.  
Please ensure this file is backed up as the encrypted data will become unusable
if this file is lost.

***********************************************************

Enterprise
Manager configuration completed successfully

FINISHED
EMCA at Jan 10, 2008 8:46:42 PM

[oracle@centos
db_1]$

Be sure
to backup your system including the emkey.ora file!

Помогло!


Локальная и удаленная отладка в Oracle SQL Developer

Взято из Oracle Magazine январь-февраль 2008

Использование Oracle SQLDeveloper для локальной отладки PL/SQL означает, что вы выбираете и присоединяетесь к PL/SQL-программе, используя SQL Developer Connections Navigator. Вы устанавливаете точку останова в том месте, где отладчик должен сделать паузу, и затем нажимаете кнопку Debug. Oracle SQL Developer стартует отладочный сеанс, соединяется с ним, и останавливается, когда достигнет точки останова. При локальной отладке Oracle SQL Developer – это клиент, который инициирует отладку.
Удаленная отладка PL/SQL-кода в Oracle SQL Developer
означает, что вы инициируете отладочную акцию с клиента, внешнего по отношению к Oracle SQL Developer. Внешние клиенты могут быть PL/SQLWeb-приложениями, приложениями Oracle Application Express или сессиями SQL*Plus. Удаленная отладка требует некоторых ручных действий: запуск отладочного листенера Oracle SQL Developer
и присоединение к этому листенеру через сессию базы данных, которую требуется отладить (Удаленная отладка недоступна для баз данных, версия которых ниже Oracle9I Database Release 2).

Следующие шаги описывают пример удаленной отладочной сессии:
1. В Oracle SQL Developer присоединитесь к базе данных, где находится PL/SQL-код. Используйте ту же строку соединения HR_ORCL и пакет EMP_FETCHER, которые использовались ранее.
2. Выберите соединение HR_ORCL в Connections Navigator,
и затем щелкните правой кнопкой мыши и выберите Remote Debug. Откроется диалоговое окно Listen for JPDA.
3. Введите порт и IP-адрес сервера. Теперь вы видите новое окно Run Manager, на котором отображается информация о сервере Debug Listener. (Можно установить Tools -> Preferences -> Debugger -> Prompt for Debugger Host для отладки, когда соединение выполняется через бранмауэр или сессии виртуальных частных сетей [virtual private network – VPN] ).
4. В Connections Navigator выберите тело пакета EMP_FETCHER, затем щелкните правой кнопкой мыши и выберите Edit…, чтобы открыть редактор кода Oracle PL/ SQL. Кликните в левом поле на FUNCTION GET_EMP…, чтобы установить точку останова.
5. Кликните Compile for Debug как показано на Рис. 2.
6. Теперь вы готовы к тому, чтобы начать удаленный процесс отладки со стороны Oracle SQL Developer. Для этого необходимо, чтобы был готов внешний клиент, в данном случае SQL*Plus. Запустите сессию в SQL*Plus для того же самого пользователя базы данных, и введите IP-адрес и порт, заменив на ваши собственные, которые использовались для удаленного подключения в Oracle SQL Developer:
exec DBMS_DEBUG_JDWP.CONNECT_TCP (‘127.0.0.1’, 4000)
7. В SQL*Plus используйте анонимный блок для вызова функции:
DECLARE
EMP_NO NUMBER;
v_Return HR.EMP_REC;
BEGIN
v_Return := EMP_FETCHER.GET_EMP(201);
END;
/
8. Теперь управление передано Oracle SQL Developer и вы можете выполнять код по шагам. Начиная с этого момента процесс такой же, как при локальной отладке.
Стали активны различные отладочные окна. Все они имеют свое назначение, однако важными сразу же становятся:
• Окно Debugging, которое позволяет управлять выполнением программы. Начните отладку, нажимая пиктограмму Step Into.
• Редактор кода, который показывает точку выполнения. Когда отладка выполняется по шагам, при перемещении мыши всплывающие подсказки отображают название и значение переменной под указателем. Продолжайте нажимать Step Into до тех пор пока не достигните строки EMP_RTN:= EMP_REC…. В этой точке дайте мыши переместиться к переменной EMP_FOUND, чтобы увидеть всплывающую подсказку, как показано на Рис. 3.
• Окно Data отображает все переменные, которые действительны в текущем контексте. Перейдя на точку выполнения EMP_RTN := EMP_REC…, выберите закладку Data и раскройте узел EMP_FOUND. Теперь запись заполнена. В этой точке можно просматривать и модифицировать переменные, чтобы увидеть их влияние на процедуру.
9. Продолжайте шаги до тех пор, когда процедура завершится, управление будет передано внешнему клиенту, и отладочная сессия завершится.

Technorati Теги:

Заметки в процессе подготовки к экзамену OCA 1Z0-147

Technorati Теги:

Реализация проверки бизнес-правил в виде триггеров:

CREATE OR REPLACE TRIGGER check_sal
  BEFORE UPDATE OF sal ON emp
  FOR EACH ROW
  WHEN (new.sal<old.sal OR new.sal>old.sal*1.1)
BEGIN
  RAISE_APPLICATION_ERROR(-20508, ‘Do not decrease salary not increase by more than 10%’);
END;

SHOW ERRORS – показ ошибок компиляции в SQL*Plus

перекомпиляция инвалидных пакетов:

declare
  str varchar2(200);
  err varchar2(2000);
  cursor c_obj is
    select object_name, object_type from user_objects where status=upper(‘invalid’);
begin
  for c1 in c_obj loop
    if c1.object_type=upper(‘package body’) then
      str:=’alter package ‘||c1.object_name||’ compile body’;
    else
      str:=’alter ‘||c1.object_type||’ ‘||c1.object_name||’ compile’;
    end if;
    begin
      execute immediate str;
    exception
      when others then
        select text
          into err
          from user_errors
          where name=c1.object_name and type=c1.object_type and rownum=1;
        dbms_output.put_line(c1.object_name||’:’||err);
        err:=null;
    end;
  end loop;
end;

использование условий типа:

where (i.id, i.date1) in (select k.id, k.date2 from table2) –почему такую конструкцию мы не использовали раньше – загадка, как говориться – век живи – век – учись

09.04.2007 Хранимые процедуры Oracle на C#

Тема хранимых процедур на C# меня заинтересовала, хоть и не совсем понятно зачем это (хранимые процедуры на C#) нужно, но забавно. Впрочем надобность хранимых процедур Oracle на Java также остается для меня туманной.

Сначала пришлось повозиться с Oracle Database Extensions for .NET: оказалось, что к листенеру не был подключен ICP протокол, не был запущен агент для CLR и не был прогнан скрипт DBMSClr.plb после установки ODE.NET. Потом возник вопрос с коннектом из процедуры: ведь глупо в хранимой процедуре, которая запускается врапленной PL/SQL процедурой из схемы, писать в строке подключения имя пользователя, пароль и базу. Ответ нашелся в виде строки подключения "context connection=true". После этого все заработало.
Исходники простенькой функции:

using System;
using System.Collections.Generic;
using System.Text;
using System.Data;
using Oracle.DataAccess.Client;
using Oracle.DataAccess.Types; 

namespace OraTest
{
    public class Class1
    {
        public static Int32 AbonentsCount()
        {
            //Создаем соединение
           
OracleConnection con = new OracleConnection();
            con.ConnectionString = "context connection=true";
            con.Open();
            //Создаем команду
           
OracleCommand cmd = new OracleCommand();
            cmd.CommandText = "select count(*) cnt from prom_abonent";
            cmd.Connection = con;
            Int32 res = 0;
            res = Convert.ToInt32(cmd.ExecuteScalar());
            cmd.Dispose();
            con.Close();
            return res;
        }
    }

Запуск:
select abonentscount from dual; 

Написал и уже потом нашел туториал от Оракла на эту тему:
http://www.oracle.com/pls/xe102/to_toc?pathname=appdev.102%2Fb25312%2Ftoc.htm&remark=portal+%28Getting+Started%29