Mostrando postagens com marcador PL/SQL. Mostrar todas as postagens
Mostrando postagens com marcador PL/SQL. Mostrar todas as postagens

sábado, 23 de maio de 2009

Cursor with parameters in PL/SQL

Ver este post em Portugûes

Here I'm gonna show another technique to work with cursors in PL/SQL. I had to do something at work where it was very useful: cursor that take arguments.

The biggest advantages of parametrized queries is that the server doesn't have to pass through the parse and plan phases ever again (after the first time, of course) and the compile-time checking for syntax errors (when we use string queries we only know any syntax errors in runtime).

That said let's go to examples:


cursor ProductCursor(pname in varchar2) is
select *
from product
where name like pname;


Nice, isn't it? It's like a procedure in terms of syntax. And opening the cursor is like this:


open ProductCursor('A%');


It'll open the cursor with all the products which name starts with 'A'.

We can create a rowtype based var from this cursor:


prod ProductCursor%rowtype;


Now I'm gonna join it all in a script:


declare
-- Cursor
cursor ProductCursor(pname in varchar2) is
select *
from product
where name like pname;

-- Simple loop
prod ProductCursor%rowtype;

-- bulk collect loop
type Tprods is table of ProductCursor%rowtype;
prods Tprods;
i integer;

begin
-- simple loop
open ProductCursor('A%');
loop
fetch ProductCursor into prod;
exit when ProductCursor%notfound;
DBMS_OUTPUT.put_line(prod.nome);
-- other commands
end loop;
close ProductCursor;

-- bulk collect loop
open ProductCursor('A%');
fetch ProductCursor bulk collect into prods;
for i in prods.first .. prods.last loop
DBMS_OUTPUT.put_line(prods(i).nome);
-- another commands using prods(i)
end loop;
close ProductCursor;
end;


Cool, isn't it? See you next time.

Artigo completo (View Full Post)

Cursor com parâmetros em PL/SQL

View this post in English

Aqui vou mostrar outra técnica para trabalhar com cursores em PL/SQL. Eu tive que fazer um trabalho recentemente onde isso foi muito útil: Cursores que aceitam argumentos.

As maiores vantagem de usar queries parametrizadas no banco de dados é que o servidor não precisa fazer o parse e traçar um plano de execução (ele só faz isso na primeira vez que a query é executada) e ela fica certa em tempo de compilação (ao contrário das queries em string que nós só sabemos se há um erro de sintaxe em tempo de execução).

Dito isso vamos a um exemplo:


cursor ProdutoCursor(pnome in varchar2) is
select *
from produto
where nome like pnome;


Legal, não? A sintaxe fica muito parecida com uma procedure. E o método de abrir o cursor também é legal:


open ProdutoCursor('A%');


Aqui vai abrir o cursor trazendo todos os produtos onde o nome começa com a letra 'A'.

Podemos ainda criar um rowtype a partir deste cursor:


prod ProdutoCursor%rowtype;


Agora vou criar um script para juntar tudo:


declare
-- Cursor
cursor ProdutoCursor(pnome in varchar2) is
select *
from produto
where nome like pnome;

-- Para loop simples
prod ProdutoCursor%rowtype;

-- Para loop com bulk collect
type Tprods is table of ProdutoCursor%rowtype;
prods Tprods;
i integer;

begin
-- Loop simples
open ProdutoCursor('A%');
loop
fetch ProdutoCursor into prod;
exit when ProdutoCursor%notfound;
-- comandos a serem executados com o registro prod
DBMS_OUTPUT.put_line(prod.nome);
end loop;
close ProdutoCursor;

-- Loop com bulk collect
open ProdutoCursor('A%');
fetch ProdutoCursor bulk collect into prods;
for i in prods.first .. prods.last loop
-- comandos a serem executaos com o registro prods(i)
DBMS_OUTPUT.put_line(prods(i).nome);
end loop;
close ProdutoCursor;
end;


Muito legal, né? Até a próxima.

Artigo completo (View Full Post)

terça-feira, 30 de dezembro de 2008

Oracle - Pl/SQL No Alter Table: Constraints e Checks, sem usar o comando

No artigo anterior demonstrei varias possibilidades de usar o camando “ALTER TABLE” no Oracle. Contudo, algumas vezes acho mais conveniente para mim definir as constraints e checks imediatamente quando estou criado a tabela. Acho que para a prática de documentação, versionamento de código/script, se toda a definição estiver no mesmo comando é mais eficiente o versionamento do script da tabela durante a evolução e manutenção do banco de dados.
A partir do que foi exemplificado no artigo anterior, vou definir todas as constraints e checks na criação das tabelas “ENDERECO_ZN” e “Tipo_EnderecoZN”. Veja a seguir ...



DROP TABLE TIPO_ENDERECOZN;
CREATE TABLE TIPO_ENDERECOZN (
TIPO_ENDERECOID NUMBER(10) NOT NULL,
TIPO_ENDERECO VARCHAR2(100) NOT NULL,
DATACADASTRO DATE NOT NULL,
CONSTRAINT PK_TIPO_ENDEZN PRIMARY KEY (TIPO_ENDERECOID),
CONSTRAINT CHK_TIPO_ENDERECO CHECK (LENGTH(TIPO_ENDERECO) > 5),
CONSTRAINT CHK_DATACADASTRO_TP CHECK (DATACADASTRO > TO_DATE('31/12/2000', 'DD/MM/YYYY'))
);

Na linha 1, primeiro excluo a tabela do banco, para em seguida recriá-la.
a partir da linha 6 começamos a definir as constraints.
Na Lina 6, defino quem é a chave primária.
Na linha 7 validação para a coluna “TIPO_ENDERECO”.
Na linha 8 validação para a coluna “DATACADASTRO”.

Agora vamos fazer o mesmo para a tabela “ENDERECO_ZN”:

DROP TABLE ENDERECO_ZN;
CREATE TABLE ENDERECO_ZN
(
ENDERECO_ZNID NUMBER(10) NOT NULL,
TIPO_ENDERECOID NUMBER(10),
LOGRADOURO VARCHAR2(130) NOT NULL,
COMPLEMENTO VARCHAR2(170),
BAIRRO VARCHAR2(50),
CIDADE VARCHAR2(50),
ESTADO VARCHAR2(2),
CEP VARCHAR2(10),
NUMERO VARCHAR2(10),
DATACADASTRO DATE NOT NULL,
CONSTRAINT PK_ENDERECOZN PRIMARY KEY (ENDERECO_ZNID),
CONSTRAINT FK_ENDERECO_ZN_TP_END FOREIGN KEY (TIPO_ENDERECOID) REFERENCES TIPO_ENDERECOZN (TIPO_ENDERECOID),
CONSTRAINT CHK_ESTADO CHECK (LENGTH(ESTADO) > 1),
CONSTRAINT CHK_CIDADE CHECK (LENGTH(CIDADE) > 4),
CONSTRAINT CHK_DATACADASTRO CHECK (DATACADASTRO > TO_DATE('31/12/2000', 'DD/MM/YYYY'))

);


Artigo completo (View Full Post)

Oracle - Pl/SQL Alter Table (Variações sobre o tema): Modify, Add Constraint, Add, Drop e Rename

Com o comando “ALTER TABLE” podemos realizar diversas modificações estruturais em tabelas de banco de dados. A seguir, experimentaremos como isso pode ser feito em PL/SQL.


Trabalharemos com a tabela exemplificada abaixo:

CREATE TABLE ENDERECO_ZN
(
CODENDERECO_ZN NUMBER(10) NOT NULL,
ID_TIPO_ENDERECO NUMBER(10),
LOGRADOURO VARCHAR2(100),
COMPLEMENTO VARCHAR2(50),
BAIRRO VARCHAR2(50),
CIDADE VARCHAR2(50),
ESTADO VARCHAR2(2),
CEP VARCHAR2(10),
NUMERO VARCHAR2(10)
);


A primeira coisa que vou fazer é alterar o nome de uma das colunas:

Alterando o nome da coluna: De “CODENDERECO_ZN” para “ENDERECO_ZNID”

ALTER TABLE
ENDERECO_ZN
RENAME COLUMN
CODENDERECO_ZN
TO
ENDERECO_ZNID


Agora vamos alterar algumas outras coisas numa coluna. Que tal o tamanho e torná-la obrigatória?


-- Alterando o tamanho da coluna e tornando-a obrigatória

ALTER TABLE ENDERECO_ZN MODIFY COMPLEMENTO varchar2(150) not null;

Veja como está a nossa tabela após essas primeiras alterações ...
-- Create table
create table ENDERECO_ZN
(
ENDERECO_ZNID NUMBER(10) not null,
ID_TIPO_ENDERECO NUMBER(10),
LOGRADOURO VARCHAR2(100),
COMPLEMENTO VARCHAR2(150) not null,
BAIRRO VARCHAR2(50),
CIDADE VARCHAR2(50),
ESTADO VARCHAR2(2),
CEP VARCHAR2(10),
NUMERO VARCHAR2(10)
);


Podemos aplicar modificações em mais de uma coluna no mesmo comando. Veja ilustrado abaixo como fazer:

-- Alterando mais de uma coluna no mesmo comando
ALTER TABLE ENDERECO_ZN MODIFY (COMPLEMENTO varchar2(170) null, LOGRADOURO varchar2(130) not null);


No exemplo acima alteramos tanto o tamanho das duas colunas, quanto a obrigatoriedade das mesmas. Aprimeira definimos para aceitar valores nulos e a segunda para não aceitar. Vejamos como ficou a tabela “ENDERECO_ZN”.


create table ENDERECO_ZN
(
ENDERECO_ZNID NUMBER(10) not null,
ID_TIPO_ENDERECO NUMBER(10),
LOGRADOURO VARCHAR2(130) not null,
COMPLEMENTO VARCHAR2(170),
BAIRRO VARCHAR2(50),
CIDADE VARCHAR2(50),
ESTADO VARCHAR2(2),
CEP VARCHAR2(10),
NUMERO VARCHAR2(10)
);


Podemos também adicionar mais de uma coluna com o mesmo comando
-- Adicionando novas colunas

Alter table ENDERECO_ZN
ADD ( Pais varchar2(50),
DataCadastro Timestamp not null);


Mais uma vez, a tabela ENDERECO_ZN refletindo o comando anterior:
     
