quarta-feira, 29 de setembro de 2010

Relatórios XML Publisher com Alto Volume de Dados

Oi Pessoal,

Como todos sabem, o XML/BI Publisher é a ferramenta padrão para criação de relatórios no EBS R12. No entanto, relatórios com grande volume de dados frequentemente falham quando executados em XML Publisher, com erros como OutOfMemoryError ou erros de lentidão excessiva. Para contornar essa situação, o XML Publisher tem um modo de trabalho chamado escalável (Scalable Mode) que pode ser ativado com configurações simples. O passo a passo:

1. Ligar a opção Scalable Mode para o relatório em questão:
1. System Administrator: Concurrent->Program->Define
2. Consultar o Concurrent do Relatório XML Publisher que desejar ativar
3. Adicionar um parâmetro com o nome ScalableFlag:
? Value Set: yes_no
? Default Value: Yes
? Selecionar checkboxes Enable e Required
? NÃO SELECIONAR checkbox Displayed, para impedir o desligamento em runtime.
? Token precisa ser ScalableFlag (Case Sensitive).

2. Configurar o Administrador do XML Publisher
1. XML Publisher Administrator: Administration -> Configuration
2. General -> Temporary Directory: Setar para o valor da variavel de ambiente $APPLPTMP
3. FO Processing -> Use XML Publisher XSLT PRocessor = True
4. FO Processing -> Enable Scalable Feature of XSLT PRocessor = FALSE
5. FO Processing -> Enable XSLT Runtime Optimizations = True

3. Configurar o Output Post Processor (OPP)
1. Logado como APPS, executar (trocar o -mx2048m pela memoria maxima desejada):
update FND_CP_SERVICES set DEVELOPER_PARAMETERS =
'J:oracle.apps.fnd.cp.gsf.GSMServiceController:-mx2048m'
where SERVICE_ID = (select MANAGER_TYPE from FND_CONCURRENT_QUEUES
where CONCURRENT_QUEUE_NAME = 'FNDCPOPP');
2. Fazer o Bounce do Concurrent Manager

Para melhorar ainda mais a performance, aplicar o patch 7687414 que corrige alguns problemas de ordenação no XDK (XML Developer Kit) usado pelo XMLP para ordenar os registros.

Abraços!!

quarta-feira, 7 de abril de 2010

Recuperando erro inesperado no Oracle Data Integrator (ODI)

Depois de muito tempo sem postar, estamos aqui novamente, o motivo foi excesso de trabalho (rsrsrsrsrs)

Hoje o Post é de bastante utilidade no desenvolvimento de Integrações, tanto simples quanto complexa.

No desenvolvimento padrão de um ODI, utilizamos vários procedimentos, variáveis e interfaces, porém na execução dos mesmos, poderão ocorrer erros que não estavam previstos e assim fica bastante complicado localizar ou mesmo saber qual foi a origem do erro.
Nesse caso o indicadio criar um procedimento genérico que todos os procedimentos, interfaces ou variáveis apontem o "KO" para ele, assim todo o erro não previsto vai redirecionar para ele com o seguinte código abaixo, você conseguirá exibi-lo:

%=odiRef.getPrevStepLog("MESSAGE")%

Esse comando pode ser utilizado da maneira que você achar mais fácil e que atenda sua necessidade.

Até a próxima...

quinta-feira, 10 de setembro de 2009

Criando um Criteria Dinâmico para o objeto LOV - (OAF).

Nesse Exemplo vamos fazer um passo-a-passo de como criar uma Lov dinâmica, recebendo um ou mais parâmentros.

Vamos pensar no seguinte cenário:

Temos um Página onde a mesma tem um FormValue ou qualquer outro campo que tenha uma valor, e esse valor desse campo vai servir como critério para o resultado de uma Lov.

A primeira coisa a ser feita é montarmos o View Object da Lov, não esquecendo de colocar um campo Transient com o mesmo formato do campo que vai servir de Criteria. Lembrando também que esse atributo no VO tem que ser marcado como Passivate.

Depois crie a Região de Lov adicionando uma Table associada ao VO, na mesma crie um FormValue com o atributo Passivate do VO, pois o mesmo vai receber o valor que estava na Página.

