Meu objetivo neste post é descrever uma situação que aconteceu comigo e que consumiu algumas horas de dor de cabeça. Achei muito interessante e resolvi compartilhar aqui no blog.
Aparentemente a situação era bem simples:
No meu banco estava logado com meu usuário TAIRONE que possui perfil de DBA. Precisava criar uma materialized view em um outro esquema.
A MV era bem simples:
create materialized view TESTE.MV_DATA_ATUAL
refresh complete on demand
disable query rewrite as
select sysdate from dual;
Porém quando executei o comando deu o seguinte erro:
ERROR at line 1:
ORA-01031: insufficient privileges
Estranho né ... Como meu usuário que tem permissão de DBA não tem permissão de criar um materialized view...
Comecei a pesquisar sobre grants de MV e todos os grants que encontrava eu dava ao meu usuário, mas o erro continuava...
GRANT CREATE MATERIALIZED VIEW TO TAIRONE;
....
...
.
Foi aí que descobri o site abaixo:
http://www.krenger.ch/blog/create-materialized-view-ora-01031/
Ele informava um detalhe...
Na prática uma MV é uma tabela, cujo os dados são atualizados a partir de uma view de acordo com a configuração que for determinada na criação dela.
Eu estava tentando criar uma MV para um outro owner que não era o que eu estava logado. Esse owner não tinha permissão de criar tabelas. Foi aí que executei o seguinte comando:
GRANT CREATE TABLE TO TESTE;
E não é que funcionou!!!!!!
Quando tiver um tempinho vou estudar melhor a situação para entender o que aconteceu, mas fica aí a dica para a galera...
Grande abraços!!!
Otimização de Processos
Meu nome é Tairone, sou Bacharel em Informática pela Universidade Catolica do Salvador, pós-graduado em Banco de Dados pelo Instituto Científico de Ensino Superior e Pesquisa e especialista no desenvolvimento de projetos com grandes bases de dados Oracle e processos otimizados. Com mais de 10 anos de experiência, estarei aqui compartilhando dicas e técnicas PL/SQL que melhoram muito a performance das aplicações.
sexta-feira, 28 de março de 2014
terça-feira, 19 de março de 2013
Usando CONNECT BY
O CONNECT BY é um comando muito interessante da linguagem PL/SQL, pois ele permite a criação uma árvore hierárquica entre as tuplas de uma consulta, definindo o registro pai e o registro filho.
Considere a tabela abaixo:
select * from
(select 5533 registro, 'João' nome, 5000 registro_pai from dual
union
select 5000 registro, 'Lima' nome, NULL registro_pai from dual
union
select 4444 registro, 'Zé' nome, 5533 registro_pai from dual
union
select 3358 registro, 'Souza' nome, 4444 registro_pai from dual) tabela;
A consulta a seguir monta a árvore hierarquica para cada um dos registros em ordem crescente do menor filho para o maior pai:
select * from
(select 5533 registro, 'João' nome, 5000 registro_pai from dual
union
select 5000 registro, 'Lima' nome, NULL registro_pai from dual
union
select 4444 registro, 'Zé' nome, 5533 registro_pai from dual
union
select 3358 registro, 'Souza' nome, 4444 registro_pai from dual) tabela
connect by prior registro_pai = registro;
Resultado:
Se invertermos a cláusula CONNECT BY a ordem dos hierarquia é alterada para cada registros, passando a ser ordenado do maior pai para o menor filho:
select * from
(select 5533 registro, 'João' nome, 5000 registro_pai from dual
union
select 5000 registro, 'Lima' nome, NULL registro_pai from dual
union
select 4444 registro, 'Zé' nome, 5533 registro_pai from dual
union
select 3358 registro, 'Souza' nome, 4444 registro_pai from dual) tabela
connect by prior registro = registro_pai;
Resultado:
Se incluírmos a cláusula START WITH podemos definir qual a árvore deve ser exibida excluindo todas as outras da consulta:
select * from
(select 5533 registro, 'João' nome, 5000 registro_pai from dual
union
select 5000 registro, 'Lima' nome, NULL registro_pai from dual
union
select 4444 registro, 'Zé' nome, 5533 registro_pai from dual
union
select 3358 registro, 'Souza' nome, 4444 registro_pai from dual) tabela
connect by prior registro = registro_pai
start with nome = 'Lima';
Resultado:
Outra caracteristcia muito interessante desse comando é que ele permite a construção de uma estrutura de informações como podem ser observado nas consultas abaixo:
select level
from dual
connect by level <= 10;
Essa consulta retorna os números menor e igual a 10...
select level - 1 + TO_DATE('01/03/2013','DD/MM/RRRR') dias
from dual
connect by level <= last_day(TO_DATE('01/03/2013','DD/MM/RRRR')) - TO_DATE('01/03/2013','DD/MM/RRRR') + 1;
Essa consulta exibe uma estrutura com todos os dias de um determinado mês...
Espero ter contribuído como novos conhecimentos!
Em breve novas dicas ...
Considere a tabela abaixo:
select * from
(select 5533 registro, 'João' nome, 5000 registro_pai from dual
union
select 5000 registro, 'Lima' nome, NULL registro_pai from dual
union
select 4444 registro, 'Zé' nome, 5533 registro_pai from dual
union
select 3358 registro, 'Souza' nome, 4444 registro_pai from dual) tabela;
|
REGISTRO
|
NOME
|
REGISTRO_PAI
|
|
3358
|
Souza
|
4444
|
|
4444
|
Zé
|
5533
|
|
5000
|
Lima
|
|
|
5533
|
João
|
5000
|
A consulta a seguir monta a árvore hierarquica para cada um dos registros em ordem crescente do menor filho para o maior pai:
select * from
(select 5533 registro, 'João' nome, 5000 registro_pai from dual
union
select 5000 registro, 'Lima' nome, NULL registro_pai from dual
union
select 4444 registro, 'Zé' nome, 5533 registro_pai from dual
union
select 3358 registro, 'Souza' nome, 4444 registro_pai from dual) tabela
connect by prior registro_pai = registro;
Resultado:
|
REGISTRO
|
NOME
|
REGISTRO_PAI
|
|
3358
|
Souza
|
4444
|
|
4444
|
Zé
|
5533
|
|
5533
|
João
|
5000
|
|
5000
|
Lima
|
|
|
4444
|
Zé
|
5533
|
|
5533
|
João
|
5000
|
|
5000
|
Lima
|
|
|
5000
|
Lima
|
|
|
5533
|
João
|
5000
|
|
5000
|
Lima
|
|
Se invertermos a cláusula CONNECT BY a ordem dos hierarquia é alterada para cada registros, passando a ser ordenado do maior pai para o menor filho:
select * from
(select 5533 registro, 'João' nome, 5000 registro_pai from dual
union
select 5000 registro, 'Lima' nome, NULL registro_pai from dual
union
select 4444 registro, 'Zé' nome, 5533 registro_pai from dual
union
select 3358 registro, 'Souza' nome, 4444 registro_pai from dual) tabela
connect by prior registro = registro_pai;
Resultado:
|
REGISTRO
|
NOME
|
REGISTRO_PAI
|
|
3358
|
Souza
|
4444
|
|
5533
|
João
|
5000
|
|
4444
|
Zé
|
5533
|
|
3358
|
Souza
|
4444
|
|
4444
|
Zé
|
5533
|
|
3358
|
Souza
|
4444
|
|
5000
|
Lima
|
|
|
5533
|
João
|
5000
|
|
4444
|
Zé
|
5533
|
|
3358
|
Souza
|
4444
|
Se incluírmos a cláusula START WITH podemos definir qual a árvore deve ser exibida excluindo todas as outras da consulta:
select * from
(select 5533 registro, 'João' nome, 5000 registro_pai from dual
union
select 5000 registro, 'Lima' nome, NULL registro_pai from dual
union
select 4444 registro, 'Zé' nome, 5533 registro_pai from dual
union
select 3358 registro, 'Souza' nome, 4444 registro_pai from dual) tabela
connect by prior registro = registro_pai
start with nome = 'Lima';
Resultado:
|
REGISTRO
|
NOME
|
REGISTRO_PAI
|
|
5000
|
Lima
|
|
|
5533
|
João
|
5000
|
|
4444
|
Zé
|
5533
|
|
3358
|
Souza
|
4444
|
Outra caracteristcia muito interessante desse comando é que ele permite a construção de uma estrutura de informações como podem ser observado nas consultas abaixo:
select level
from dual
connect by level <= 10;
Essa consulta retorna os números menor e igual a 10...
|
LEVEL
|
|
1
|
|
2
|
|
3
|
|
4
|
|
5
|
|
6
|
|
7
|
|
8
|
|
9
|
|
10
|
select level - 1 + TO_DATE('01/03/2013','DD/MM/RRRR') dias
from dual
connect by level <= last_day(TO_DATE('01/03/2013','DD/MM/RRRR')) - TO_DATE('01/03/2013','DD/MM/RRRR') + 1;
Essa consulta exibe uma estrutura com todos os dias de um determinado mês...
|
DIAS
|
|
01/03/2013
|
|
02/03/2013
|
|
03/03/2013
|
|
04/03/2013
|
|
05/03/2013
|
|
06/03/2013
|
|
07/03/2013
|
|
08/03/2013
|
|
09/03/2013
|
|
10/03/2013
|
|
11/03/2013
|
|
12/03/2013
|
|
13/03/2013
|
|
14/03/2013
|
|
15/03/2013
|
|
16/03/2013
|
|
17/03/2013
|
|
18/03/2013
|
|
19/03/2013
|
|
20/03/2013
|
|
21/03/2013
|
|
22/03/2013
|
|
23/03/2013
|
|
24/03/2013
|
|
25/03/2013
|
|
26/03/2013
|
|
27/03/2013
|
|
28/03/2013
|
|
29/03/2013
|
|
30/03/2013
|
|
31/03/2013
|
Espero ter contribuído como novos conhecimentos!
Em breve novas dicas ...
terça-feira, 12 de março de 2013
Dicas de Funções Genéricas
Esta função bem simples retorna a quantidade de um determinado dia da semana existente em um intervalo de datas passados como parâmetro:
CREATE OR REPLACE FUNCTION QTTY_DAY_WEEK(p_dt_start DATE, p_dt_end DATE, p_day_week NUMBER) RETURN NUMBER IS
v_day NUMBER;
v_count NUMBER := 0;
v_start NUMBER;
v_end NUMBER;
BEGIN
v_start := TRUNC(SYSDATE) - p_dt_start;
v_end := TRUNC(SYSDATE) - p_dt_end;
FOR v_aux IN v_end .. v_start LOOP
SELECT to_char(SYSDATE - v_aux ,'d')
INTO v_day
FROM dual;
IF v_day IN (p_day_week) THEN -- 1 is Sunday, 2 is Monday, ...
v_count:= v_count + 1;
END IF;
ENd LOOP;
RETURN(v_count);
END;
onde:
p_dt_start é a data inicial
p_dt_end é a data final
p_day_week é o dia da semana (1 para Domingo, 2 para Segunda, 3 para Terça, ..., 7 para Sábado)
para testar:
select QTTY_DAY_WEEK(TO_DATE('01/03/2013','DD/MM/RRRR'), TO_DATE('20/03/2013','DD/MM/RRRR'), 1) QTD_DOMINGOS
from dual;
Resultado:
3
CREATE OR REPLACE FUNCTION QTTY_DAY_WEEK(p_dt_start DATE, p_dt_end DATE, p_day_week NUMBER) RETURN NUMBER IS
v_day NUMBER;
v_count NUMBER := 0;
v_start NUMBER;
v_end NUMBER;
BEGIN
v_start := TRUNC(SYSDATE) - p_dt_start;
v_end := TRUNC(SYSDATE) - p_dt_end;
FOR v_aux IN v_end .. v_start LOOP
SELECT to_char(SYSDATE - v_aux ,'d')
INTO v_day
FROM dual;
IF v_day IN (p_day_week) THEN -- 1 is Sunday, 2 is Monday, ...
v_count:= v_count + 1;
END IF;
ENd LOOP;
RETURN(v_count);
END;
onde:
p_dt_start é a data inicial
p_dt_end é a data final
p_day_week é o dia da semana (1 para Domingo, 2 para Segunda, 3 para Terça, ..., 7 para Sábado)
para testar:
select QTTY_DAY_WEEK(TO_DATE('01/03/2013','DD/MM/RRRR'), TO_DATE('20/03/2013','DD/MM/RRRR'), 1) QTD_DOMINGOS
from dual;
Resultado:
3
Até a próxima postagem...
segunda-feira, 21 de janeiro de 2013
Índices (Index)
Quando falamos de performance em banco de dados, o primeiro termo que devemos associar é Indice. Indice é um ponteiro que o SGBD utiliza para localizar um ou mais de um registro de uma determinada tabela em um banco de dados.
Para melhorar nosso entendimento, imagine uma fita métrica onde a cada centímetro foi escrito uma letra aleatória, podendo a mesma ser utilizada mais de 1 vez ao longo da fita :
-----------------------------------------
| a f e a t f e r s a |
-----------------------------------------
1 2 3 4 5 6 7 8 9 10
Analogamente a uma tabela sem indice, ao ser executada uma consulta para trazer todas as letras "a" que existe na fita nós teriamos que ler toda a fita para descobrir que existem 3 letras "a" em toda sua extensão. Em banco de dados esse tipo de acesso a informação é denominado acesso FULL, ou seja, o SGBD faz uma leitura completa da tabela para encontrar as informações que são solicitadas.
Caso um índice fosse criado para essa fita, ele armazenaria o posicionamento de todas as letras que foram escritas ao longo dela. E quando uma consulta fosse executada para trazer todas as letras "a" que existe na fita, antes do acesso a fita, nós acessaríamos o índice que informaria a existência de uma letra "a" na posição 1, outra letra "a" na posição 4 e uma ultima letra "a" na posição 10, com isso nós não necessitaríamos ler toda a fita para descobrir quantas letras "a" existem na fita, o que tornaria o retorno da consulta muito mais rápido. Em banco de dados esse tipo de acesso a informação é denominado acesso BY INDEX.
Antes de sairmos criando índices em um banco de dados é necessário fazer algumas avaliações:
- Qual o foco principal da aplicação que acessa o banco de dados ?
- Qual a granularidade da informação onde queremos criar o índice ?
- Qual o índice ideal a ser criado ?
Todo índice é uma estrutura a mais no banco de dados que consome espaço e tempo de processamento. Espaço porque ele cria uma estrutura a parte da tabela com chaves e ponteiros apontando para os dados da tabela e tempo de processamento porque o índice sofre alteração quando sua tabela de referência sofre alteração.
Analisar o foco principal da aplicação é importante porque os índices melhoram a performance das consultas a uma base de dados, mas degrada a performance das inserções, deleções e atualizações. Toda inserção, deleção e atualização de registros em uma tabela implica em atualização dos indices associados a ela que dependendo da quantidade de índices, do tamanho e da reorganização deles podem consumir muito tempo de processamento. Se o foco principal da aplicação é gerar relatórios índices podem ser criados para tornar a performance dos relatórios melhores, porém se o foco da aplicação é o cadastro de informações é importante avaliar a real necessidade de se criar índices para ela.
A granularidade de um campo diz respeito a variação de informações que ele representa. Vamos imaginar uma tabela de clientes onde existem apenas 3 campos RG, Primeiro Nome e Sexo. O campo Sexo só pode ter 2 opções masculino e feminino. Esse campo é considerado com granularidade baixa pois a variação de informações que ele representa são apenas duas. Campos com granularidade baixa não são indicados para criação de índices, pois o custo de acesso ao índice mais o custo de acesso a grande quantidade de dados retornados não compensa em relação a um acesso FULL a tabela. Imagine uma tabela com 50% de clientes do sexo masculino e 50% do sexo feminino, se fossemos consultar todos os clientes com sexo masculino a quantidade de registros retornados é metade da tabela. Caso a tabela tivesse 90% de clientes do sexo masculino e 10% do sexo feminino essa consulta retornaria praticamente todos os registros da tabela e ainda perderíamos o tempo de acesso ao índice. Já o campo RG possui uma variação muito grande de valores, praticamente um valor exclusivo para cada registro. Esse tipo de campo possui uma granularidade muito alta e é o tipo de campo mais indicado para criação de um índice. O desempenho de qualquer consulta que utiliza índices em campos de granularidade alta é excelente, pois a quantidade de acesso a disco é muito pequena e quanto menos acesso a disco uma consulta fizer mais rápido é o tempo de resposta dela. O campo Primeiro Nome é considerado com granularidade média, pois os valores podem se repetir muitas vezes mas a variação de valores também é muito grande. Esse tipo de campo também é indicado para criação de índices, apesar de não ser perfeito ele é muito bom.
O conhecimento do negócio é um fator determinante para tomadas de decisões em relação a criação de índices ou não. Apesar de campos com granularidade baixa não serem indicados para criação de índices existem processos onde eles funcionam muito bem. Como exemplo, podemos citar um processo que recebe uma carga de registros todos os dias e esses registros são processados logo em seguida. No registro existe um campo de status que informa se aquele registro já foi processado ou não, sendo "N" igual a "Não processado" e "S" igual a "Processado". Apesar da granularidade baixa, pois o campo recebe somente 2 valores, se o processamento é feito apenas nos registros com valores igual a "N", a tendencia é que a quantidade de registros com "N" seja muito menor que a quantidade de registros com "S" e a criação de um índice para esse campo pode tornar o processo muito mais rápido do que o acesso FULL a tabela. Porém somente a criação do índice e a execução de uma consulta que utiliza o campo do índice não implica em utilização do índice pela banco. Quem determina a utilização ou não do índice é o SGBD de acordo com o melhor plano de execução montado para aquela consulta e normalmente índices com granularidade baixa são desconsiderados pelo SGBD, nesses casos é necessário a utilização de um HINT para forçar a utilização do índice para a consulta. Em outro post falaremos sobre HINTS.
Espero ter ajudado um pouco e em breve retornarei com outras dicas!
Abraços
sábado, 8 de outubro de 2011
Introdução
Existem muitas formas de se criar um determinado programa, porém a forma escolhida pode ser o céu ou o inferno para muitos procedimentos. O exemplo mais fácil de entender pode ser visto analisando uma simples consulta para contar a quantidade de registros de uma tabela.
Podemos separar duas formas de consulta.
forma 1:
select count(*) from tabela;
forma 2:
select count(1) from tabela;
A segunda consulta é muito mais rápida que a primeira pois o banco não precisa recuperar todas as informações da tabela em disco. Quando a tabela analisada é pequena essa diferença de tempo pode não ser percebida, mas em tabelas grandes essa diferença pode causar grandes problemas.
Com um conhecimento mais avançado de banco de dados, dependendo da situação, podemos conseguir essa informação de uma forma muito mais rápida.
Imaginem que as consultas acima levem mais que 4 horas para informar a quantidade de registros de uma tabela muito grande e você precisa dessa informação o mais breve possivel. Sabendo que no seu banco de dados é executado um analyze da tabela de tempos em tempos, que pode ser diariamente, ou semanalmente, ou mensalmente, com a simples consulta abaixo você obtem a quantidade de registros aproximada da tabela, que pode já atender as suas necessidades, de uma forma quase que instantanea.
select num_rows, last_analyzed
from all_tables
where table_name like 'TABELA';
O num_rows é a quantidade de registros que existiam na tabela quando o ultimo analyze foi executado, cuja dia e hora é armazenado na coluna last_analyzed.
Meu objetivo aqui no blog é trazer dicas e técnicas que permitem resolver problema de performance, como também problemas do dia a dia dos projetos de banco de dados.
Espero que tenham gostado e em breve estarei postando outras dicas.
Tragam seus problemas que tentaremos encontrar uma solução...
Assinar:
Postagens (Atom)