create table ENDERECO_ZN
(
ENDERECO_ZNID NUMBER(10) not null,
ID_TIPO_ENDERECO NUMBER(10),
LOGRADOURO VARCHAR2(130) not null,
COMPLEMENTO VARCHAR2(170),
BAIRRO VARCHAR2(50),
CIDADE VARCHAR2(50),
ESTADO VARCHAR2(2),
CEP VARCHAR2(10),
NUMERO VARCHAR2(10),
PAIS VARCHAR2(50),
DATACADASTRO TIMESTAMP(6) not null
)


Supondo que a coluna “Pais” não seja adequada ao modelo de negócio em questão, vamos excluí-la da tabela.


-- Dropnado uma coluna
Alter table ENDERECO_ZN drop column Pais


Agora vamos alterar o tipo de dado de uma das colunas

-- Alterando o tipo de dado de uma coluna
ALTER TABLE ENDERECO_ZN MODIFY DATACADASTRO Date;


Outra vez, alteramos a tabela ...

create table ENDERECO_ZN
(
ENDERECO_ZNID NUMBER(10) not null,
ID_TIPO_ENDERECO NUMBER(10),
LOGRADOURO VARCHAR2(130) not null,
COMPLEMENTO VARCHAR2(170),
BAIRRO VARCHAR2(50),
CIDADE VARCHAR2(50),
ESTADO VARCHAR2(2),
CEP VARCHAR2(10),
NUMERO VARCHAR2(10),
DATACADASTRO DATE not null
)



O Comando “ALTER TABLE” também é uma forma de definirmos constraits. Vejamos ...


Definindo uma Chave primária: Suponha que você esta trabalhando com uma tabela que foi crianda a muitos anos por outros programadores. Esses programadores não definiram nenhuma coluna como chave primária. Mas hoje, você precisa fazer isso. Hoje você precisa que esta tabela possua uma PK. Vaja uma forma de fazer isso ilustrada abaixo:



-- Pk
alter table ENDERECO_ZN add primary key (ENDERECO_ZNID);


Note que eu preparei a tabela para estar relaciona a outra. A coluna “ID_TIPO_ENDERECO” deveria ser uma chave estrangeira para uma tabela que armazenasse os tipos de Endereço. Esse tipo de modelagem atende a uma normalização muito comum. Portanto, antes de declararmos a chave estrangeira vamos criar a tabela de tipo de endereço:


-- Crinado a tabela de Tipo de Endereço
Create Table Tipo_EnderecoZN (
ID_TIPO_ENDERECO NUMBER(10) not null,
Tipo_Endereco Varchar2(100) not null
);

alter table Tipo_EnderecoZN add primary key (ID_TIPO_ENDERECO);

Na linha 7 do trecho de código acima, repeti o mesmo comando, que usei para criar a chave primária para tabela Enreco_ZN, para definir a coluna “ID_TIPO_ENDERECO” como PK (Primary Key – Chave primária) em “Tipo_EnderecoZN”.

Agora vamos criar a constraint que garantirá a integridade referencial entre as tabelas:


Alter table Endereco_ZN add constraint FK_Endereco_TpEndereco foreign key (ID_TIPO_ENDERECO) references Tipo_EnderecoZN (ID_TIPO_ENDERECO);


Qualidade


Para garantir a qualidade dos dados que serão persistidos nessas tabelas podemos definir regras de validação dos dados (Veja mais sobre isso em “Banco de Dados ou Bando de Dados? ”).


-- Definido regras de validação de dados - checks
Alter table Tipo_EnderecoZN Add CONSTRAINT chk_Tipo_Endereco CHECK (LENGTH(Tipo_Endereco) > 5);
Alter table Endereco_ZN Add CONSTRAINT chk_DATACADASTRO CHECK (DATACADASTRO > TO_DATE('01/01/2000', 'DD/MM/YYYY'));
Alter table Endereco_ZN Add CONSTRAINT chk_BAIRRO CHECK (LENGTH(BAIRRO) > 3);
Alter table Endereco_ZN Add CONSTRAINT chk_ESTADO CHECK (LENGTH(ESTADO) > 1);


Qunado for necessário "dropar" uma constraint ...

ALTER table Endereco_ZN drop constraint FK_Endereco_TpEndereco;


Observe que não referenciei o nome da constraint entre plics.

Obviamente, você deve abusar de ser criterioso ao definir uma regra como essas. Visto que, se ela não definida com exatidão em total harmonia com as regras de negócio (da empresa, para qual o sistema gerenciador de banco de dados está sendo desenvolvido) você terá sérios problemas. Por exemplo, com relação ao trecho de código anterior:
Na linha 1 definimos que na tabela Tipo_EnderecoZN, a coluna Tipo_Endereco não vai aceitar nenhum valor cujo número de caracteres seja menor que 5. Portanto, se tentarem inserir o valor “Resid” (abreviação de “Residencial”) será frustrado. Isso tanto pode ser muito útil, evitando que valores lixo sejam inseridos no banco, mas pode ser prejudicial caso exista alguma possibilidade de uma valor de, por exemplo, três caracteres precise ser inserido na coluna em questão.
Na linha 2, definimos um valor mínimo para a coluna “DataCadastro”, da tabela “ENDERECO_ZN”. Isso vai evitar valores de data do tipo “01/02/0009”. Esse valor de data não é uma valor inválido, entretanto pode causar problema.


Artigo completo (View Full Post)

terça-feira, 4 de novembro de 2008

VB6 & Oracle - Inserindo Dados

O Modelo Cliente/Servidor caracteriza-se por centralizar grande parte dos processos de um sistema de informação no SGBD. Esses processos são programados em forma de UDFs, Procedures e Triggers.
Neste artigo veremos como criar um procedure no Oracle, para em seguida desenvolvermos um aplicativo em VB6 o qual ira chamar a execução da mesma.

Iniciaremos então pelo banco de dados, veja abaixo o código da procedure:



Back-End Script de Banco de Dados

-- Criando a tabela para testar insert apartir de um front-end vb6
CREATE TABLE TESTEGM (
COD VARCHAR(3),
DESCRICAO VARCHAR(10)

);

-- Procedimento para insert - exemplo de arquitetura cliente/servidor
CREATE OR REPLACE PROCEDURE PROC_TESTE_GM (
PCOD IN VARCHAR,
PDESCRICAO IN VARCHAR,
PAUX_RETORNO OUT VARCHAR)
AS
BEGIN
INSERT INTO TESTEGM
(COD, DESCRICAO) VALUES (PCOD, PDESCRICAO);
COMMIT;
PAUX_retorno := 'OPERAÇÃO REALIZADA COM SUCESSO!';
EXCEPTION
WHEN OTHERS THEN
PAUX_retorno := SQLERRM || ' INSERT DISTRATO ERRO.' ; -- RETORNA A MENSAGEM COM CÓDIGO E DESCRIÇÃO DO ERRO ORACLE
ROLLBACK;

END;


Front-end Código do lado Cliente

Neste exemplo, aproveito para demostrar um pouco de modularização. Criei duas funcionalidades, uma que se preocupa com a conexão com o banco, "GetConnectionCRUD". Essa "Sub" retorna um "ADO Command" conectado e configurado como "procedure". Uma segunda rotina, "BuildParams", monta os parâmetros do "ADO Command" (o qual recebe como parâmetro) dinamicamente e atribui valor para os mesmos.


Private Sub BuildParams(ByRef obj As ADODB.Command)
'Cria o parâmetro de saida no commad
objInsertCommandAux.Parameters.Append objInsertCommandAux.CreateParameter("PAUX_RETORNO", adBSTR, adParamOutput)
'Cria os parâmetros de entrada nos commads já passando o valor para eles
obj.Parameters.Append objInsertCommandAux.CreateParameter("PCOD", adBSTR, adParamInput, , Text1.Text)
obj.Parameters.Append objInsertCommandAux.CreateParameter("PDESCRICAO", adBSTR, adParamInput, , Text2.Text)

End Sub

Private Function GetConnectionCRUD() As ADODB.Command
' Retorna um objeto (um activeX) ja conectado ao banco
' Dessa forma podemos de forma coesa, isolada definir uma conexão com o banco
Dim objCommandAux As New ADODB.Command
Dim MyConn As New ADODB.Connection

MyConn.Open "Provider=MSDAORA;Data Source=desvbancoteste;User ID=sistema;Password=landjah;"
objCommandAux.ActiveConnection = MyConn
objCommandAux.CommandType = adCmdStoredProc
Set GetConnectionCRUD = objCommandAux

End Function


Em seguinda codificaremos no evento OnClick de um botão a execução dos procedimentos, "Caixa Preta", descritos acima.


Dim objInsertCommandAux As New ADODB.Command
Dim AuxRetorno As String
' Para trabalar desconectado do banco ...
Dim rsZN As New ADODB.Recordset

Private Sub Command1_Click()

Set objInsertCommandAux = GetConnectionCRUD()
' Define que o command criado executará proc de Insert
objInsertCommandAux.CommandText = "PROC_TESTE_GM"


BuildParams objInsertCommandAux

'*** Executando a stored procedure
objInsertCommandAux.Execute

MsgBox (objInsertCommandAux("PAUX_RETORNO"))

'Destroi o objeto
Set objInsertCommandAux = Nothing

End Sub


Artigo completo (View Full Post)

segunda-feira, 3 de novembro de 2008

Banco de Dados ou Bando de Dados?

O fato de se gastar na ordem dos milhares por uma tecnologia de banco de dados relacional, tipo Oracle, MS SQL Server, IBM DB2, etc, não garante por si só que você terá informações consistentes.
Se você deseja algum dia extrair informações que agreguem valor ao seu negócio, trate de garantir que o que entra no seu banco de dados não seja lixo.
Supondo que, no que tange a modelagem de dados seu banco seja impecável. Suponhamos ainda, que você comprou o que existe de mais poderoso em termos de hardware, ainda assim existe a possibilidade desses dois fatores serem totalmente irrelevantes caso não exista mecanismos de checagem, neste suposto data base, a fim de garantir que o dado que é “inputado” não possui qualidade.
Para isso existem as constraints, checks e domains. Através deles programamos regras no banco de dados que trabalharão para que tudo que for inserido nele seja consistente, compreensível, relevante (Leia mais sobre ... ).