Feito isso, crie um Controller na Table da Lov, e adicione o seguinte código no ProcessRequest:

Dictionary passiveCriteria = (Dictionary)pageContext.getLovCriteriaItems();
//Cria um Dictionary, em seguida pega o valor que vem do LovMap da página requisitante
String passiveInvoiceId = (String)passiveCriteria.get("NomeLovMap");

//Instância o Application Module
OAApplicationModule am = (OAApplicationModule)pageContext.getApplicationModule(webBean);
//Chama o método do AM passando o valor que recebemos do LovMap
am.invokeMethod("execQuery", new Serializable []{passiveInvoiceId});

//Executa a Table para trazer todos os registros
if (!pageContext.isFormSubmission()) {
OATableBean table = (OATableBean) webBean;
table.queryData(pageContext);
}

No Application Module criamos um método que executa uma query dinâmica conforme a sua necessidade.

O último passo é na página que chama a Lov, criarmos o LovMap que refere-se ao valor que vamos passar como critério. No mesmo colocamos no "LOV Region Item" o campo que vai receber, no "Criteria Item" passamos o valor da página que vai servir comom critério, e por último marcamos "Programmatic Query" como "True".

Assim conseguimos montar a nossa Lov dinâmicamente conforme a necessidade do negócio.

Abraços e até a próxima.

quarta-feira, 2 de setembro de 2009

[PL/SQL] Esqueça tabelas temporárias: use funções PIPELINED!

Oi Pessoal,

Por várias vezes temos situações de relatórios, por exemplo, em que a consulta ficaria tão complexa que decidimos gerar uma tabela temporária para basear o relatório, ficando o preenchimento das informações a cargo de uma package PL/SQL que é chamada momentos antes do relatório. No entanto, o Oracle 9i em diante nos permite o uso de funções PIPELINED para atingir o mesmo fim, sem ter que criar tabela nenhuma, e sem necessitar controle sobre a transação (commit, usuários concorrentes etc).

Para fazê-lo, os quatro simples passos são:

1) Criar um type no banco do tipo OBJECT que contenha todos os campos necessários na sua query:
create or replace type test_rec as object (key number, value1 varchar2(100), value2 varchar2(200));

2) Criar um outro type, desta vez como uma tabela do type criado anteriormente:
create or replace type tab_rec as table of test_rec;

3) Criar uma função/package a ser chamada do relatório, com o modificador PIPELINED:
 create or replace function get_test(p_key IN NUMBER) return tab_rec pipelined is
begin
for i in 1..p_key LOOP
pipe row( test_rec(i, 'Test'||i, 'This is test number '||i||' for pipelined functions' ));
end loop;
return;
end;


4) Pronto! Agora é só selecionar os registros direto da função, assim:

SELECT *
FROM TABLE(get_test(2));

KEY VALUE1 VALUE2
----------- -------------- ---------------
1 Test1 This is test number 1 for pipelined functions
2 Test2 This is test number 2 for pipelined functions


Abraços pessoal, até a próxima!

terça-feira, 11 de agosto de 2009

[SQL] Como pegar o último valor não-nulo para preencher colunas

Olá pessoal!

Dica rápida interessante de SQL: Imaginem a seguinte situação, que pode acontecer em queries que possuem outer join por exemplo (usarei uma tabela apenas para simplificar o exemplo):

SELECT empregado, gestor FROM empregados

Empregado Gestor
---------- ----------
Joao Luis
Joao NULL
Roberto Joao
Roberto NULL

Essa query trouxe valores duplicados, uma delas com gestor e outra sem. Agora imagine que queremos copiar o valor GESTOR para os valores onde for nulo, agrupando por EMPREGADO. Para isso, podemos usar a função analítica LAST_VALUE, que traz o último valor de uma série, em conjunto com o modificador IGNORE NULLS para ignorar os valores nulos e particionando pela coluna EMPREGADO (que é não-nula):

SELECT empregado, LAST_VALUE(gestor IGNORE NULLS) OVER (ORDER BY empregado) FROM empregados

Empregado Gestor
---------- ----------
Joao Luis
Joao Luis
Roberto Joao
Roberto Joao

Espero que seja útil para livrá-los de enrascadas! Até mais!