pl/sql – Documentos do CPD
Resumão de PL/SQL, parte 4: controles condicionais e seqüenciais
A PL/SQL conta com as seguintes estruturas de controle:
1) IF-THEN-ELSIF-ELSE
A sintaxe geral do controle condicional por IF, é a seguinte:
[ELSIF condição THEN
comandos;]
Na sintaxe acima, condição é uma expressão booleana que pode retornar TRUE, FALSE ou NULL. O bloco de código só executa se a condição for TRUE. Devemos ter cuidado para tratar condições que avaliam para NULL, ou seja: para cada variável ou coluna que referenciamos em uma expressão booleana, devemos pensar sobre o que fazer se o resultado for NULL!
As condições especificadas no IF e no ELSIF devem ser mutuamente exclusivas (isso não é obrigatório, mas evita bugs e confusões no código) e podemos aninhar várias instruções IF-THEN-ELSIF-ELSE.
É importante lembrar que a PL/SQL avalia as expressões de modo "curto", ou seja, ela não precisa avaliar toda a expressão condicional se, ao avaliar apenas uma parte, ela já determina que a condição será TRUE ou FALSE. Esse comportamento é util para melhorar a performance do código: coloque as condições que impliquem em maior utilização de CPU ou memória no final da expressão (ou use um IF aninhado onde o IF externo avalia a condição de menor custo e, se ela é verdadeira, o IF interno avalia a condição de maior custo).
2.1) CASE statements
O CASE permite que selecionemos uma de muitas condições possíveis para ser executada. Existem 2 tipos de CASE statements: simple e searched.
O SIMPLE CASE STATEMENT avalia o resultado de 1 única expressão e executa uma de muitas condições baseadas no resultado dessa única expressão:
WHEN resultado THEN comandos ;
[WHEN resultadoN THEN comandos ;]
[ELSE comandos ;]
A expressão e os resultados podem ser valores escalares ou expressões que avaliam para valores escalares (mas a expressão continua sendo 1 única), por exemplo:
when ( (1 = 1) and (1 > 2) ) then dbms_output.put_line('a');
when ( (1 < 2) and null ) then dbms_output.put_line('b');
else dbms_output.put_line('c');
ATENÇÃO: a cláusula ELSE pode ou não ser obrigatória na simple case statement: se os resultados listarem TODOS os resultados possíveis da expressão, então a ELSE não é obrigatória; mas se os resultados não listarem todos os resultados possíveis da expressão e a expressão avaliar para um resultado não listado, a ELSE é obrigatória, caso contrário a PL/SQL levanta uma exception de CASE_NOT_FOUND. Teste isso com o código abaixo:
when 2 then dbms_output.put_line('2');
when 3 then dbms_output.put_line('3');
A SEARCHED CASE STATEMENT avalia o resultado de múltiplas expressões booleanas e executa os comandos da primeira que resulta TRUE:
WHEN expressão_booleana THEN comandos ;
[WHEN expressão_booleanaN THEN comandos ;]
O ELSE é opcional ou obrigatório dependendo se pelo menos uma expressão avalia para TRUE. Nesse caso o ELSE é opcional. Mas se nenhuma expressão avaliar para TRUE, o ELSE é obrigatório (caso contrário ocorre o erro CASE_NOT_FOUND). Teste isso com:
when 1 between 2 and 3 then dbms_output.put_line('a');
when 2 between 3 and 4 then dbms_output.put_line('b');
Da mesma forma que o IF-THEN-ELSIF-ELSE, as expressões na CASE devem ser mutuamente exclusivas (não obrigatório, mas importante para evitar bugs) e várias CASE podem ser aninhadas. Lembre-se também do modo de avaliação "curto" da PL/SQL e coloque as expressões mais freqüentes e/ou de menor utilização de CPU entre as primeiras opções.
Qual escolher, a SIMPLE ou a SEARCHED case statements? Usamos a SIMPLE CASE STATEMENT quando queremos que a decisão tenha como base o resultado de 1 única expressão; usamos a SEARCHED CASE STATEMENT quando queremos usar várias expressões booleanas que podem avaliar para TRUE, FALSE ou NULL.
2.2) CASE expressions
Agora a coisa fica um pouco confusa: as CASE EXPRESSION são quase a mesma coisa das case statements, mas são usadas para substituir expressões na SQL e na PL/SQL e retornar um valor. Por exemplo, estude os exemplos abaixo:
end as teste
v_teste varchar2(10) := null;
v_teste := case 'azul'
when 'vermelho' then 'errado'
when 'verde'    then 'errado'
when 'azul'     then 'certo'
else 'não sei'
dbms_output.put_line(v_teste);
when ('azul' = 'vermelho') then 'doido'
when ('azul' = 'verde')    then 'doido'
when ('azul' = 'azul')     then 'normal'
v_teste := case
Note que as CASE EXPRESSIONS, tanto as SIMPLE CASE EXPRESSION quanto as SEARCHED CASE EXPRESSION, são usadas para substituir uma expressão em SQL ou em PL/SQL e retornar um único resultado para a expressão.
ATENÇÃO: as CASE EXPRESSION são terminadas por "END" e não por "END CASE". Além disso, NÃO USAMOS o ponto-e-vírgula (;) nas CASE EXPRESSION, nem após o END final.
Por fim, o ELSE nas CASE EXPRESSION, diferentemente das CASE STATEMENT, é sempre opcional: se nenhuma situação das CASE EXPRESSION retornar o valor desejado e não existir um ELSE, a CASE EXPRESSION retorna NULL (não dá o erro de CASE_NOT_FOUND).
Somente como uma ilustração entre a diferança das case STATEMENT para as case EXPRESSION, veja um exemplo de CASE EXPRESSION usada para fornecer o resultado da expressão de uma CASE STATEMENT (estude as diferenças de sintaxe):
CASE (CASE 1
WHEN 'a' THEN dbms_output.put_line('O resultado é:');
WHEN 'b' THEN dbms_output.put_line('O resultado é:');
WHEN 'c' THEN dbms_output.put_line('O resultado é:');
ELSE dbms_output.put_line('O resultado é:');
dbms_output.put_line('Desconhecido');
O GOTO é um tipo de controle seqüencial, ou seja, não-condicional. Ele informa para a PL/SQL seguir incondicionalmente para outra parte do código no bloco. Sua sintaxe é simples:
O label é o identificador de um label de código definido por:
<<nome_do_label>>
Ao usar o GOTO, a PL/SQL executa os comandos imediatamente após o label.
O uso do GOTO pode ser útil, mas é extremamente raro. Obs.: deve existir obrigatóriamente um comando executável após o label (mesmo que seja o controle NULL).
O NULL é outro tipo de controle seqüencial (não-condicional) e serve para informar à PL/SQL para não fazer absolutamente nada. A sintaxe é:
Ele é usado, basicamente, para: melhorar a legibilidade do código; evitar a execução de comandos adicionais; pular várias checagens de validação quando uma falha.
Author Abrantes Araújo Silva FilhoPosted on 2015-09-16 2018-07-11 Categories Bancos de Dados, ProgramaçãoTags oracle, pl/sql4 Comments on Resumão de PL/SQL, parte 4: controles condicionais e seqüenciais
Resumão de PL/SQL, parte 3: fundamentos da linguagem
1) O bloco PL/SQL
A menor estrutura de código PL/SQL é o bloco, que pode ser anônimo ou nomeado. O bloco é dividido em 4 partes, das quais somente 1 é obrigatória:
Cabeçalho: opcional, usada apenas nos blocos nomeados, e determina a maneira pela qual o programa deve ser invocado;
Declaração: opcional, serve para declararmos variáveis, cursores, etc.;
Execução: obrigatória, é o programa propriamente dito;
Exceções: opcional, serve para definirmos o que fazer em caso de erros no programa especificado na parte de execução.
1.1) Bloco anônimo
Um bloco PL/SQL anônimo não tem nome e, por isso mesmo, não pode ser chamado por outros blocos. Ele serve como um contêiner que executa código PL/SQL, podendo chamar blocos nomeados (como funções ou procedures).
Esquematicamente o bloco anônimo tem a seguinte estrutura (seções entre colchetes são opcionais, palavras-chave estão em negrito):
... declarações de variáveis, etc. ... ]
... código PL/SQL ...
... código para manipulação de erros ... ]
1.2) Blocos nomeados
São blocos que têm nome, são armazenados no banco de dados, e podem ser chamados de outros blocos (nomeados ou anônimos). À diferença dos blocos anônimos, os blocos nomeados tem um cabeçalho que define o nome e o objetivo do bloco (por exemplo: é uma procedure, uma função, um trigger, etc.).
1.3) Blocos aninhados e escopo
Os blocos podem ser aninhados e isso define o escopo das variáveis existentes em cada bloco. As variáveis definidas nos blocos mais externos podem ser utilizadas nos blocos mais internos (o inverso não é verdadeiro).
Blocos aninhados podem ter um label através do uso de << e >>, por exemplo: <<bloco_local>>. Note bem: label não é nome, mas pode ser útil para qualificar as referências às variáveis e colunas.
1.4) Qualifique as referências às variáveis e colunas
Devemos qualificar o escopo das variáveis e colunas em nosso código SQL e PL/SQL para aumentar a legibilidade do código, garantir que o compilador sempre interpretará as variáveis e colunas da maneira como queremos, e evitar bugs à medida que o código aumenta e que alterações à estrutura das tabelas do banco de dados ocorrem (por exemplo: você pode ter uma variável v_total hoje mas, amanhã, alguém acrescentou uma coluna à uma tabela exatamente com o mesmo nome; pronto, seu código PL/SQL não funciona mais pois o compilador não sabe se o código se refere à variável v_total ou à coluna v_total da tabela).
Para saber se a qualificação é obrigatória ou opcional, temos que verificar a visibilidade da variável.
Um identificador visível pode ser referenciado apenas pelo seu nome, sem qualificação, e incluem:
Identificadores declarados no próprio bloco ou no bloco pai;
Objetos de banco de dados ou objetos PL/SQL que são possuídos pelo seu schema;
Um objeto que você não possui, mas que tem privilégios de acesso e que tenham um sinônimo que você pode ver
Um identificador invisível deve ser qualificado com um prefixo adequado para que o compilador PL/SQL saiba do que estamos tratando, por exemplo:
<schema>.<objeto>.<objeto>
Lembre-se que a ordem de resolução de identificadores é primeiro buscar um nome de coluna de banco de dados igual ao nome do identificador e, depois, buscar um nome declarado no código PL/SQL.
1.5) Programas aninhados
É um procedimento ou função que aparece totalmente dentro da seção de declaração de um bloco. Útil quando você terá uma parte de código que será reutilizado várias vezes dentro do bloco: você inclui a procedure ou função na seção de declaração do bloco, e pode reutilizá-la dentro do bloco, em vários locais, chamando seu nome.
2) Os caracteres aceitos em PL/SQL
PL/SQL é case-insentive (o compilador enxerga tudo como uppercase) e aceita os caracteres: A-Z, a-z, 0-9, whitespace (tab, espaço, newline, carriage return) e símbolos (~ ! @ # $ % * () _ – + = | : ; " ' < > , . ? / ^). Alguns desses símbolos têm significado especial em SQL e em PL/SQL:
; = termina os statements e declarações
% = indicador de atributo
@ = indicador de localização
<< e >> = delimitadores de labels
:= é indicador de atribuição de valor à variáveis, etc.
=> é operador de associação para notação posicional
. . (dois pontos) é operador de range
Esses caracteres e símbolos se combinam para formar as unidades léxicas da linguagem, ou seja, os menores componentes individuais, que são os identificadores, os literais, os delimitadores e os comentários.
2.1) Identificadores
Um identificador é simplesmente um nome para um objeto PL/SQL, ou uma palavra reservada. As regras para os identificadores são as seguintes:
Devem começar com uma letra
Podem incluir $, _ e #
Não podem conter whitespace
Adote convenções de nomes para seus identificadores, por exemplo prefixar parâmetros com p_, funções com fn_, triggers com trg_, packages com pkg_, variáveis com v_, etc.
Palavras reservadas são identificadores que têm um significado especial para a SQL e a PL/SQL (por exemplo: END, BEGIN, SELECT, etc.) ou identificadores definidos nas bult-in package STANDARD e DBMS_STANDARD.
As palavras reservadas especiais da SQL e da PL/SQL não podem ser utilizadas de jeito nenhum, o compilador dá erro. Já os identificadores das packages STANDARD e DBMS_STANDARD não são reservados, ou seja, você pode utilizá-los, embora isso não seja recomendado.
Para conhecer a lista de palavras reservadas, pesquisa a view V$RESERVED_WORDS e evite utilizar alguma coisa listada ali (no Oracle 12.1.0.1.0 existem 2089 identificadores, sendo 100 palavras reservadas e o restante identificadores das packages STANDARD e DBMS_STANDARD).
2.2) Literais
Literais são valores que não são representados por um identificador, ou seja, são apenas simples valores. Os literais podem ser:
2.2.1) Literais string
Literais de string devem ser delimitados por aspas simples e são case-sensitive.
Na PL/SQL e na SQL, null é usualmente a mesma coisa que um string sem nenhum caracter, ", mas o comportamente é diferente se a variável em questão é varchar2 ou char: se for varchar2, PL/SQL realmente deixa null, mas se for char a PL/SQL preenche com espaços, tornando-a not null. Cuidado com isso se for usar variáveis e colunas char.
Um efeito colateral do uso obrigatório das aspas simples ao redor dos literais de string é que quando você precisa usar um string que contém uma aspas simples em seu interior, por exemplo Mc'Donalds, você tem que prefixar a aspas simples com outra aspas simples, por exemplo Mc"Donalds, ou usar delimitadores q" customizados, por exemplo q'{Mc'Donolds}'. Verifique os exemplos:
SELECT ''''''          FROM DUAL;
SELECT ''' '''         FROM DUAL;
SELECT ' '' '' '       FROM DUAL;
SELECT 'Mc''Donalds'   FROM DUAL;
SELECT q'{Mc'Donalds}' FROM DUAL;
SELECT q'xMc'Donaldsx' FROM DUAL;
SELECT q'!Mc'Donalds!' FROM DUAL;
2.2.2) Literais numéricos
Literais numéricos podem ser números inteiros ou números reais (quando têm parte fracionária, mesmo que a parte fracionária seja 0).
Também podemos usar notação científica (ex.: 3.05E8) e, a partir do Oracle 10g, um número real pode ser do datatype NUMBER, da própria Oracle, ou do datatype BINARY (32 bits, com sufixo f) ou BINARY DOUBLE (64 bits, com sufixo d), do padrão IEEE754.
Em geral eu não uso os datatypes da IEEE754 por causa do problema de cálculo com esses números de pontos flutuantes pois dão erro em algumas situações. Verifique as queries abaixo: a primeira o datatype é NUMBER, da Oracle, e as outras o datatype BINARY e com o BYNARY DOUBLE; veja qual dá o resultado correto:
SELECT 1 * (0.5 -0.4 -0.1) FROM dual;
SELECT 1f * (0.5f -0.4f -0.1f) FROM dual;
SELECT 1d * (0.5d -0.4d -0.1d) FROM dual;
2.2.3) Literais booleanos
São a representação do verdadeiro e falso, TRUE e FALSE, e não devem ter aspas simples ou duplas. Note que uma variável booleana também pode ser null.
Ao usuar variáveis booleanas em uma expressão, a variável pode falar por si mesma. As duas formas abaixo são iguais:
IF v_booleana = TRUE THEN ... ELSE ... END IF;
IF v_booleana THEN ... ELSE ... END IF;
2.3) Delimitadores
Os statements e declarations da PL/SQL são delimitados por um ;
2.4) Comentários
Podem ser em uma únia linha, iniciando com 2 hífens (–), ou de múltiplas linhas, entre /* e */.
3) A keyword PRAGMA
O pragma é uma maneira de passar opções ao compilador PL/SQL que podem resultar em comportamentos diferentes do programa durante o runtime. Ela pode ser usada na seção de declaração de um bloco PL/SQL, com a seguinte sintaxe:
PRAGMA <instrução ao compilador>;
Algumas das instruções ao compilador que podem ser utilizadas são:
AUTONOMOUS_TRANSACTION: faz com que o programa execute um commit ou rollback dentro do bloco corrente sem afetar a transação principal;
EXCEPTION_INIT: associa um número de erro particular com um identificador que você declarou como uma exception;
RESTRICT_REFERENCES: informa o purity level de um programa ao compilador;
SERIALLY_REUSABLE: informa que dados ao nível da package não devem persistir entre referências a esses dados.
O label serve para dar um apelido a uma parte particular de seu programa, geralmente um bloco aninhado (mas você pode dar um label para um bloco anônimo também), podendo .
Um label também serve basicamente como:
Qualificador para as referências às variáveis, colunas e outros elementos;
Alvo para uma instrução GOTO;
Alvo de uma instrução EXIT de um loop aninhado sem executar o loop exterior.
Author Abrantes Araújo Silva FilhoPosted on 2015-09-09 2018-07-11 Categories Bancos de Dados, ProgramaçãoTags oracle, pl/sql1,763 Comments on Resumão de PL/SQL, parte 3: fundamentos da linguagem
Resumão de PL/SQL, parte 2: criar e rodar código PL/SQL
Como a PL/SQL é uma embedded language, ela não roda por si só, precisa ser executada dentro de um ambiente Oracle (Oracle Database, SQL*Plus, Oracle Forms, etc.). Em geral o código PL/SQL precisa estar carregado no banco de dados Oracle para ser compilado e executado.
Para escrever código PL/SQL você pode usar uma IDE gráfica ou rodar scripts na linha de comando. Existem várias escolhas para a IDE e poucas para a linha de comando:
IDEs recomendadas: Oracle SQL Developer, Allround Automations PL/SQL Developer, Toad, SQL Navigator e outras;
Linha de comandos: Oracle SQL*Plus (instalado junto com o database server ou com as ferramentas clientes da Oracle).
Eu uso o Oracle SQL Developer que me permite, além da IDE, utilizar comandos do SQL*Plus "simulando" o ambiente de linha de comandos.
Algumas dicas de SQL*Plus:
Toda vez que você conecta no SQL*Plus, ele roda um arquivo de startup onde você configurar várias preferências. Na verdade, ele checa a existência de 3 arquivos:
$ORACLE_HOME/sqlplus/admin/glogin.sql: utilizado para executar comandos comuns para todos os usuários;
<diretório de trabalho>/login.sql: utilizado para executar comandos para seu usário;
login.sql encontrado via SQLPATH: utilizado para executar comandos para seu usuário.
Crie um arquivo de startup para o SQL*Plus, com as seguintes configurações:
DEFINE _EDITOR = /bin/vi
Atenção: sempre que você se disconecta normalmente do SQL*Plus, se houver transações pendentes, ele dá um COMMIT. Cuidado com isso! Não se esqueça de dar commit ou rollback por conta própria!
Para avisar ao SQL*Plus que você já acabou de digitar seu PL/SQL, deve incluir uma barra na próxima linha, sem mais nenhum outro caractere). Essa barra diz ao SQL*Plus para executar o statement mais recente digitado.
Para rodar um script que está em seu diretório de trabalho (ou que possa ser encontrado pela variávevl SQLPATH) use o comando @ ou o START. Se esse script chamar outros scripts, a chamada deve ser feita com o comando @@ para que o SQL*Plus entenda que a chamada está sendo feita a partir do diretório onde o script pai foi rodado.
Existem 2 tipos de variáveis no SQL*Plus: DEFINE e BIND.
Uma variável define é nada mais que a definição de um valor através do comando DEFINE, por exemplo:
DEFINE x = 'teste';
DEFINE y = 50;
Para usar uma variável define, utilize o operador &:
SELECT &y FROM DUAL;
Para usar uma variável bind, ela tem que ser declarada primeiro, e depois um valor é atribuído à ela, por exemplo:
VARIABLE x VARCHAR2(15);
Note que agora há 2 variáveis "x" e 2 variáveis "y", uma define e uma bind. Execute a seqüência abaixo para entender a diferença:
define x = 'teste define';
define y = 10;
print :y;
:x := 'teste bind';
:y := 2;
SELECT '&x' "x define",
:x   "x bind",
&y   "y define",
:y   "y bind"
Para salvar o output do SQL*Plus em um arquivo, use o comando SPOOL:
SET SPOOL /path/arquivo.txt
<comandos ...>
Se estamos executando um script e queremos que o SQL*Plus pare se houver algum erro, devemos usar o comando (o rollback é opcional):
WHENEVER SQLERROR EXIT SQL.SQLCODE [ROLLBACK]
Se estamos criando algum PL/SQL e ocorreram erros de compilação, podemos usar o comando abaixo para ver os erros (as categorias opcionais são: dimension, function, java source, java class, package, package body, procedure, trigger, type, type body e view):
SHOW ERRORS [CATEGORY [SCHEMA.] OBJECT]
Tarefas PL/SQL essenciais: criar/compilar, executar e exibir o código
Para criar código em PL/SQL, temos que usar algum CREATE statement, por exemplo, create function, create procedure, etc., quando queremos que o código fique armazenado no banco de dados.
Ao criar algum código PL/SQL, ele é "compilado" pelo banco de dados, ou seja, é checado, validado e armazenado no banco. Se ocorreram erros de compilação, podemos usar o comando abaixo para ver os erros (as categorias opcionais são: dimension, function, java source, java class, package, package body, procedure, trigger, type, type body e view):
É muito importante colocar o SHOW ERRORS após cada comando create de um PL/SQL!
Para executar um código PL/SQL, é a coisa mais fácil do mundo: chame o nome do código criado e pronto. Podemos também código que não fica armazenado no banco de dados, usanto um bloco simples (begin … end;); também podemos usar o comando EXECUTE do SQL*Plus.
Para exibir código PL/SQL, podemos consultar a view USER_OBJECTS, para ver o que temos armazenado, e depois usar o comando DESCRIBE (sim, describe funciona com tabelas, views, object types, procedures e packages). Aliás, o DESCRIBE é muito útil para uma rápida visualização dos parâmetros dos procedures.
Para exibir o código fonte, consulte a view USER_SOURCE ou view TRIGGER_SOURCE.
Gerenciar grants e sinônimos para PL/SQL armazenado
Quando você cria um PL/SQL só você e um DBA podem executá-lo. Para permitir que outros usuários possam aproveitar a funcionalidade que você desenvolveu, precisa dar um grant:
GRANT EXECUTE ON <schema>.<código> TO {user|role|public};
REVOKE EXECUTE ON <schema>.<código> FROM {user|role|public};
Para ver uma lista dos privilégios que você concedeu a outros usuários ou papéis, consulte a view USER_TAB_PRIVS_MADE.
Para faciliar o uso dos códigos, você pode criar um sinônimo (privado ou público) para o PL/SQL.
CREATE OR REPLACE [PUBLIC] SYNONYM <sinônimo> FOR <schema>.<object>
DROP SYNONYM <sinônimo>
Apagar um código armazenado
Basta usar um DROP statement.
Esconder a fonte PL/SQL
Você pode "criptografar" o código fonte PL/SQL com o utilitário wrap do Oracle, mas isso não é uma criptografia verdadeira: seu código fonte ainda pode ser recuperado!
Author Abrantes Araújo Silva FilhoPosted on 2015-09-07 Categories Bancos de Dados, ProgramaçãoTags oracle, pl/sql, sql, sql*plus1,021 Comments on Resumão de PL/SQL, parte 2: criar e rodar código PL/SQL
Resumão de PL/SQL, parte 1: introdução
Esta série de artigos é um resumão dos capítulos do livro Oracle PL/SQL Programming, 6ª edição, de Steven Feuerstein e Bill Pribyl, que estou estudando. O objetivo deste resumão é manter aqui um lembrete para as coisas mais importantes que aprendi e que podem ser úteis no futuro.
PL/SQL significa "Procedural Language EXTENSIONS to Structured Query Language", ou seja, é uma EXTENSÃO procedural à linguagem SQL do banco de dados Oracle.
Se você trabalha com, ou programa qualquer coisa para, o Oracle Database terá que, em algum momento, escrever código em PL/SQL. Sendo uma extensão procedural à SQL, a PL/SQL agrega várias coisas ao SQL do Oracle:
Condicionais (if, then, else, case, etc.)
Funçṍes, procedures
Tratamento de exceptions e erros
Variáveis, arrays, datatypes, etc.
Várias packages built-in com funcionalidades excepcionais
A PL/SQL não é executável, ou seja, para rodar o código deve estar compilado dentro do Oracle Database ou de alguma ferramenta cliente capaz de executar código PL/SQL, como o SQL*Plus ou o Oracle Forms.
A cada release do Oracle Database uma nova versão da PL/SQL é lançada, com várias inovações para os programadores. Hoje, falar que "isso não pode ser feito com PL/SQL" é mera desculpa.
Author Abrantes Araújo Silva FilhoPosted on 2015-09-03 2015-09-03 Categories Bancos de Dados, ProgramaçãoTags oracle, pl/sql, sql1,015 Comments on Resumão de PL/SQL, parte 1: introdução