Veja um exemplo desse tipo de controle:

-- Criando a tabela de Tipo de Contato
CREATE TABLE ZnTipo_Contato(
TIPO_ContatoID NUMBER (8,0) PRIMARY KEY,
DESCRICAO VARCHAR2(20) NOT NULL,
DT_CADASTRO TIMESTAMP NOT NULL,
UpdateDate TIMESTAMP NOT NULL,
USERUPDATE NUMBER (8,0) NOT NULL,
CONSTRAINT chk_DESCRICAO CHECK (LENGTH(DESCRICAO) > 4)
);


No trecho de código acima criei uma tabela que possui nela duas constraints, ou seja, duas checagens de controle. Uma que não permitirá valores repetidos (Primary Key) na coluna “Tipo_ContatoID” (linha 3). A outra, não vai permitir valores com menos de 4 caracteres na coluna “DESCRICAO” (linha 8).

Comentários nos Objetos de Banco de Dados

Outro ponto importante que vai ser de extrema relevância para que seu banco de dados passe longe de ser o “bando de dados” mais high tech do mundo é o cuidado em comentar cada objeto construído no banco. Isso deve ser considerado uma cultura a ser desenvolvida, tanto pela equipe, num âmbito gerencial, quanto individualmente (Ou seja, aonde quer que você vá leve esse bom hábito).

Pense, com que cara você vai ficar quando daqui a três meses alguém lhe perguntar qual o significado da coluna “ValPerJurosPgto” e você não lembrar? Todavia, para a mesma situação, se houver um comentário na coluna sua orelha vai ser eternamente grata por continuar cinco palmos menor (hehehhe), além disso seu bolso irá lhe agradecer o salário continuar entrando.

COMMENT ON TABLE ZnTipo_Contato IS 'Tabela de referência. Armazena os possíveis Tipos de Contato. Exemplo: Email, Telefone, Celular';
COMMENT ON COLUMN ZnTipo_Contato.TIPO_ContatoID IS 'Primary key da tabela ZnTipo_Contato.';
COMMENT ON COLUMN ZnTipo_Contato.DESCRICAO IS 'Descrição do Tipo de Contato.';
COMMENT ON COLUMN ZnTipo_Contato.DT_CADASTRO IS 'Data de Cadastro do registro na tabela ZnTipo_Contato.';
COMMENT ON COLUMN ZnTipo_Contato.UpdateDate IS 'Data em que o usuário do sistema que atualizou um registro de Tipo de Contato.';
COMMENT ON COLUMN ZnTipo_Contato.USERUPDATE IS 'Usuário do sistema que atualizou um registro de Tipo de Contato.';


Agora vamos fazer alguns testes ...

-- Criando a Sequence para geração de Uk para a tabela ZnTipo_Contato
CREATE SEQUENCE SQ_ZnTipo_Contato
MINVALUE 1
MAXVALUE 999999999999999999999999999
START WITH 1
NOCYCLE
NOORDER
CACHE 20;



A seguir, vou inserir alguns registros na nova tabela. Pretento, em dado momento testar a constraint "chk_DESCRICAO".



-- Inserindo registros de Tipo de Contato em ZnTipo_Contato

INSERT INTO ZnTipo_Contato(
TIPO_CONTATOID
, DESCRICAO
, DT_CADASTRO
, UPDATEDATE
, USERUPDATE
) VALUES
(
SQ_ZnTipo_Contato.NEXTVAL
, 'Email'
, SYSDATE
, SYSDATE
, 1
);

INSERT INTO ZnTipo_Contato(
TIPO_CONTATOID
, DESCRICAO
, DT_CADASTRO
, UPDATEDATE
, USERUPDATE
) VALUES
(
SQ_ZnTipo_Contato.NEXTVAL
, 'Telefone Residencial'
, SYSDATE
, SYSDATE
, 1
);

INSERT INTO ZnTipo_Contato(
TIPO_CONTATOID
, DESCRICAO
, DT_CADASTRO
, UPDATEDATE
, USERUPDATE
) VALUES
(
SQ_ZnTipo_Contato.NEXTVAL
, 'Telefone Comercial'
, SYSDATE
, SYSDATE
, 1
);

INSERT INTO ZnTipo_Contato(
TIPO_CONTATOID
, DESCRICAO
, DT_CADASTRO
, UPDATEDATE
, USERUPDATE
) VALUES
(
SQ_ZnTipo_Contato.NEXTVAL
, 'Telefone Celuar'
, SYSDATE
, SYSDATE
, 1
);

INSERT INTO ZnTipo_Contato(
TIPO_CONTATOID
, DESCRICAO
, DT_CADASTRO
, UPDATEDATE
, USERUPDATE
) VALUES
(
SQ_ZnTipo_Contato.NEXTVAL
, 'Gtalk'
, SYSDATE
, SYSDATE
, 1
);

INSERT INTO ZnTipo_Contato(
TIPO_CONTATOID
, DESCRICAO
, DT_CADASTRO
, UPDATEDATE
, USERUPDATE
) VALUES
(
SQ_ZnTipo_Contato.NEXTVAL
, 'Skype'
, SYSDATE
, SYSDATE
, 1
);

INSERT INTO ZnTipo_Contato(
TIPO_CONTATOID
, DESCRICAO
, DT_CADASTRO
, UPDATEDATE
, USERUPDATE
) VALUES
(
SQ_ZnTipo_Contato.NEXTVAL
, 'Orkut Profile'
, SYSDATE
, SYSDATE
, 1
);

COMMIT;


Ok, agora vamos provocar a validação. Vou inserir um tipo de contato cujo tamanho será menor que 4 caracteres.

---- Testanto a constraint, inserindo um contato cujo o tamanho é menor que 4

INSERT INTO ZnTipo_Contato(
TIPO_CONTATOID
, DESCRICAO
, DT_CADASTRO
, UPDATEDATE
, USERUPDATE
) VALUES
(
SQ_ZnTipo_Contato.NEXTVAL
, 'Msn'
, SYSDATE
, SYSDATE
, 1
);




Verificando os registros inseridos


SELECT * FROM ZnTipo_Contato;



Como posso ver os comentários que colocamos nas colunas da tabela?

SELECT * FROM ALL_COL_COMMENTS WHERE table_name = 'ZNTIPO_CONTATO'



Artigo completo (View Full Post)

quarta-feira, 22 de outubro de 2008

Front End x Back End – UDFS no Servidor SGBD


UDF: “User Defined Function”, nos SGDBs, são funções criadas pelo desenvolvedor. Os principais fornecedores de tecnologia de banco de dados disponibilizam essa feature nos seus produtos.

O modelo Cliente/Servidor caracteriza-se por centralizar a programação referente a regra de negócio no servidor SGBD. Neste artigo, veremos como executar uma função no banco Oracle (PL/SQL) a partir da aplicação em Delphi.

No Back-And

Primeiro vamos criar a função no Oracle, a qual posteriormente evocaremos de uma interface (um aplicativo, front-end), um programa desenvolvido em Delphi.


create or replace function DeterminaMaior(ZNValor1 in NUMBER, ZNValor2 in NUMBER, ZNValor3 in NUMBER) return number is
Result number;
begin
IF (ZNValor1 > ZNValor2) AND (ZNValor1 > ZNValor3) THEN
RESULT := ZNValor1;
ELSE
IF (ZNValor2 > ZNValor1) AND (ZNValor2 > ZNValor3) THEN
RESULT := ZNValor2;
ELSE
RESULT := ZNValor3;
END IF;
END IF;
return(Result);
end DeterminaMaior;



Compile a função “DeterminaMaior” e pronto! Finalizamos aqui a parte de programação no servidor.

No Front-End

Inicie uma aplicação (no menu - File, New, Application), adicione no form1 os Seguintes componentes:

1- TADOConnection (Palheta ADO): configure a conexão com o banco na propriedade “ConnectionString”. Em seguida, Altere a propriedade “LoginPrompt” para “False”.

2- TADOStoredProc(Palheta ADO): Associe ele ao ADOConnection através da propriedade “Connection”. Em seguida, na propriedade “ProcedureName” digite o nome da função que acabamos de criar. Exemplo: “DeterminaMaior”. Pode ser conveniente digitar o nome do schema ponto o nome da função, tipo: “[eschemaName]. DeterminaMaior”.

Feto isso, click na propriedade parameters e veja alista re parâmetros que ele recupera automaticamente. Veja a ilustração abaixo:




Adicione também um TBitBtn (Palheta Additional). Na propriedade “Caption” defina: “Determinar Maior”.

Preciso adicionar três edits (TEdit – Palheta Standard) e um Memo (TMemo – Palheta Standard).




Codificando o evento OnClick do BitBtn1: Digite conforme exemplificado abaixo:


procedure TForm1.BitBtn1Click(Sender: TObject);
const
ZnConstMsg = 'www.estacaozn.blogspot.com - O Valor maior é %s';
Var
ZnMsg: String;
begin

with ADOStoredProc1 do
begin
Parameters[0].Value := 0;
Parameters[1].Value := GetValorEdit(Edit1);
Parameters[2].Value := GetValorEdit(Edit2);
Parameters[3].Value := GetValorEdit(Edit3);
ExecProc;
end;
ZnMsg := Format(ZnConstMsg,
[VarTOStr(ADOStoredProc1.Parameters[0].Value)]);
Memo1.Lines.Add(ZnMsg);
MessageDlg(ZnMsg, mtInformation, [mbOK], 0);
end;

Nas linhas 2 e 3, defini como constante o valor da mensagem a ser exibida como resultado do processamento da UDF que criamos.

Nas linhas 4 e 5, declarei uma variável para auxiliar no out put do processamento que estamos realizando. Quando recuperar o valor retornado pela UDF vamos usá-la para compor a mensagem para o usuário.

Na liha 6, a palavra reservada “begin” inidca o início do bloco de comando executados pelo procedimento “BitBtn1Click”.

Na linha 7, espaço em branco. Devemos deixar espaços em branco em alguns casos onde desejamos que, na leitura do nosso código, o entendimento fique mais fácil.

Da linha 9 a 12, passamos os valores para os parâmetros do “ADOStoredProc1”. Note que criei uma função “GetValorEdit” para poder recuperar o valor dos edits de forma segura. veremos o código dela mais adiante.

Na linha 13, chamamos o método ExecProc, de TADOStoredProc, o qual executa a UDF, “DeterminaMaior”, no banco.

Na linha 15, formatamos o valor da mensagem que deverá ser exibida composta do valor retornado do processamento no banco.

Na linha 17, adiciono ao Memo1 a mensagem.

Na linha 18, chamamos a função “MessageDlg”, API do Windows, para exibir o resultado do processamento para o usuário.


Codificando o Método “GetValorEdit”:

Na seção “private” vamos codificar a declaração método “GetValorEdit”. Segue trecho de código ilustrando o corpo do método:



function TForm1.GetValorEdit(Edt: TEdit): Double;
var
AuxNum: Double;
begin
try
AuxNum := StrToFloat(Edt.Text);
except
on EConvertError do
AuxNum := 0;
end;

Result := AuxNum;
end;


Agora execute a aplicação (pressione F9) e teste ...



A seguir, código completo da unit ...
unit Unit1;

interface

uses
Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
Dialogs, StdCtrls, Buttons, DB, ADODB;

type
TForm1 = class(TForm)
BitBtn1: TBitBtn;
ADOStoredProc1: TADOStoredProc;
ADOConnection1: TADOConnection;
Memo1: TMemo;
Edit1: TEdit;
Edit2: TEdit;
Edit3: TEdit;
procedure BitBtn1Click(Sender: TObject);
private
function GetValorEdit(Edt: TEdit): Double;
public
{ Public declarations }
end;

var
Form1: TForm1;

implementation

{$R *.dfm}

procedure TForm1.BitBtn1Click(Sender: TObject);
const
ZnConstMsg = 'www.estacaozn.blogspot.com - O Valor maior é %s';
Var
ZnMsg: String;
begin

with ADOStoredProc1 do
begin
Parameters[0].Value := 0;
Parameters[1].Value := GetValorEdit(Edit1);
Parameters[2].Value := GetValorEdit(Edit2);
Parameters[3].Value := GetValorEdit(Edit3);
ExecProc;
end;
ZnMsg := Format(ZnConstMsg,
[VarTOStr(ADOStoredProc1.Parameters[0].Value)]);
Memo1.Lines.Add(ZnMsg);
MessageDlg(ZnMsg, mtInformation, [mbOK], 0);
end;

function TForm1.GetValorEdit(Edt: TEdit): Double;
var
AuxNum: Double;
begin
try
AuxNum := StrToFloat(Edt.Text);
except
on EConvertError do
AuxNum := 0;
end;

Result := AuxNum;
end;

end.


Artigo completo (View Full Post)

Pl/SQL Sequence

A “Sequence” é um objeto de banco de dados cujo aprincipal e única função é permitir que vários usuários possam gerar valores inteiros isentos de duplicidade (não repetidos). Essa funcionalidade é usada quando precisamos gerar valores para uma coluna Unique Key (UK). Ou seja,uma coluna cujos valores são únicos. Uma coluna “Uk” pode ser definida como chave primária (PK).
Outros SGBDSR, além do Oracle, também trabalham com ess conceito.
Observe que, embora o nome seja “sequence” não há nela nehum comprometimento em gerar sequencias de valores. A sequence tem a responsabilidade apenas de gerar valores únicos.
Abaixo veja a sintaxe para criação de uma “Sequence”:

CREATE SEQUENCE [schema.]sequence
[INCREMENT BY integer]
[START WITH integer]
[MAXVALUE integer]
[MINVALUE integer]
[CYCLE | NOCYCLE]
[CACHE integer | NOCACHE]
[ORDER | NOORDER]


Sobre os Parâmetros

schema :
Nome do usuário dono da sequence.
Sequence: Nome da sequence.
INCREMENT BY: Define o intervalo entre os números gerados.
MINVALUE: Define o valor mínimo que a sequence pode assumir.
MAXVALUE: Valor máximo que a sequence pode assumir.
START WITH: O primeiro valor gerado pela sequence.
CYCLE: Define a sequence como cíclica. Ou seja, recomeçará, do valor mínuimo, a contagem quando atingir os extremos.
NOCYCLE: Define a sequence como não cíclica. Incrementando até atingir o limite estipulado.
CACHE: Otimiza o processo de geração de UK. Define quantos valores da sequence o ORACLE ira manter na memória para um acesso mais rápido.
NOCACHE: Não terá valores pré-alocados.
ORDER: Garante que o numero gerado pela sequence obedece a ordem de requisição.
NOORDER: Não garante que os números seqüenciais gerados obedecem à ordem de requisição.




Exemplo I
O comando seguinte cria uma sequence com incremento de 10 começando do 1:

CREATE SEQUENCE ZnVSqc
INCREMENT BY 10


Exemplo II
O comando a seguir cria uma sequence com incremento de 1 começando do 11:

CREATE SEQUENCE ZnVSqc_MusicUk
INCREMENT BY 1
START WITH 11;

Para usar a sequence e gerar um valor “uk” novo, ou verificar qual o valor do último gerado, podemos através de comandos SQL retornar valor desejado. Veja conforme ilustração abaixo:

-- retorna o valor coorente da sequence
SELECT ZnVSqc_MusicUk.currval FROM dual;
-- Gera um novo uk e retorna o valor gerado (Nextval).
SELECT ZnVSqc_MusicUk.nextval FROM dual;








Artigo completo (View Full Post)

segunda-feira, 20 de outubro de 2008

Declarar Variável - Pl/SQL - Oracle

Podemos usar programação em PL/SQL sem necessariamente criar uma procedure ou function para isso.


DECLARE
ZnNum1 NUMBER(3);

Para declara variáveis, atribuir valores e verificar o valor de saída, veja o trecho de código ilustrado abaixo:


DECLARE
ZnNum1 NUMBER(3);
ZnNum2 NUMBER(3);
ZnData DATE;

BEGIN
ZnNum1 := 3;
ZnNum2 := ZnNum1 + 10;
ZnData := SYSDATE;

DBMS_OUTPUT.PUT_LINE('ZnNum1 = ' || ZnNum1);
DBMS_OUTPUT.PUT_LINE('Calculando ZnNum2 ....');
DBMS_OUTPUT.PUT_LINE('ZnNum2 = ' || ZnNum2);

DBMS_OUTPUT.PUT_LINE('Fim do Cálculo');

DBMS_OUTPUT.PUT_LINE('Data = ' || ZnData);
END;

Artigo completo (View Full Post)

domingo, 5 de outubro de 2008

Para matar um sessão no Orcle

Dica enviada pelo meu brother Diogo Pizaneschi:


No SqlPlus ou qualquer outra ferramenta, faça um select em
"v$session" para procurar na a sessão que deseja matar. Em seguida, após identificar a sessão, pegar os valores das colunas "SID" e "SERIAL#", referentes a sua sessão. Finalmente, executar o comando:


alter system kill session 'SID,SERIAL#' immediate


Ou seja, substitua os valores da sessão identificada, referentes as colunas citadas acima pela sua respectiva posição (exemplificada no comando anterior). Portanto, supondo que os valores para as colunas "SID" e "SERIAL#" que desejamos matar sejam, respctivamente, "135" e "6487", logo o comando para matar esta sessão deverá ser assim:


alter system kill session '135,6487' immediate


Obrigado Diogão!

Artigo completo (View Full Post)

domingo, 4 de maio de 2008

“DBMS_SQL package” & “EXECUTE IMMEDIATE” - PL/SQL

“DBMS_SQL package” & “EXECUTE IMMEDIATE”

Aproveitando o gancho deixado pelo comentário do Malta(no post anterior), acho que cabe um exemplo mostrando a aplicação das pls da package “DBMS_SQL” na abordagem de SQL dinâmico no Oracle.

Usando a Package

CREATE OR REPALCE PROCEDURE insert_ZNtable (
ZN_ID NUMBER,
NameZN VARCHAR2) IS
ZNcursor_Manipulador INTEGER;
DynSQLZn VARCHAR2(280);
rows_processed BINARY_INTEGER;

BEGIN
DynSQLZn := 'INSERT INTO MyTableZN VALUES (:ZN_ID, :NameZN)';

-- Ao Abrir o cursor atribui valor para o "Handle" , o cursor ID.
ZNcursor_Manipulador := dbms_sql.open_cursor;

-- efetuando o parse
dbms_sql.parse(ZNcursor_Manipulador, DynSQLZn,
dbms_sql.native);

-- fazando o BIND_VARIABLE
dbms_sql.bind_variable
(ZNcursor_Manipulador, ':ZN_ID', ZN_ID);
dbms_sql.bind_variable
(ZNcursor_Manipulador, ':NameZN', NameZN);

-- Executando o cursor
rows_processed :=
dbms_sql.execute(ZNcursor_Manipulador);

-- Fechando o cursor
dbms_sql.close_cursor(ZNcursor_Manipulador);

END;


Usando SQL dinâmico nativo

CREATE PROCEDURE insert_ZNtableExecuteImm(
ZN_ID NUMBER,
NameZN VARCHAR2) IS
DynSQLZn VARCHAR2(280);

BEGIN
DynSQLZn := 'INSERT INTO MyTableZN VALUES (:ZN_ID, :NameZN)';
-- parse e execução no mesmo comando
EXECUTE IMMEDIATE DynSQLZn
USING ZN_ID, NameZN;

END;

Artigo completo (View Full Post)

SQL dinâmico no Oracle

Existe alguma forma de criar dinamicamente um comando SQL, tanto DML quanto DDL, no Oracle?

Sim, é possível. A partir da versão 8i podemos usar o "EXECUTE IMMEDIATE" statement para executar um comando SQL montado em tempo de execução. Essa funcionalidade foi introduzida para substituir o “DBMS_SQL package”. Veja alguns exemplos abaixo:

-- Executando comando DDL “Create”
EXECUTE IMMEDIATE 'CREATE TABLE MyZNTable (ZN_ID NUMBER, ZNDescricao Varchar2(30))';

--- executando comando DML “INSERT”
EXECUTE IMMEDIATE 'insert into MyZNTable values (1, ''ZNteste'') ’;



Esse comando é um recurso nativo do Oracle para abordagem de SQL dinâmico, apropriado para tuning pois reduz overhead, otimiza a performance do comando SQL. Considerando que, Comandos SQL criados e executados dinamicamente apresentam overhead em performance. Utilização do comando "EXECUTE IMMEDIATE" reduz este overhead e proporciona maior performance.
Através dessa técnica podemos também usar placeholders para bind.
O que é placeholder? R - Um placeholder é um identificador não declarado que vai servir como parâmetro, cujo o valor será atribuído dinamicamente. O compilador SQL vai entender o identificador porque ele estará marcado com o prefixo, o sinal de “:” (dois pontos).

DECLARE
V_NUM_CONTRATO varchar2(20) := 'ZN1000';
V_NUM_GUIA varchar2(10) := '3000ZN';
ZnSQL_TMP VARCHAR2 (100);
BEGIN
-- Busca ASSOCIADO, DATA DE CADASTRO, e ORIGEM
ZnSQL_TMP := ' SELECT
DATA_CADASTRO, COD_ASSOCIADO, ASSOCIADO_ORIGEM
FROM
ASSOCIADOS
WHERE
NUM_CONTRATO = :NUM_CONTRATO AND
NUM_GUIA = :NUM_GUIA AND
ROWNUM = 1';

EXECUTE IMMEDIATE ZnSQL_TMP
USING V_NUM_CONTRATO
, V_NUM_GUIA;

END;



Acredito que isso pode facilitar muito o trabalho de desenvolvedor. Portanto, vejamos maiores detalhes sobre a sintaxe desse comando:

EXECUTE IMMEDIATE ZNSQL_string
[INTO {variável[, variável]...
| record}]
[USING [IN | OUT | IN OUT] bind_argument
[, [IN | OUT | IN OUT] bind_argument]...];

Analisando a sintaxe:

ZNSQL_string : Trata-se de uma string com comando SQL construído dinamicamente, conforme exemplificamos anteriormente. Essa string pode conter qualquer comando SQL sem o terminador. Comandos DML “SELECT” não devem retornar mais de uma linha. A string também pode conter placeholders (exemplo: :ZN_ASSOCIADO) e bind_arguments. Um detalhe importante, caso o camando SQL na string for uma chamada a uma procedure ou function (ou seja, uma PL/SQL) é necessário o emprego de “begin” e “end”.

INTO (palavra reservada): Indica quais variáveis (uma ou mais) cujos valores selecionados, em uma consulta, serão atribuídos. O record é um TYPE ou %ROWTYPE definido pelo usuário. Ele pode receber uma linha inteira retornada por uma consulta. Atenção, esta cláusula é opcional. É utilizada somente quando a String for uma consulta que retorne somente uma linha. Além disso, tanto a quantidade de variáveis empregadas, quanto a ordem em que elas estarão dispostas no comando, deve respeitar a quantidade e a ordem das colunas retornadas na consulta. Do mesmo modo, o tipo da variável ou registro (record) deve ser compatível com o valor que lhe será atribuído.

USING (palavra reservada): seção onde declaramos os valores atribuídos a cada argumento na bind variables da string. Não é permitido fazer bind para repassar nomes de objetos de um esquema, como nomes de tabelas ou de colunas. Também não é permitido valor booleano ou NULL. Suponha que o código exemplificado abaixo seja o trecho de uma procedure ou function:


SQL_TMP VARCHAR2 (1000);
BEGIN

SQL_TMP := ' SELECT
DATA_REALIZACAO, COD_ASSOCIADO, ORIGEM
FROM
ASSOCIADOS
WHERE
MES_REF = :MES_REF AND
COD_CONTADOR = :COD_CONTADOR AND
NUM_CONTRATO = :NUM_CONTRATO AND
NUM_GUIA = :NUM_GUIA AND
ROWNUM = 1';
BEGIN
EXECUTE IMMEDIATE SQL_TMP
INTO V_DATA_REALIZACAO, V_COD_ASSOCIADO, V_ORIGEM
USING V_MES_REF
, V_COD_CONTADOR
, V_NUM_CONTRATO
, V_NUM_GUIA;
EXCEPTION
WHEN no_data_found THEN
V_DATa_ZN := NULL;
V_NUM_ASSOCIADO_ZN := NULL;
END;

Preciso retornar várias linhas de uma consulta usando SQL dinâmico. Isso é possível?

Como mencionei anteriormente, consultas que retornem mais de uma linha não são suportadas como valor de retorno. Entretanto, existem alternativas: Ou utilizar uma tabela temporária para armazenar os registros, ou ref cursores.

Adiante, pretendo falar mais sobre o emprego do “EXECUTE IMMEDIATE”.

Artigo completo (View Full Post)

terça-feira, 22 de abril de 2008

TYPE CURSOR - Retornar um Resultset numa Procedure - Oracle & DBExpress

Nos artigos anteriores vimos como retornar um resultset num parâmetro de saída de uma procedure no Oracle (ADOStoredProc, ADODataset). Seguiremos dando continuidade ao assunto abordando o problema trocando a tecnologia de acesso a dados. Usaremos DBExpress para isso e documentaremos as adversidades que encontradas.

Construindo o Exemplo com DBExpress para testar a procedure com mais de um parâmetro de entrada:

Primeiro vamos criar uma outra procedure na package “MY_Pkg”:

procedure CalculaMediaStr(
Nota1 in VARCHAR2,
Nota2 in VARCHAR2,
Nota3 in VARCHAR2,
MediavalAprovacao in VARCHAR2,
ZnCursor OUT ZNCursorType) IS
BEGIN
/* Calcula a média de um aluno */

Media := (Nota1 + Nota2 + Nota3)/ 3;
IF (Media >= TO_NUMBER(MediavalAprovacao)) THEN
ResultadoAprovacao := 'Aprovado';
ELSE
ResultadoAprovacao := 'Reprovado';
END IF;

/* retornando no CURSOR o resultado do aluno */
OPEN ZnCursor FOR
SELECT 'Aluno Estação ZN: ' AS Nome,
ResultadoAprovacao AS ResultadoZN,
TO_Char(Media) AS ValorMedia
FROM dual;

end CalculaMediaStr;


Adicione mais uma página no PagControl1. Além disso adicione um SQLConnection, um SQLStoredProc, um DataSetProvider, um ClientDataSet, um DataSource. Na nova TabSheet, adicione um Panel, quatro Edits, um BitBtn (BtnMediaStr), um DBGrid. Conecte todos os componentes dataware, configure o SQLStoredProc conforme ilustração abaixo:



Veja abaixo a listagem de parâmetros do SQLStoredProc:



Uma vantagem da DBExpress é que você não precisa deletar o parâmetro cursor. Conect os componentes da DBExpress (SQLConnection e SQLStoredProc), com os componentes Midas(DataSetProvider e ClientDataset):

Componentes Dataware - * indica a propriedade para associação entres os componentes







No ClientDataSet, com o botão direito do mouse selecione “Fetch Params”, em seguida adicione os campos persistentes no “Fields Editor”.



Componentes MIDAS:









O Componente SQLStoredProc perde os parâmetros quando efetua uma execução da procedure “CALCULAMEDIASTR”. Por isso somos obrigados a montar toda lista de parâmetros manualmente. Antes de codificar o evento OnClick do “BtnMediaStr”, onde faremos de fato a chamada a execução da “proc” vamos implementar um método que montará a lista de parâmetros do “SQLStoredProc”. Já que criamos uma proceedure nova para este exemplo, visto que teríamos que trabalhar somente com tipo não numéricos retornados ou enviados ao banco de dados, aproveitei e mudei um pouco a lógica da procedure “CALCULAMEDIASTR” em relação a calcula média anterior. Neste nova versão, o valor referência para aprovação é um parâmetro de entrada: “MediavalAprovacao”.


procedure TForm1.BuildParams;
var
i: Integer;

procedure AddParameter(Dataset: TClientDataSet; DataType: TFieldType;
Name: String; ParamType: TParamType);
var
MyParameter: TParam;
begin
{Somos obrigados a criar dinamicamente os parâmetros}
MyParameter := TParam.Create(DataSet.Params);
MyParameter.DataType := DataType;
MyParameter.Name := Name;
MyParameter.ParamType := ParamType;
DataSet.Params.AddParam(MyParameter);
end;

begin

with ClientDataSet do
begin
Close;

Params.Clear;
AddParameter(ClientDataSet, ftInteger, 'MEDIAVALAPROVACAO', ptInput);
AddParameter(ClientDataSet, ftInteger, 'NOTA3', ptInput);
AddParameter(ClientDataSet, ftInteger, 'NOTA2', ptInput);
AddParameter(ClientDataSet, ftInteger, 'NOTA1', ptInput);
AddParameter(ClientDataSet, ftCursor, 'ZnCursor', ptOutput);
end;
end;


Codificando o evento OnClick do “BtnMediaStr”

BuildParams;
with ClientDataSet do
begin
Close;
Params[0].AsString := EdtMediaAprovacao.Text;
Params[1].AsString := EdtNota1.Text;
Params[2].AsString := EdtNota2.Text;
Params[3].AsString := EdtNota3.Text;
Open;
end;


Testando a plicação:



Tivemos que converter, no comando SQL a variável “Media”, para tipo caractere. Segue código da package atual:


create or replace package MY_Pkg is

-- Author : GMottazn
-- Created : 12/03/2008 09:58:08
-- Purpose :

-- Public type declarations
TYPE ZNCursorType IS REF CURSOR;

-- Public variable declarations
-- ;

-- Public function and procedure declarations
procedure CalculaMedia(
Nota1 in INTEGER,
Nota2 in INTEGER,
Nota3 in INTEGER,
Nota4 in INTEGER,
ZnCursor OUT ZNCursorType);

PROCEDURE RetCircunferencia(
ZnCursor1 OUT ZNCursorType,
Raio IN NUMBER DEFAULT 0);


procedure CalculaMediaStr(
Nota1 in VARCHAR2,
Nota2 in VARCHAR2,
Nota3 in VARCHAR2,
MediavalAprovacao in VARCHAR2,
ZnCursor OUT ZNCursorType);

end MY_Pkg;
/
create or replace package body MY_Pkg is

-- Public constant declarations
ValorAprovacao constant NUMBER := 6;
PI CONSTANT NUMBER := 3.1416; -- Constante usada na procedure "RetCricunferencia"

-- Private variable declarations
ResultadoAprovacao VARCHAR2(20);
Media NUMBER;
-- Function and procedure implementations
procedure CalculaMedia(
Nota1 in INTEGER,
Nota2 in INTEGER,
Nota3 in INTEGER,
Nota4 in INTEGER,
ZnCursor OUT ZNCursorType) IS
BEGIN
/* Calcula a média de um aluno */

Media := (Nota1 + Nota2 + Nota3 + Nota4)/ 4;
IF (Media >= ValorAprovacao) THEN
ResultadoAprovacao := 'Aprovado';
ELSE
ResultadoAprovacao := 'Reprovado';
END IF;

/* retornando no CURSOR o resultado do aluno */
OPEN ZnCursor FOR
SELECT 'Aluno Estação ZN: ' AS Nome,
ResultadoAprovacao AS ResultadoZN,
Media AS ValorMedia
FROM dual;

end CalculaMedia;


PROCEDURE RetCircunferencia(
ZnCursor1 OUT ZNCursorType,
Raio IN NUMBER DEFAULT 0) IS
vResultado NUMBER;
BEGIN
/* Calcula a retificação da circunferência */

vResultado := 2 * (Raio * PI);

OPEN ZNCursor1 FOR
SELECT
'Retificação da Circuferência' AS Descricao,
VResultado AS Resultado
FROM
Dual;
END RetCircunferencia;


procedure CalculaMediaStr(
Nota1 in VARCHAR2,
Nota2 in VARCHAR2,
Nota3 in VARCHAR2,
MediavalAprovacao in VARCHAR2,
ZnCursor OUT ZNCursorType) IS
BEGIN
/* Calcula a média de um aluno */

Media := (Nota1 + Nota2 + Nota3)/ 3;
IF (Media >= TO_NUMBER(MediavalAprovacao)) THEN
ResultadoAprovacao := 'Aprovado';
ELSE
ResultadoAprovacao := 'Reprovado';
END IF;

/* retornando no CURSOR o resultado do aluno */
OPEN ZnCursor FOR
SELECT 'Aluno Estação ZN: ' AS Nome,
ResultadoAprovacao AS ResultadoZN,
TO_Char(Media) AS ValorMedia
FROM dual;

end CalculaMediaStr;

end MY_Pkg;
/


Artigo completo (View Full Post)

domingo, 16 de março de 2008

TYPE CURSOR - Retornar um Resultset numa Procedure - Oracle

Retornar um resultset como parâmetro de saída numa procedure em PL/SQL não é algo tão intuitivo como no Interbase ou no MS SQL Server. Portanto, vamos desenvolver um exemplo onde construiremos um procedure em PL/SQL para retornar um resultset. Em seguida vamos implementar um programa em Delphi que vai executar esse procedimento e recuperaremos na interface o resultado do Select.

Para criar a pakcage:
No PL/SQL Developer, no Object Browser, ou somente Browser, selecione a pasta (folder) “Packages” com o botão direito do mouse click em new. Conforme ilustrado abaixo:



Defina o Nome da package:




Note que será criado uma área de declaração como interface da package e outra área para o “body” da package.



Eu vou definir um tipo cursor o qual será usado para um parâmetro de saída da procedure “CalulaMedia”. Nesse parâmetro retornarei o resultset que desejo exibir na aplicação.

Interface da package:


Body da package:



Construindo a interface em Delphi:

Ok, recapitulando: Vamos construir um aplicativo o qual acessará o banco de dados Oracle onde criamos a package “My_Pkg”. A middleware de acesso a dados será OLE_DB, cujos objetos de acesso a dados estão disponibilizados no Delphi na palheta ADO (Activex Data Object). Estou documentando esse exemplo porque não foi nada fácil, nem intuitivo, recuperar um parâmetro do tipo cursor no Delphi. Na nossa primeira tentativa nos deparamos com muitos problemas o maior deles foi justamente com o parâmetro de retorno do tipo cursor no componente “TADOStoredProc”. Constatamos que nesta tecnologia o tipo “ftCursor” quando atribuído a propriedade “DataType” do parâmetro disparava uma exceção informado que os argumentos estavam incorretos.



Constatamos que para fazer funcionar tínhamos que deletar esse parâmetro da lista de parâmetros, deixando somente o parâmetro de input. Ainda referente ao cenário da primeira tentativa, somente obtivemos sucesso com “TADOStoredProc” enquanto a procedure no banco possuía dois parâmetros, um deles era o tipo cursor, parâmetro de saída, o outro era um parâmetro para filtro na cláusula “where” do comando SQL. Vejamos um exemplo:
Duas condições devem ser atendidas – O tipo cursor deve ser o primeiro parâmetro pois o último, de entrada, deve obrigatoriamente ser definido como “default”. Criei para este exemplo uma procedure que efetua um cálculo simples:


PROCEDURE RetCircunferencia(
ZnCursor OUT ZNCursorType,
Raio IN NUMBER DEFAULT 0) IS
vResultado NUMBER;
BEGIN
/* Calcula a retificação da circunferência */

vResultado := 2 * (Raio * PI);

OPEN ZNCursor FOR
SELECT
'Retificação da Circuferência' AS Descricao,
VResultado AS Resultado
FROM
Dual;
END RetCricunferencia;


A constante “PI” declarei na área de declaração de constantes da package “MY_PKG”. Veja o Código da package agora:

create or replace package body MY_Pkg is

-- Public constant declarations
ValorAprovacao constant NUMBER := 6;
PI CONSTANT NUMBER := 3.1416; -- Constante usada na procedure "RetCricunferencia"

-- Private variable declarations
ResultadoAprovacao VARCHAR2(20);
Media NUMBER;
-- Function and procedure implementations
procedure CalculaMedia(
Nota1 in INTEGER,
Nota2 in INTEGER,
Nota3 in INTEGER,
Nota4 in INTEGER,
ZnCursor OUT ZNCursorType) IS
BEGIN
/* Calcula a média de um aluno */

Media := (Nota1 + Nota2 + Nota3 + Nota4)/ 4;
IF (Media >= ValorAprovacao) THEN
ResultadoAprovacao := 'Aprovado';
ELSE
ResultadoAprovacao := 'Reprovado';
END IF;

/* retornando no CURSOR o resultado do aluno */
open ZnCursor FOR
SELECT 'Aluno Estação ZN: ' AS Nome,
ResultadoAprovacao AS ResultadoZN/*,
Media AS ValorMedia */
FROM dual;

end CalculaMedia;


PROCEDURE RetCircunferencia(
ZnCursor OUT ZNCursorType,
Raio IN NUMBER DEFAULT 0) IS
vResultado NUMBER;
BEGIN
/* Calcula a retificação da circunferência */

vResultado := 2 * (Raio * PI);

OPEN ZnCursor FOR
SELECT 'Aluno Estação ZN: ' AS Nome,
ResultadoAprovacao AS ResultadoZN,
Media AS ValorMedia
FROM dual;

END RetCircunferencia;

end MY_Pkg;


Inicie uma nova aplicação, no form1 adicione um “ADOConnection”, um TADOStoredProc, um TDataSource, um TDBGRid, um TEdit, um TLabel. Conecte o ADOConnection com o banco aonde vc criou a procedure. No ADOConnection, o menu popup, com o botão direito do mouse selecione build connection. Veja a ilustrção abaixo:



Não esqueça de alterar a propriedade “LoginPrompt” do ADOConnetion para “False”.

Em seguida conecte a ADOStoredProc no ADOConnection (pela propriedade “Connection” do ADOStoredProc). Conecte o DataSource no ADOStoredProc (propriedade DataSet do DataSource), conecte o DBGrid no DataSource (propriedade “DataSource” do DBGrid). Na propriedade “ProcedureName” do ADOStoredProc digite “[eschema].MY_PKG.RetCircunferencia”. Veja na propriedade parameters do ADOStoredProc a lista de parâmetros recuperados da procedure.



Alterei a propriedade Name do Tedit para “EdtRaio”. Adicione um TBitBtn, nomeie de BtnExcRaio,No Evento OnClick digite conforme exemplificado abaixo:

procedure TForm1.BtnExcRaioClick(Sender: TObject);
begin
with ADOStoredProc1 do
begin
Parameters[0].Value := StrtoFloat(EdtRaio.Text);
Open;
end;
end;


Ok, tudo parece estar certo, tudo pronto para testarmos, correto? Posso adiantar que se você executar agora vai levar uma exceção bacana na lata.



Pra funcionar ainda temos que, sem razão aparente, deletar o parâmetro “ZNCURSOR” da lista de parâmetros da propriedade “Parameters” do ADOStoredProc.



Para demonstrar o quanto é complicado alcançar o objetivo proposto no início do nosso exmplo (executar um procedure no Oracle e recuperar o valor de um parâmetro tipo cursor), veja que ainda faltam algumas configurações a fazer no ADOStoredProc. Antes garanta que o ADOConnection esteja desconectado (propriedade “Connected = False”):
1° - Altere a propriedade “EnableBCD” do ADOStoredProc para “False”.
2° - Altere a propriedade “DataType” do parâmtro “RAIO” de “ftBCD” para “ftFloat”. Você pode fazer isso na propriedade “Parameters” do ADOStoredProc.

Agora adicione os campos persitentes, no fields Editor do ADOStoredProc. Em seguida execute o programa e teste:





Com um pouco de persistência obtivemos sucesso! Contudo, os problemas não param por aqui. Por exemplo, se você tiver mais de um parâmetro de entra o bicho vai pegar e o bagulho não vai funfar. Justamente, esse é o caso da procedure “CalculaMedia”, de jeito nenhum conseguimos fazer funcionar com o ADOStoredProc. Tentando de várias outras formas conseguimos sucesso trocando o dataset para TADODataSet, só assim funcionou e mesmo assim tivemos que fazer as mesmas alterações quanto ao tipo do parâmetro e deleção o tipo cursor. A única exceção foi a não obrigatoriedade do parâmetro de saída, tipo cursor, na procedure “CalculaMedia” ser o primeiro na declaração.

No próximo artigo daremos continuidade ....

segue o código da package:


create or replace package MY_Pkg is

-- Author : GMottazn
-- Created : 12/03/2008 09:58:08
-- Purpose :

-- Public type declarations
TYPE ZNCursorType IS REF CURSOR;

-- Public variable declarations
-- ;

-- Public function and procedure declarations
procedure CalculaMedia(
Nota1 in out NUMBER,
Nota2 in NUMBER,
Nota3 in NUMBER,
Nota4 in NUMBER,
ValorAprovacao IN NUMBER,
Media out NUMBER,
ZnCursor IN OUT ZNCursorType);

end MY_Pkg;
/
create or replace package body MY_Pkg IS

-- Public constant declarations
ValorAprovacao constant NUMBER := 6;

-- Private variable declarations
ResultadoAprovacao VARCHAR2(20);

-- Function and procedure implementations
procedure CalculaMedia(
Nota1 in out NUMBER,
Nota2 in NUMBER,
Nota3 in NUMBER,
Nota4 in NUMBER,
ValorAprovacao IN NUMBER,
Media out NUMBER,
ZnCursor IN OUT ZNCursorType) is
begin
Media := (Nota1 + Nota2 + Nota3 + Nota4)/ 4;
IF (Media >= ValorAprovacao) THEN
ResultadoAprovacao := 'Aprovado';
ELSE
ResultadoAprovacao := 'Reprovado';
END IF;
open ZnCursor FOR
SELECT 'Aluno Estação ZN: ' AS Nome,
ResultadoAprovacao AS ResultadoZN
FROM dual;

end CalculaMedia;


end MY_Pkg;
/



Artigo completo (View Full Post)

sábado, 15 de março de 2008

Algoritmo III – Um passo além da teoria

Prosseguindo com a série sobre Introdução a Programação, especificamente ao tema Algoritmos, vamos dar mais um passo no sentido de aprofundarmos mais nesse assunto.


Usando estrutura de decisão:

Segue um Algoritmo que lê o nome e as 4 notas bimestrais de um aluno. Em seguida o Algoritmo calcula e escreve a média obtida pelo aluno escrevendo também se o aluno foi aprovado ou reprovado.


Programa MEDIA_FINAL;
Constantes
MediaAprovacaoZN = 6;
Variaveis
NOTA1Zn, NOTA2Zn, NOTA3Zn, NOTA4Zn, MEDIAZn: REAL;
NOMEZn : CARACTERE [35]
INICIO
LER (NOMEZn);
LER (NOTA1Zn, NOTA2Zn, NOTA3Zn, NOTA4Zn);
MEDIAZn := (NOTA1Zn + NOTA2Zn + NOTA3Zn + NOTA4Zn) / 4;
SE MEDIAZn (>= 6) ENTÃO
ESCREVER (‘APROVADO’)
SENÃO
ESCREVER (‘REPROVADO’);

ESCREVER (NOMEZn, MEDIAZn)
FIM.


Desvio condicional aninhado:

Usados para tomadas de decisões para mais de 2 opções.

Exemplo:

SE <<CONDIÇÃO>> ENTÃO
inicio
<<COMANDO 1>>;
<<COMANDO 2>>;
.
.
<<COMANDO N>>;
fim
SENÃO SE <<CONDIÇÃO>> ENTÃO
inicio
<<COMANDO 1>>;
<<COMANDO 2>>;
.
.
<<COMANDO N>>;
fim
SENÃO
início
<<COMANDO 1>>;
<<COMANDO 2>>;
.
.
<<COMANDO N>>;

fim;


Implementação em PL/SQL:


create or replace procedure CalculaMedia(
Nota1 in out NUMBER,
Nota2 in NUMBER,
Nota3 in NUMBER,
Nota4 in NUMBER,
ValorAprovacao IN NUMBER,
Media out NUMBER,
ResultadoAprovacao OUT Varchar2) is
begin
Media := (Nota1 + Nota2 + Nota3 + Nota4)/ 4;
IF (Media >= ValorAprovacao) THEN
ResultadoAprovacao := 'Aprovado';
ELSE
ResultadoAprovacao := 'Reprovado';
END IF;
end CalculaMedia;


PL/SQL é a linguagem de programação do Oracle. Para debuggar no Developer, no object browser, selecione a procedure criada, “CalculaMedia”, com o botão direito do mouse sobre ela selecione “Add debugger information”, em seguida, ainda no mesmo menu popup, selecione “Test”. Passe valores para os argumentos (parâmetros) e pressione F9, para ir para o próximo comando pressione “ctrl + n”.




Implementação em Javascript:


<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
<html>
<head>
<title> Estação ZN - Algoritmos III </title>
<meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1">
<script type= "text/javascript">
var ZnNota1 = 0;
var ZnNota2 = 0;
var ZnNota3 = 0;
var ZnNota4 = 0;
var ZnMediaAprovacao = 6;
var Aprovacao, MediaCalculadaZn;

// Input de Dados
// Lendo o valor da primeira nota
ZnNota1 = window.prompt( "Digite a primeira Nota:", "0" );
// Lendo o valor da segunda nota
ZnNota2 = window.prompt( "Digite a Segunda Nota:", "0" );
//Lendo o valor da 3° nota
ZnNota3 = window.prompt( "Digite a Terceira Nota:", "0" );
//Lendo o valor da 4° nota
ZnNota4 = window.prompt( "Digite a Quarta Nota:", "0" );

<!-- Processamento - Calculando a Média | convertendo os valore para numérico -->
MediaCalculadaZn = (parseInt(ZnNota1) + parseInt(ZnNota2) +
parseInt(ZnNota3) + parseInt(ZnNota4)) / 4;
//MediaCalculadaZn = (ZnNota1 + ZnNota2 + ZnNota3 + ZnNota4) / 4;

alert("Calculo media = " + MediaCalculadaZn);

<!-- Verificando Aprovação do Aluno -->
if (MediaCalculadaZn >= ZnMediaAprovacao) {
Aprovacao = "Parabéns, aluno Aprovado!!!!!"}
else { Aprovacao = "Lamento, aluno Reprovado."};

alert(" Verificando Aprovação do Aluno = " + Aprovacao);
// Saída - Exibindo o resultado formatado numa tabela
document.writeln( "<h1> <center>Estação ZN - Resultado </center></h1>" );
document.writeln("<table border=2>" );
document.writeln("<table border =1 width =100%>" );
document.writeln( "<tr><td> Nota na 1° prova = " + ZnNota1 + "</td></tr>" );
document.writeln( "<td> Nota na 2° prova = " + ZnNota2 + "</td></tr>" );
document.writeln( "<td> Nota na 3° prova = " + ZnNota3 + "</td></tr>" );
document.writeln( "<td> Nota na 4° prova = " + ZnNota4 + "</td></tr>" );
document.writeln( "<tr><td> Média = " + MediaCalculadaZn + "</td></tr>" );
document.writeln( "<tr bgcolor=#000066> <td> <font face=Courier New, Courier, mono color=#FFFFFF> Resultado : " + Aprovacao + "</font> </td></tr>" );
document.writeln( "</table>" );

</script>
</head>
<body>

</body>
</html>




Inicie uma nova aplicação. Na seção implementation da página de código digite conforme ilustrado abaixo:


unit Unit1;

interface

uses
Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
Dialogs;

type
TForm1 = class(TForm)
procedure FormPaint(Sender: TObject);
private
{ Private declarations }
public
{ Public declarations }
end;

var
Form1: TForm;
ZnNota1, ZnNota2, ZnNota3, ZnNota4, ZnMedia: Double;
ResultadoAprovacaoZN: string;

implementation

{$R *.dfm}
const
ValorAprovacao = 6;

procedure CalculaMediaZN;
const
Aprovado = 'Parabéns!!! Aluno Aprovado.';
Reprovado = 'Lamento!!! Aluno Reprovado. Vai estudar e RAFM.';
begin
(* Entrada de dados - Input*)

{Preciso converter o valor retornado pela função "InputBox" de string para float}
ZnNota1 := StrToFloat(InputBox('Estação Zn | Algoritmo III',
'Digite a 1° Nota do Aluno:', '0'));
ZnNota2 := StrToFloat(InputBox('Estação Zn | Algoritmo III',
'Digite a 2° Nota do Aluno:', '0'));
ZnNota3 := StrToFloat(InputBox('Estação Zn | Algoritmo III',
'Digite a 3° Nota do Aluno:', '0'));
ZnNota4 := StrToFloat(InputBox('Estação Zn | Algoritmo III',
'Digite a 4° Nota do Aluno:', '0'));
ZnMedia := (ZnNota1 + ZnNota2 + ZnNota3 + ZnNota4) / 4;

(* Processamento *)
if (ZnMedia >= unit1.ValorAprovacao) then
ResultadoAprovacaoZN := Aprovado
else
ResultadoAprovacaoZN := Reprovado;
end;

procedure TForm1.FormPaint(Sender: TObject);
begin
(* Sída de dados - Outpu*)
Form1.Canvas.TextOut(12, 12, Format('1° Nota: %f', [ZnNota1]));
Form1.Canvas.TextOut(12, 32, Format('2° Nota: %f', [ZnNota2]));
Form1.Canvas.TextOut(12, 52, Format('3° Nota: %f', [ZnNota3]));
Form1.Canvas.TextOut(12, 72, Format('4° Nota: %f', [ZnNota4]));
Form1.Canvas.TextOut(12, 92, Format('Média: %f', [ZnMedia]));

Form1.Canvas.TextOut(12, 102, '*******************************');

Form1.Canvas.TextOut(12, 132, Format('Resultado: %s', [ResultadoAprovacaoZN]));
end;

initialization
CalculaMediaZN;
end.

No exemplo acima, note que implementei de uma forma completamente diferente de outros programas que construímos no Estação Zn. Meu principal objetivo, além de mostrar como o algoritmo pode ser implementado, foi fazer com que uma pessoa iniciante no Delphi possa perceber alguns detalhes difíceis de explicar e de fazer entender. Vamos agora chamar a tenção para a relação entre o que é declarado na unit, de uma forma independente, e a unit como um todo. Por exemplo, existe uma tendência das pessoas interpretarem que a unit inteira é uma área de definição exclusiva ao form1. Isso não é verdade, o form é somente mais uma declaração dentre outras declarações possíveis numa unit. Observe, o form1 não conhece a procedure “CalculaMediaZN”. Tente fazer um teste, no evento “FormPaint” experimente chamar a execução dela. Veja o exemplo abaixo, a segunda linha da procedure “FormPaint”,você acha que vai compilar?




procedure CalculaMediaZN;
const
Aprovado = 'Parabéns!!! Aluno Aprovado.';
Reprovado = 'Lamento!!! Aluno Reprovado. Vai estudar e RAFM.';
begin
(* Entrada de dados - Input*)

{Preciso converter o valor retornado pela função "InputBox" de string para float}
ZnNota1 := StrToFloat(InputBox('Estação Zn | Algoritmo III',
'Digite a 1° Nota do Aluno:', '0'));
ZnNota2 := StrToFloat(InputBox('Estação Zn | Algoritmo III',
'Digite a 2° Nota do Aluno:', '0'));
ZnNota3 := StrToFloat(InputBox('Estação Zn | Algoritmo III',
'Digite a 3° Nota do Aluno:', '0'));
ZnNota4 := StrToFloat(InputBox('Estação Zn | Algoritmo III',
'Digite a 4° Nota do Aluno:', '0'));
ZnMedia := (ZnNota1 + ZnNota2 + ZnNota3 + ZnNota4) / 4;

(* Processamento *)
if (ZnMedia >= unit1.ValorAprovacao) then
ResultadoAprovacaoZN := Aprovado
else
ResultadoAprovacaoZN := Reprovado;
end;

procedure TForm1.FormPaint(Sender: TObject);
begin
CalculaMediaZN;
(* Sída de dados - Outpu*)
Form1.Canvas.TextOut(12, 12, Format('1° Nota: %f', [ZnNota1]));
Form1.Canvas.TextOut(12, 32, Format('2° Nota: %f', [ZnNota2]));
Form1.Canvas.TextOut(12, 52, Format('3° Nota: %f', [ZnNota3]));
Form1.Canvas.TextOut(12, 72, Format('4° Nota: %f', [ZnNota4]));
Form1.Canvas.TextOut(12, 92, Format('Média: %f', [ZnMedia]));

Form1.Canvas.TextOut(12, 102, '*******************************');

Form1.Canvas.TextOut(12, 132, Format('Resultado: %s', [ResultadoAprovacaoZN]));
end;


Você esta correto se respondeu sim. Mas, isso não prova que o que acabei de afirmar está errado. Só está compilando porque a “CalculaMediaZN” está declarada primeiro que o evento “FormPaint” na unit. Logo, basta você inverter a ordem dos procedimentos para perceber que o form1 não conhece a procedure que está declarada separadamente na unit. Tente compilar novamente para receber a mensagem “Undeclared identifier: ‘CalculaMediaZN’”. Exemplo:


procedure TForm1.FormPaint(Sender: TObject);
begin
CalculaMediaZN;
(* Sída de dados - Outpu*)
Form1.Canvas.TextOut(12, 12, Format('1° Nota: %f', [ZnNota1]));
Form1.Canvas.TextOut(12, 32, Format('2° Nota: %f', [ZnNota2]));
Form1.Canvas.TextOut(12, 52, Format('3° Nota: %f', [ZnNota3]));
Form1.Canvas.TextOut(12, 72, Format('4° Nota: %f', [ZnNota4]));
Form1.Canvas.TextOut(12, 92, Format('Média: %f', [ZnMedia]));

Form1.Canvas.TextOut(12, 102, '*******************************');

Form1.Canvas.TextOut(12, 132, Format('Resultado: %s', [ResultadoAprovacaoZN]));
end;

procedure CalculaMediaZN;
const
Aprovado = 'Parabéns!!! Aluno Aprovado.';
Reprovado = 'Lamento!!! Aluno Reprovado. Vai estudar e RAFM.';
begin
(* Entrada de dados - Input*)

{Preciso converter o valor retornado pela função "InputBox" de string para float}
ZnNota1 := StrToFloat(InputBox('Estação Zn | Algoritmo III',
'Digite a 1° Nota do Aluno:', '0'));
ZnNota2 := StrToFloat(InputBox('Estação Zn | Algoritmo III',
.
.
.



Mesmo que você referencie a “unit1” ao chamar a procedure que calcula a média não adiantará, visto que o ela não está declarada na seção “interface”.




procedure TForm1.FormPaint(Sender: TObject);
begin
unit1.CalculaMediaZN;// Ainda assim não vai compilar
.
.


Estou forçando uma barra somente para ilustrar o que estou tentando explicar, mas não faz o menor sentido chamar a procedure “CalculaMediaZN” novamente. Finalizando argumentação, para a provar de uma vez por todas, declare na seção “interface”, em qualquer lugar antes da palavra reservada “implementation”, desde que não seja dentro do escopo da seção “type” onde a classe TForm1 esta declarada, codifique conforme exemplificado abaixo:



type
TForm1 = class(TForm)
procedure FormPaint(Sender: TObject);
private
{ Private declarations }
public
{ Public declarations }
end;
(* Ao ter declarado o cabeçalho deste procedimento na interface ele passou
a estar visível não só para toda unit, mas também para outras que a referenciarem.*)
procedure DesenhaPoligono;

var
Form1: TForm;
.
.

Agora, codifique o corpo do porcedimento “DesenhaPoligono” na seção “implementation”.


procedure DesenhaPoligono;
var
X, Y, DX, DY: Integer;
begin
Randomize;
X := Random(Form1.Width - 10);
Y := Random(Form1.Height - 250);
Randomize;
Form1.Canvas.Pen.Color := Random(65535);
Form1.Canvas.Pen.Width := Random(7);
DX := Random(400);
DY := Random(400);
Form1.Canvas.RoundRect(X, Y, X + DX, Y + DY, DX div 2, DY div 2);
end;

Bem, agora o Form1 ainda não conhece esse novo procedimento, contudo neste momento se você fizer referência a unit1 ele estará visível ao Form1. O compilador, desta forma, não irá reclamar. Segue o código completo da unit1 com as últimas alterações:

unit Unit1;

interface

uses
Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
Dialogs;

type
TForm1 = class(TForm)
procedure FormPaint(Sender: TObject);
private
{ Private declarations }
public
{ Public declarations }
end;
(* Ao ter declarado o cabeçalho deste procedimento na interface ele passou
a estar visível não só para toda unit, mas também para outras que a referenciarem.*)
procedure DesenhaPoligono;

var
Form1: TForm;
.
.


Agora, codifique o corpo do porcedimento “DesenhaPoligono” na seção “implementation”.

procedure DesenhaPoligono;
var
X, Y, DX, DY: Integer;
begin
Randomize;
X := Random(Form1.Width - 10);
Y := Random(Form1.Height - 250);
Randomize;
Form1.Canvas.Pen.Color := Random(65535);
Form1.Canvas.Pen.Width := Random(7);
DX := Random(400);
DY := Random(400);
Form1.Canvas.RoundRect(X, Y, X + DX, Y + DY, DX div 2, DY div 2);
end;

Prosseguindo, o Form1 ainda não conhece esse novo procedimento, contudo neste momento se você fizer referência a unit1 ele estará visível ao Form1. O compilador, desta forma, não irá reclamar. Segue o código completo da unit1 com as últimas alterações:

unit Unit1;

interface

uses
Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
Dialogs;

type
TForm1 = class(TForm)
procedure FormPaint(Sender: TObject);
private
{ Private declarations }
public
{ Public declarations }
end;
(* Ao ter declarado o cabeçalho deste procedimento na interface ele passou
a estar visível não só para toda unit, mas também para outras que a referenciarem.*)
procedure DesenhaPoligono;

var
Form1: TForm;
ZnNota1, ZnNota2, ZnNota3, ZnNota4, ZnMedia: Double;
ResultadoAprovacaoZN: string;

implementation

{$R *.dfm}
const
ValorAprovacao = 6;

procedure TForm1.FormPaint(Sender: TObject);
begin
unit1.DesenhaPoligono;
(* Sída de dados - Outpu*)
Form1.Canvas.TextOut(12, 12, Format('1° Nota: %f', [ZnNota1]));
Form1.Canvas.TextOut(12, 32, Format('2° Nota: %f', [ZnNota2]));
Form1.Canvas.TextOut(12, 52, Format('3° Nota: %f', [ZnNota3]));
Form1.Canvas.TextOut(12, 72, Format('4° Nota: %f', [ZnNota4]));
Form1.Canvas.TextOut(12, 92, Format('Média: %f', [ZnMedia]));

Form1.Canvas.TextOut(12, 112, '*******************************');

Form1.Canvas.TextOut(12, 132, Format('Resultado: %s', [ResultadoAprovacaoZN]));
end;

procedure CalculaMediaZN;
const
Aprovado = 'Parabéns!!! Aluno Aprovado.';
Reprovado = 'Lamento!!! Aluno Reprovado. Vai estudar e RAFM.';
begin
(* Entrada de dados - Input*)

{Preciso converter o valor retornado pela função "InputBox" de string para float}
ZnNota1 := StrToFloat(InputBox('Estação Zn | Algoritmo III',
'Digite a 1° Nota do Aluno:', '0'));
ZnNota2 := StrToFloat(InputBox('Estação Zn | Algoritmo III',
'Digite a 2° Nota do Aluno:', '0'));
ZnNota3 := StrToFloat(InputBox('Estação Zn | Algoritmo III',
'Digite a 3° Nota do Aluno:', '0'));
ZnNota4 := StrToFloat(InputBox('Estação Zn | Algoritmo III',
'Digite a 4° Nota do Aluno:', '0'));
ZnMedia := (ZnNota1 + ZnNota2 + ZnNota3 + ZnNota4) / 4;

(* Processamento *)
if (ZnMedia >= unit1.ValorAprovacao) then
ResultadoAprovacaoZN := Aprovado
else
ResultadoAprovacaoZN := Reprovado;
end;

procedure DesenhaPoligono;
var
X, Y, DX, DY: Integer;
begin
Randomize;
X := Random(Form1.Width - 10);
Y := Random(Form1.Height - 250);
Randomize;
Form1.Canvas.Pen.Color := Random(65535);
Form1.Canvas.Pen.Width := Random(7);
DX := Random(400);
DY := Random(400);
Form1.Canvas.RoundRect(X, Y, X + DX, Y + DY, DX div 2, DY div 2);
end;

initialization
CalculaMediaZN;
end.


Execute o programa e teste.

Artigo completo (View Full Post)

 
BlogBlogs.Com.Br