Procedimentos:
Definem processos não interativos de consulta e atualização da base de dados. Os procedimentos
podem gerar ...
Definição procedural
A diferença das regras das transações onde as especificações se realizam de forma declarativa e
GeneX...
Para cada procedimento se pode definir:
• Source: Aqu o c digo correspondente a l gica do procedimento. Também podem defin...
Por exemplo, vamos supor que queremos implementar um procedimento para imprimir o identificador,
nome e pa s de todos noss...
O Layout de um procedimento será uma sucessão de Printblocks que não tem por que seguir a
ordem em que deseja que apareçam...
O Printblock é um tipo de controle v lido somente nos procedimentos, que é inserido e eliminado do
Layout pelo analista, e...
Nesta seção se define a lógica do procedimento .
A linguagem utilizada para programar o código fonte dos procedimentos é m...
A definição do acesso a base de dados para recuperar a informação se realiza com um único
comando: o comando For each1.
Us...
Intuitivamente com este comando queremos listar identificador, nome e país de cada um dos clientes
da base de dados. Ou se...
Dentro de todo For each navega-se - percorre ou itera - a tabela base, mas podemos acessar as
tabelas que constituem sua t...
A tabela base correspondente a essa tabela estendida é chamada de tabela base do For each
e será percorrida seqüencialment...
Para o exemplo apresentado onde queremos uma lista dos clientes: seu identificar, nome e nome de
país, observamos os atrib...
Listagem de navegação
GeneXus oferece para todos os objetos uma lista conhecida como listagem de navegação, que é o
result...
Para restringir os dados que queremos listar no For each são utilizadas as cláusulas where do comando.
Se na listagem de c...
Listagem de navegação
Aparece um novo elemento nesta listagem que não existia, quando não tínhamos cláusulas where: os
con...
Os atributos utilizados nas condições de filtro podem ser de qualquer tabela estendida do For each.
No exemplo, a tabela b...
Se queremos realizar uma lista de todos os clientes, ordenado por nome do cliente ao invés do
código, precisamos modificar...
Listagem de navegação
Quando não existe um índice que satisfaça o ordem de um For each, como é o caso do Exemplo, o
analis...
Se queremos filtrar os clientes por determinada faixa de nomes, o primeiro exemplo, como não foi
especificada a cláusula o...
A listagem de navegação nos informa que a consulta está otimizada já que percorre somente os
registros incluídos no filtro...
Por exemplo, se temos as transações:
COUNTRY CITY
{ {
CountryId* CountryId*
} CityId*
}
O For each:
For Each order CityId
...
Pode ocorrer de que um For each tenha mais de uma tabela base cuja estendida contenha os atributos do For
each, sendo míni...
Podem aparecer vários atributos, no caso de um único atributo não determinar a tabela base, onde
ao menos um deles deverá ...
O Printblock menssage (poderá ter um texto advertindo ao usuário que não existam clientes que
cumpram os filtros) executa ...
A Sintaxe apresentada generaliza o exemplo com o que estamos trabalhando.
Order order_attributes::= att1, …, attn
É uma li...
Escolha do índice: GeneXus escolhe automaticamente o índice que utilizará para satisfazer a ordem. GeneXus sempre tentará
...
Os comandos especificados serão executados seqüencialmente para os dados da tabela estendida que cumpram
as Condições de f...
Os code snippets são moldes de c digo que GeneXus tem predefinido, que nos ajudam a
escrever o c digo fonte. Quando estamo...
O For each é um comando como outros, e portanto pode aparecer várias vezes dentro do Source,
tanto em forma paralela (inde...
O For each é uma estrutura repetitiva, que permite recuperar muitos registros de uma tabela. Quando
pensamos em For eachs ...
Se queremos realizar uma lista de todas as faturas do sistema, onde para cada uma se mostre tanto
a informação do cabeçalh...
Como para um For each simples, o GeneXus deve determinar para cada For each (principal e
aninhado) qual é sua tabela base....
Quando temos dois For eachs aninhados,GeneXus deve determinar a tabela base de cada um, e
essas serão as tabelas que se na...
Consideremos o caso mais simples, de um par de For eachs aninhados.
• A determinação da tabela base do For each principal,...
Para o exemplo apresentado anteriormente, mostramos acima como se determina cada tabela base.
Para a do aninhado, como os ...
Da determinação das tabelas base, surgem os três casos de For eachs aninhados que foram
mencionados e que estudaremos um a...
Este é o caso em que o GeneXus determina que as tabelas base de cada For each são distintas e tem
uma espécie de relação 1...
Vejamos, como o GeneXus faz para determinar as tabelas base. Os atributos utilizados no For each
externo são CustomerId e ...
Se queremos realizar uma listagem das faturas emitidas pelo país, teremos outro caso de For eachs
aninhados com distintas ...
Neste caso o GeneXus não encontra uma relação 1-N direta ou indireta entre as tabelas e portanto
não aplica filtros implíc...
No exemplo que vimos anteriormente, da listagem de clientes e suas faturas, o que acontece se um
cliente não possuir fatur...
GeneXus oferece uma forma de programar o que foi visto de uma forma simples.
O pseudocódigo visto na página anterior se pr...
Um controle de corte é fácil de programar e pode ser feito seguindo as considerações anteriores.
Na ordem do For each mais...
Exemplo: Controle de corte duplo
Vamos supor que queremos como antes listar os clientes e suas faturas, mas queremos agrup...
2. Determinação da navegação
Depois de determinadas as tabelas base, GeneXus determina a navegação. Como neste caso são tr...
Recomendamos programar em GeneXus este procedimento e observar cautelosamente a listagem
de navegação.
Verá que GeneXus es...
Os comandos introduzidos são similares aos existentes nas linguagens de Programação imperativa
conhecidas, por isso não in...
For in Expression:
• Expression é qualquer expressão cujo valor seja uma coleção, vetor ou matriz.
• var é uma variável qu...
Aqui veremos alguns comandos de impressão, que permitem desenhar a saída do procedimento.
Print
Onde nomePrintBlock é o id...
Também poder amos ter escrito diretamente: Print title no início do Source, mas neste caso, se a
listagem tem várias p gin...
Desenho da saída
Existem alguns comandos para desenhar a saída do procedimento. Apresentamos aqui alguns
efeitos da docume...
Em Web as listagens somente podem ser PDF e devem ser configuradas as propriedades e regras
anteriores para que funcionem....
Nesta seção se permite estabelecer condições que devem cumprir os dados para ser recuperados.
Uma “condição” é equivalente...
onde:
• “customer” é um Printblock que contem os atributos CustomerId, CustomerName, CountryName
• “invoice” é um Printblo...
Resumimos aqui as distintas formas de filtrar em um procedimento a informação da base de dados a ser
recuperada:
a. cláusu...
07 procedures-curso gxxbr
Próximos SlideShares
Carregando em…5
×

07 procedures-curso gxxbr

1.923 visualizações

Publicada em

Genexus Course

Publicada em: Tecnologia
0 comentários
0 gostaram
Estatísticas
Notas
  • Seja o primeiro a comentar

  • Seja a primeira pessoa a gostar disto

Sem downloads
Visualizações
Visualizações totais
1.923
No SlideShare
0
A partir de incorporações
0
Número de incorporações
2
Ações
Compartilhamentos
0
Downloads
33
Comentários
0
Gostaram
0
Incorporações 0
Nenhuma incorporação

Nenhuma nota no slide

07 procedures-curso gxxbr

  1. 1. Procedimentos: Definem processos não interativos de consulta e atualização da base de dados. Os procedimentos podem gerar um arquivo formato PDF, mediante o qual é possível listar informação na tela ou impressora. Além disso, os procedimentos podem atualizar a base de datos1. _____________________________________________________________________________ 1 Como veremos mais adiante, existe um tipo de dados especial, que não é estritamente um tipo de dados, mas algo um pouco mais complexo, o business component, por meio do qual serão realizados atualizações a base de dados em qualquer objeto GeneXus. Portanto, utilizando variáveis de tipo de dados business component, poderão ser realizadas atualizações incluso nos objetos que por natureza não oferecem esta possibilidade, como as web panels.
  2. 2. Definição procedural A diferença das regras das transações onde as especificações se realizam de forma declarativa e GeneXus determina no momento de gerar o programa a seqüência de execução, nos procedimentos as especificações se realizam de forma procedural. Desta forma, a seqüência de execução é determinada pelo analista, utilizando para isso uma linguagem simples que contem comandos de controle, de impressão, de acesso a base de dados, etc. Definição sobre a base de conhecimento A grande potencia da linguagem dos procedimentos está que as definições são realizadas sobre a base de conhecimento e não diretamente sobre o modelo f sico (tabelas, ndices, etc.). Isto nos permite utilizar autom ticamente todo o conhecimento já incorporado ou gerado por GeneXus a partir das especificações realizadas. Por exemplo, se desejamos mostrar o resultado de uma f rmula é suficiente nomear o atributo f rmula no lugar adequado e GeneXus dispara o c lculo mostrando o resultado, sem necessidade do analista oferecer nenhuma outra informação. A informação de como se calcula um atributo f rmula est contida na base de conhecimento. Também podemos utilizar o conceito de tabela estendida, já que GeneXus conhece as relações entre as tabelas da base de dados, o analista não precisa explicitar estas relações na hora de recuperar dados. Independência da base de dados: definição a nível de atributos A definição dos procedimentos se faz a nível de atributos: não é necessário indicar expl citamente quais tabelas ser ndices. Somente mencionando os atributos que deseja acessar é suficiente para que o GeneXus determine esta informação. Isto é possível porque GeneXus possui um completo conhecimento da estrutura da base de dados. Desta maneira obtemos uma real independência da base de dados, já que qualquer alteração nas tabelas ser gerenciado automaticamente pelo GeneXus e desta forma, para atualizar os programas alcança em grande parte das vezes, como regerar os objetos sem ter que modificar nada do programado neles.
  3. 3. Para cada procedimento se pode definir: • Source: Aqu o c digo correspondente a l gica do procedimento. Também podem definir-se ao final do c digo subrotinas1 que podem ser chamadas a partir do próprio código mediante o comando adequado. • Layout: As como as transações possuem uma tela (form), os procedimentos possuem um layout de saída. Nesta seção se define apresentação do procedimento: os dados que se quer listar e o formato da saída. • Regras-Propriedades: Definem aspectos gerais do procedimento, como seu nome, descrição, tipo de saída (impressora, arquivo, tela), parâmetros que recebe o objeto, etc. • Condições: Condições que devem cumprir os dados para ser recuperados (filtros). • Variáveis: Variáveis locais ao objeto. • Ajuda: Permite a inclusão de texto de ajuda, para ser consultado pelos usuários em tempo de execução, para o uso do procedimento. Pode ter uma ajuda para cada linguagem. • Documentação: Permite a inclusão de texto t cnico, para ser utilizado como documentação do sistema. _____________________________________________________________________________________ 1 Não serão vistas no presente curso. Ver no Curso Não Presencial de GeneXus.
  4. 4. Por exemplo, vamos supor que queremos implementar um procedimento para imprimir o identificador, nome e pa s de todos nossos clientes e queremos que a listagem saia como mostrada na figura. Para isso, devemos identificar na saída da listagem das distintas reas que o compõem. A cada uma delas a representaremos com um Printblock. Os primeiros dois Printblocks ilustram no GeneXus tal qual as primeiras duas reas pois contem unicamente textos, linhas, retângulos. Também poderíamos ter colocado estas duas reas convertendo-as em uma e utilizando portanto um nico Printblock. O terceiro Printblock ser o correspondente da rea de dados variáveis da figura anterior, que representa informação que deve ser extra da da base de dados. O que queremos mostrar neste caso é o identificador e nome de cada cliente, junto com o nome do pa s ao que pertence. Esta informação é a representada pelos atributos CustomerId, CustomerName e CountryName da base de conhecimento da aplicação, o terceiro Printblock conter os três controles atributo CustomerId, CustomerName e CountryName. Transformando as reas em Printblocks, o Layout do procedimento ficará como o da figura na p gina seguinte.
  5. 5. O Layout de um procedimento será uma sucessão de Printblocks que não tem por que seguir a ordem em que deseja que apareçam na saída. No exemplo anterior, o mesmo procedimento teria sido impresso se houvesse especificado os Printblocks na ordem inversa (ou em qualquer ordem). Aqui simplesmente são declarados. A ordem que são executados fica determinado na seção Source que é a que contem a lógica do procedimento. A partir dali serão chamados mediante um comando específico para tal finalidade (o comando print). Por esta razão, cada Printblock deve ter um nome único para poder ser referenciado depois a partir do Source. No exemplo, para listar todos os clientes, o Printblock de nome “customer” deve ser chamado dentro de uma estrutura repetitiva no Source. Esta estrutura repetitiva é o comando For each que estudaremos depois.
  6. 6. O Printblock é um tipo de controle v lido somente nos procedimentos, que é inserido e eliminado do Layout pelo analista, e que contem outros controles -atributos, textos, retângulos, linhas, etc.-, sendo estes ltimos os que efetivamente especificam qu é o que se quer mostrar na saída. Para inserir os controles no Form de uma transação contamos com uma toolbox. A mesma toolbox se utiliza para inserir os controles no Layout. De fato esta toolbox está disponível para todos os objetos GeneXus criados, e em cada caso terá os controles disponíveis segundo o tipo de objeto. Para inserir um Printblock - botão direito em qualquer lugar do layout e selecionamos Insert Printblock. Como todo controle, o Printblock possui propriedades que podem ser configuradas pelo usuário. Em particular, tem a propriedade “Name”, muito importante visto que é o identificador do Printblock. Com este identificador é que o Printblock pode ser chamado a partir do Source para ser impresso. Para acessar as propriedades de um Printblock, o selecionamos e pressionamos F4 ou View/Properties.
  7. 7. Nesta seção se define a lógica do procedimento . A linguagem utilizada para programar o código fonte dos procedimentos é muito simples, e consta de alguns comandos que veremos. O estilo de programação é procedural – imperativo – o Source será uma sucessão de comandos onde a ordem é fundamental: a ordem em que estejam especificados corresponderá, exceto exceções, a ordem em que serão executados. Existem, como em toda linguagem imperativa, comandos de controle para a execução condicional (if, do case), o repetitivo (do while, for), para chamar a outro objeto (call), para cortar as iterações dentro de um loop (exit) ou abandonar o programa (return), assim como também comandos específicos desta linguagem: para imprimir um Printblock do Layout (print), para acessar a base de dados (For each), para inserir novos registros em uma tabela (new), para chamar a uma subrotina (do), etc. No final da sucessão de comandos que constitui o código geral ou principal do procedimento, podem definir-se subrotinas que podem ser chamadas (mediante o comando do) a partir do código geral. Não podem ser chamadas a partir de outro objeto (são locais). Por sua importância, começamos estudando detalhadamente o comando de acesso a base de dados, fundamental na hora de recuperar a informação armazenada. Depois serão tratados brevemente os comandos de controle, que são comuns a todos as linguagens de programação imperativa, os comandos de atribuição e os de impressão.
  8. 8. A definição do acesso a base de dados para recuperar a informação se realiza com um único comando: o comando For each1. Usando o For each se define a informação que vai acessar. A forma de o fazer é baseada em nomear os atributos a utilizar. Assim, com este comando se definem quais atributos são necessários em qual ordem vai ser recuperada, e GeneXus se encarrega de encontrar como fazer. Não se especifica de quais tabelas se devem obter, nem quais índices se devem utilizar para acessar a essas tabelas: isso GeneXus infere. Evidentemente isto nem sempre é possível, e em tais casos GeneXus dá uma série de mensagens de erro indicando por que não se podem relacionar os atributos envolvidos. A razão pela qual não se faz referencia ao modelo físico de dados é porque desta maneira a especificação do procedimento é de mais alto nível possível, de tal forma que ante mudanças na estrutura da base de dados a especificação do mesmo se mantenha válida a maior parte das vezes. Quando aparece um For each se está indicando que se quer recuperar informação da base de dados. Concretamente GeneXus sabe que com um For each se quer percorrer (ou navegar) uma tabela. Para cada registro dessa tabela, se quer fazer algo com a informação associada (ex: imprimir). Portanto, todo comando For each possui uma tabela física associada: a tabela que será percorrida ou navegada. A esta tabela vamos chamar tabela base do For each. ______________________________________________________________________________ 1 Quando estudarmos os business components veremos que utilizando seu método Load também se consegue consultar a base de dados.
  9. 9. Intuitivamente com este comando queremos listar identificador, nome e país de cada um dos clientes da base de dados. Ou seja, queremos percorrer à tabela CUSTOMER, e para cada cliente seja recuperado da tabela COUNTRY o nome do país ao qual pertence, imprimindo esta informação, junto com o identificador e nome do cliente. (Observar que a tabela COUNTRY pertence à estendida de CUSTOMER) Como o GeneXus infere isto, só o que fizemos foi For each do exemplo informar os atributos que nos interessava mostrar?
  10. 10. Dentro de todo For each navega-se - percorre ou itera - a tabela base, mas podemos acessar as tabelas que constituem sua tabela estendida para recuperar a informação, por pertencer a estendida está relacionada com cada registro da tabela base com que esteja trabalhando em cada interação (o conceito de tabela estendida é muito importante neste comando e sugerimos repassar sua definição). É por isso que no For each do exemplo, a tabela base será CUSTOMER, e acessa “para cada” cliente, não somente os dados de seu registro, como também do registro associado na tabela COUNTRY (que está na estendida de CUSTOMER). Dizemos então que se percorre CUSTOMER e se acessa além disso a de COUNTRY para buscar o resto da informação requerida. Como podemos ver claramente no exemplo apresentado, não apresentamos de forma explícita ao GeneXus esta informação. Não é necessário, já que o GeneXus conhece as relações entre as tabelas, e na base os atributos mencionados dentro do For each e pode encontrar sem necessidade de mais informações uma tabela estendida que os contenha. A tabela base dessa estendida é escolhida como tabela base do For each.
  11. 11. A tabela base correspondente a essa tabela estendida é chamada de tabela base do For each e será percorrida seqüencialmente, executando para cada registro o que for indicado nos comandos internos do For each.
  12. 12. Para o exemplo apresentado onde queremos uma lista dos clientes: seu identificar, nome e nome de país, observamos os atributos utilizados dentro do For each, e percebemos que eles são os contidos no print block de nome “customer”: CustomerId, CustomerName e CountryName. Em que tabelas estão estes atributos? • CustomerId est em 2 tabelas: - CUSTOMER como chave primária (PK). - INVOICE como chave estrangeira (FK). • CustomerName est somente em CUSTOMER (é um atributo secundário). • CountryName est somente em COUNTRY (é um atributo secundário). GeneXus conhece as relações entre as tabelas. Podemos ver o diagrama correspondente às tabelas nas quais aparecem os atributos do For each (Tools/Diagrams). Aqui podemos ver porque do requerimento da tabela estendida seja a mínima (entendendo por mínima aquela que envolve um número menor de tabelas). A tabela estendida de INVOICE também contêm todos os atributos do For each, mas não é mínima, pois a de CUSTOMER também os contêm. Portanto, se vamos percorrer seqüencialmente à tabela CUSTOMER, e para cada registro dessa tabela, vamos acessar a tabela COUNTRY, e recuperar o registro da mesma que cumpre: COUNTRY.CountryId = CUSTOMER.CountryId e para o mesmo recupera-se o valor do atributo CountryName, para poder imprimi-lo, junto com o código e nome do cliente.
  13. 13. Listagem de navegação GeneXus oferece para todos os objetos uma lista conhecida como listagem de navegação, que é o resultado da especificação do objeto. Esta listagem é muito útil para os relatórios, já que indica quais são as tabelas que são acessadas em cada For each do Source, se existe um índice para recuperar os dados da tabela base, e em caso de que assim seja qual é esse índice (seu nome), se aplicam filtros sobre os dados ou se devemos listar todos, etc. Desta maneira, o analista não tem que executar o objeto para verificar se a lógica é a esperada. Estudando a listagem de navegação já têm a informação necessária para saber se está percorrendo a tabela esperada, se estão aplicando corretamente os filtros desejados, etc. Como pode ser visto, esta listagem mostra para o comando For each que aparece no Source, qual é a tabela base do mesmo, por que ordem essa consulta é resolvida (a ordem que os resultados são impressos), se existe um índice que satisfaça essa ordem, qual é seu nome, e aparecem mais duas informações envolvidas: os filtros de navegação e o diagrama de tabelas. Os filtros da navegação indicam que faixa da tabela base que vai ser percorrida. No Exemplo é percorrida toda a tabela base do For each: começando pelo primeiro registro de CUSTOMER, até que chegue ao fim de tabela (utilizando o índice ICUSTOMER). Também é mostrado num pequeno diagrama de tabelas, a tabela base do For each com sua chave primária, e endentadas todas as tabelas da estendida que devam ser acessadas para recuperar a informação associada ao registro da tabela base que está sendo trabalhada em cada interação do For each. Neste caso se mostra somente a tabela COUNTRY. No comando For each do Exemplo não aparece explicitamente nenhuma informação referente a ordem desejada da impressão da informação do relatório. Neste caso GeneXus escolhe a ordem da chave primária da tabela base do For each. É por esta razão que no For each do Exemplo, GeneXus determinou que a ordem é realizado pelo atributo CustomerId, chave primária da tabela CUSTOMER.
  14. 14. Para restringir os dados que queremos listar no For each são utilizadas as cláusulas where do comando. Se na listagem de clientes não queremos listar todos os clientes, mas apenas aqueles cujo nome esteja dentro de uma faixa inserida pelo usuário, então devemos agregar ao For each que havíamos visto uma cláusula where, para especificar os filtros desejados sobre os dados: For each where (CustomerName >= &Start) and (CustomerName <= &End) print customer Endfor onde as variáveis &Start e &End devem ser definidas no procedimento com o mesmo tipo de dados que CustomerName, e as carregar com valores fixos ou recebidos por parâmetro1. Com a cláusula where definida estamos dizendo ao GeneXus que não queremos todos os registros da tabela base, e sim, somente aqueles que satisfaçam a condição booleana da cláusula. No exemplo escrevemos uma cláusula somente where com uma condição composta, mas poderíamos ter programando o mesmo com duas cláusulas where, como mostramos no slide acima. Quando aparecem vários “where” a condição de filtro que vai ser aplicada sobre os dados é a conjunção booleana de todas as Condições dos “where” que apareceram. Observemos que no slide os comandos where estejam condicionadas com as claúsulas when. Se interpreta da seguinte forma: os filtros estabelecidos pelo where são aplicados somente quando o when é satisfeito. Isto é lido da seguinte forma: o filtro estabelecido será aplicado pelo where somente quando satisfaça a condição do when. No exemplo, somente é aplicado o primeiro filtro: “CustomerName >= &Start” se a variável &Start não for vazia. Se for vazia, este filtro não será aplicado. Igual é ao caso da segunda cláusula when. Observar que se &Start e &End estejam vazios, não serão aplicados nenhum dos comandos where, e portanto serão listados todos os clientes (como se não existissem os comandos where). Cada condição booleana de um “where” pode estar composta de várias expressões booleanas concatenadas com os operadores lógicos and, or e not. _____________________________________________________________________________________ 1 A través de um objeto que é pedido ao usuário, por exemplo um Web Panel.
  15. 15. Listagem de navegação Aparece um novo elemento nesta listagem que não existia, quando não tínhamos cláusulas where: os constraints (restrições). Que informação ganhamos com esta listagem de navegação? • que a tabela base do For each continua sendo CUSTOMER • que a ordem da consulta continua sendo por CustomerId, utilizando o índice ICUSTOMER correspondente • que continua percorrendo toda a tabela CUSTOMER em busca da informação • mas para cada cliente avalia se cumpre as restrições que aparecem enumeradas (CustomerName>= &Start se &Start não estiver vazia e CustomerName<=&End se &End não estiver vazio) e somente caso se cumpra, executar para esse cliente os comandos que aparecem dentro do For each. Neste caso, imprime os valores dos atributos do Printblock “customer”: CustomerId, CustomerName, CountryName. • que deve acessar a tabela COUNTRY cuja chave primária é CountryId para obter algum dado (CountryName)
  16. 16. Os atributos utilizados nas condições de filtro podem ser de qualquer tabela estendida do For each. No exemplo, a tabela base do For each é CUSTOMER, estamos filtrando os dados a serem recuperados utilizando o atributo CountryName, que é da tabela COUNTRY, pertencente à tabela estendida de CUSTOMER. Neste exemplo nada foi feito em relação à ordem, os dados aparecerão ordenados pela chave primária da tabela base, ou seja, pelo identificador de cliente, CustomerId. .
  17. 17. Se queremos realizar uma lista de todos os clientes, ordenado por nome do cliente ao invés do código, precisamos modificar o comando For each agregando esta informação da ordem. Fazemos isso utilizando a cláusula order do For each, como mostramos no primeiro exemplo. Como não existe um índice definido na tabela CUSTOMER por atributo CustomerName, o GeneXus indicará na listagem de navegação mediante uma advertência (“warning”) que não existe um índice para satisfazer a ordem, o que pode ocasionar problemas de performance, dependendo da plataforma de implementação, da quantidade de registros que devem ser lidos da tabela, etc. Em ambientes cliente/servidor, se não existe índice para satisfazer a ordem, o For each se traduz numa consulta SQL (“select”) que é resolvida pelo motor do DBMS da plataforma. Igual o caso dos comandos where, em plataformas cliente/servidor a cláusula order pode ser condicional,como o exemplo dois. Caso a condição when não for cumprida, essa ordem não será utilizada e não existir ordem incondicional (sem a cláusula when) como no exemplo, a ordem será indefinida, isto é, a ordem poderá variar de DBMS a DBMS e inclusive entre execuções seguidas. Podem ter várias cláusulas order consecutivas condicionais (com cláusula when) em arquiteturas cliente/servidor e uma sem condição (a última da lista). O primeiro comando order cuja condição do when for satisfeita, será a ordem escolhida para ser utilizada. Clásula Order None: clásula que evita que GeneXus escolha por default a ordem dos atributos da chave primária da tabela base e utiliza uma ordem de navegação indefinida. Se utilizar a cláusula Order None, GeneXus entende que não deseja estabelecer nenhuma ordem em particular e delega esta tarefa ao DBMS. A cláusula order none pode ter condição (when).
  18. 18. Listagem de navegação Quando não existe um índice que satisfaça o ordem de um For each, como é o caso do Exemplo, o analista GeneXus pode resolver criá-lo (índice de usuário). Na maioria dos casos o relatório será mais eficiente desta maneira, mas o índice é mantido (implicando num maior armazenamento e maior processamento para que o índice seja mantido atualizado) Não é possível recomendar a priori qual das duas soluções é a melhor (índice temporário vs índice de usuário), portanto deve-se estudar, caso por caso, a solução particular levando em consideração a plataforma de implementação e sendo fundamental a analisar a freqüência que o relatório é executado. De qualquer maneira, se no início não foi definido o índice de usuário e posteriormente decide-se fazê-lo, somente é preciso que o relatório seja gerado novamente (sem modificar nada do que foi programado) e o relatório passará a utilizá-lo. A listagem de navegação anterior mostra o seguinte: • a tabela base do For each é CUSTOMER • a ordem é por CustomerName • não existe um índice definido para esse atributo • toda a tabela CUSTOMER com a ordem especificada será recorrida • não tem condições de filtro, todos os registros da tabela executarão os comandos dentro do For each (neste caso, o comando print)
  19. 19. Se queremos filtrar os clientes por determinada faixa de nomes, o primeiro exemplo, como não foi especificada a cláusula order, GeneXus ordena pela chave primária, ou seja, CustomerId. Neste caso, a listagem de navegação mostra que deve-se percorrer toda a tabela CUSTOMER, e para cada registro da mesma deve-se analisar se o registro cumpre ou não as condições (restrições ou “constraints”). Em caso afirmativo, será impresso o mesmo os dados correspondentes. Ao invés de ordenar os dados por CustomerId vamos fazer uma ordenação por CustomerName, como no segundo exemplo, a tabela base é percorrida ordenada por CustomerName e como nos filtros estabelecemos que queremos somente aqueles clientes cujo nome, CustomerName, esteja na faixa determinada, já não sendo necessário recorrer toda a tabela base para obter os dados que cumprem com as Condições! A segunda consulta está otimizada nesse sentido. Prestar atenção que o GeneXus não criou um índice por CustomerName de forma automática, e aqui temos que avaliar se é conveniente criar um índice de usuário (será mantido pelo GeneXus depois de criado) ou não criar o índice e deixar que seja criado um índice temporário em execução para resolver a consulta se o DBMS não pode fazer de outra forma. A listagem de navegação mostra se a consulta está ou não otimizada, de acordo se toda a tabela é percorrida (desde “First Record” até “End of table”) ou apenas uma faixa mais reduzida
  20. 20. A listagem de navegação nos informa que a consulta está otimizada já que percorre somente os registros incluídos no filtro (desde CustomerName>=&Start até CustomerName <=&End). Para determinar a ordem levar em consideração: • Os atributos da cláusula Order especificada pelo usuário • As restrições que aplicam ao nível (atributos mencionados na regra Parm do procedimento, condições explícitas tanto no Where como nas Conditions) • A existência de índices sobre estes atributos. Distinguimos 2 casos: 1) Se escreve uma cláusula order •O For each fica ordenado por esses atributos, exista ou não um índice por estes. •Se não existe um índice com os atributos do order, mas existem condições implícitas ou condições explícitas por igualdade, se busca se existe um índice que contenha os atributos das condições mais os do Order. A condição explícita prevalece sobre a implícita para a determinação do Order, em caso que sejam diferentes e exista índice por cada uma delas. •Se existe um índice, os atributos das condições serão agregadas na lista do Order para que dito índice seja considerado em seu lugar. 2) Não se escreve cláusula order •Neste caso, se existe algum índice para os atributos da condição, o Order fica determinado pelos atributos do índice. •Se não existe um índice que corresponda com as condições, ou seja que não se pode otimizar a percorrida segundo as condições do nível, então se ordena pelos atributos da Primary Key.
  21. 21. Por exemplo, se temos as transações: COUNTRY CITY { { CountryId* CountryId* } CityId* } O For each: For Each order CityId Where CountryId = 1 ... Endfor Percorre a tabela CITY, ordenando por: CountryId, CityId e utilizando o índice ICITY (índice por chave primária que contem ambos atributos). Ainda é o próprio DBMS que resolve o plano de acesso mais apropriado, a informação antes mencionada influirá em sua determinação.
  22. 22. Pode ocorrer de que um For each tenha mais de uma tabela base cuja estendida contenha os atributos do For each, sendo mínima. Frente esta ambigüidade, GeneXus escolhe a “primeira” destas tabelas estendidas mínimas. Para resolver este tipo de ambigüidade surge a cláusula defined by, que permite usar atributos da tabela base desejada, que não serão utilizados para devolver a consulta ordenada por esses atributos, nem para filtrar informação, nem para ser mostrados no relatório (não tem nenhuma funcionalidade com respeito aos dados à recuperar), apenas para aportar mais informação que permita determinar a tabela base do for each. Na cláusula Defined by devemos fazer referência pelo menos à um atributo da tabela base desejada. Da mesma forma, pode ser utilizada para modificar qual é a tabela base em caso de não utilizar nenhum atributo mais dentro do For each. Este é o caso do exemplo apresentado, não que não queremos listar todos os clientes da tabela CUSTOMER, pelo contrário, queremos listar todos os clientes das faturas. Se não utilizar nenhum atributo dentro do For each de INVOICE , a tabela base é a de CUSTOMER. Na maioria dos casos não é necessário utilizar este comando. Para procedimentos mais ou menos complexos, quando não exista problema de ambigüidade, recomenda-se o uso do Defined by pois melhora bastante o tempo de especificação do procedimento. Contudo, não é aconselhável seu uso indiscriminado. A desvantagem de utilizar esta cláusula quando não é necessário é que “ata” um pouco mais o código ao desenho das tabelas. Por exemplo, não foi criada uma tabela COUNTRY, e para cada cliente o país que ele pertence, como atributo secundário. Se quisermos uma listagem dos clientes e seu país, seriam equivalentes: For each For each Defined by CountryName print customer print customer Endfor Endfor onde customer é um Printblock com os atributos CustomerId, CustomerName e CountryName. Se agora decidir criar a tabela COUNTRY e ter na transação “Customer” a CountryId como FK, o primeiro For each continuará sendo válido e fazendo o que queremos, o segundo deixará de funcionar, ja que no Defined By não tem nenhum atributo da tabela base.
  23. 23. Podem aparecer vários atributos, no caso de um único atributo não determinar a tabela base, onde ao menos um deles deverá estar associado à tabela base desejada. Recomenda o uso do defined by de atributos secundários da tabela base que desejamos navegar, já que os atributos secundários somente podem estar em uma tabela do modelo e desta forma eliminamos por completo toda possível ambigüidade. Isto não é obrigatório, podemos usar no Defined by para atributos primários quando notarmos que não haverá ambigüidade na eleição da tabela base. Um erro comum é acreditar que quando um For each tem esta cláusula, a tabela base do mesmo fica determinada exclusivamente a partir dos atributos mencionados no defined by. A realidade é que os atributos do defined by determinam uma ou mais tabelas base candidatas a serem a tabela base do For each, mas tendo selecionado a tabela base candidata, sua estendida contêm todos os demais atributos do For each, além dos do defined by. Se nenhuma das possíveis tabelas base candidatas cumprem esta condição, então o relatório dará um erro ao ser especificado, e não poderá ser gerado (recordemos que todos os atributos do For each devem estar contidos em uma mesma tabela estendida).
  24. 24. O Printblock menssage (poderá ter um texto advertindo ao usuário que não existam clientes que cumpram os filtros) executa somente quando não entrar no For each, isto é, quando não tem nenhum registro correspondente na tabela base do For each para que se cumpram as Condições de filtro Também aplica-se o For each [selected] line, XFor Each y XFor First, comandos que veremos mais adiante. A cláusula when none deve ser a última dentro do For each. As ações a serem realizadas quando não existe nenhum registro que cumpra as condições, ficam determinadas pelo bloque de código que tem dentro cláusula when none do For each e do endfor. Quando um For each não tem condições de filtro, os comandos do When none serão executados somente no caso em que a tabela base do For each esteja vazia, porque somente nesse caso não haverá nenhum registro que cumpra as condições de filtro. Importante: •Se aparecer atributos no bloque de código correspondente ao When none, estes não são levados em consideração para determinar a tabela base do For each. • Se incluir For eachs dentro do When none não se inferem Joins nem filtros de nenhum tipo com respeito ao For each que contêm o When none, já que são considerados dos For eachs paralelos.
  25. 25. A Sintaxe apresentada generaliza o exemplo com o que estamos trabalhando. Order order_attributes::= att1, …, attn É uma lista de atributos, que indica a ordem da consulta, sendo atti um atributo da base de conhecimento escrito simples, ou entre parênteses. Quando um atributo do order aparece entre parênteses está indicando a ordem descendente para o mesmo. Podem se mencionar atributos da tabela estendida. É possível definir várias cláusulas order condicionais, e uma incondicional, que deveria ser a última listada. Respondendo ao fato de que somente uma dessas cláusulas order tomará efeito, se vão avaliando suas condições (as do when) até a primeira que de True, e com essa fica. Se nenhuma der true e existe uma cláusula incondicional (isto é, sem when), pega essa ordem. Se não existe tal cláusula, ou ordem será indefinido, querendo isto significa que dependerá da plataforma, e incluso poderá variar entre execuções sucessivas. A justificatova para escrever cláusulas order condicionais, deriva se quisermos aplicar cláusulas where condicionais. Isto é, por motivos de otimização das consultas. Por exemplo, se queremos filtrar por CustomerName > &Name when not &Name.IsEmpty(), então para otimizar a consulta deveríamos ordenar por CustomerName, mas se não aplicar o filtro, visto que &Name está vazio, então será melhor deixar uma ordem indefinida. Para isso especificamos a cláusula order condicional: order CustomerName when not &Name.IsEmpty() Ao invés do exemplo anterior, também pode ser especificado uma cláusula order none que é utilizada quando não nos interessa uma ordem em particular e queremos que fique indefinido.
  26. 26. Escolha do índice: GeneXus escolhe automaticamente o índice que utilizará para satisfazer a ordem. GeneXus sempre tentará encontrar a melhor ordem possível para que a consulta seja otimizável, isto é, coincida com algum índice definido na base de dados. Para determinar a ordem é levado em consideração: •Os atributos da cláusula Order especificada pelo usuário •As restrições que aplicam ao nível (atributos mencionados na regra Parm do procedimento, condições explícitas tanto no Where como nas Conditions) •A existência de índices sobre estes atributos. Ainda é o próprio DBMS que resolve o plano de acesso mais apropriado, a informação antes mencionada influirá em sua determinação. Os atributos do order são levados em consideração na hora de determinar a tabela base do For each. Mas eles por si só não determinam. Devem examinar-se também outras partes do For each. Using DataSelectorName Permite definir filtros de acordo ao critério definido no DataSelector1 definido em DataSelectorName. Where Condition Condição booleana que deverão cumprir os dados para ser processados dentro do For each, podendo ser uma condição composta, utilizando os operadores l gicos and, or e not. Os atributos que apareçam na condição booleana podem ser tanto da tabela base do For each como da estendida. Como se desprende da sintaxe, para um mesmo For each podem especificar-se n cl usulas where sucessivas, cada uma com uma condição: where cond1 where cond2 ... where condn A ocorrência de n cl usulas where é equivalente a ocorrência de uma única cl usula, com a conjunção booleana das condições: where cond1 and cond2 and and condn Os dados da tabela estendida do For each que cumpram com todas as condições dos where ser os processados nos comandos internos ao For each (os do bloque de c digo code1). Da mesma forma que ocorre com a cl usula order, pode condicionar os filtros (com cl usulas when). Desta maneira, primeiro se avalia a cl usula when de cada cl usula where, e caso a condição seja cumprida, aplicam o filtro especificado no where. Para que uma restrição condicional possa ser gerada como tal, a condição do when tem que ser “avaliável" pelo DBMS que se está utilizando, isto é, GeneXus tem que saber como escrever a condição na linguagem própria do DBMS utilizado. Se não puder gerar como tal (porque o gerador não o suporta ou porque a condição não pode ser escrita na linguagem do DBMS) se transformará em um filtro "comum" substituindo o WHEN por um OR. Além disso, se gerar a mensagem de código spc0053 – ‘Unsupported conditional constraint”%1” changed to standard constraint %2.’ - no Diagrama de Navegação. Nota: Existe também a cl usula Option Distinct do For Each que permite retornar os registros que cumpram unicidade de valores dos atributos referenciados. Não veremos esta cláusula. O leitor interessado pode recorrer as distintas fontes de documentação para estudá-la (Help, Wiki, Release Notes, etc.) Defined by defined_attributes::= att1, att2,…,attp É um conjunto de atributos que serão utilizados somente efeitos de determinar a tabela base do For each. Ao mencionar aqui alguns atributos da tabela que se deseja percorrer, estes participarão na determinação da tabela base do For each. A cláusula defined by aparece para solucionar alguns problemas de ambigüidade na determinação da tabela base (quando existem várias tabelas estendidas mínimas que contenham os atributos do For each) ou quando se deseja que a tabela base seja outra, diferente da que seria determinada pelos atributos que aparecem no resto do For each (este caso tem sentido quando se estude “controle de corte”). Também se utiliza para melhorar o tempo de especificação em procedimentos complexos. Os atributos desta cláusula não determinam por si só a tabela base do For each. Poderia acontecer que estes atributos determinam tabela como a candidata a tabela base, mas se depois os outros atributos do For each não estão contido na estendida dessa tabela, o For each dará um error e o objeto que o contem não poderá ser gerado. code1 É uma sucessão de comandos que podem utilizar atributos da tabela estendida do For each. A este bloque de código o chamaremos corpo do For each. Os atributos que figurem neste bloque de código participam na determinação da tabela base do For each. _________________________________________________________________________________________________ 1 O objeto DataSelector será visto mais adiante no curso.
  27. 27. Os comandos especificados serão executados seqüencialmente para os dados da tabela estendida que cumpram as Condições de filtro, considerando os dados na ordem especificado. Blocking Este tema será abordado mais adiante do curso, mas a idéia geral é que a especificação desta clásula permite realizar atualizações e eliminações em blocos, reduzindo assim o número de acesso a base de dados. When Duplicate Esta cláusula tem sentido somente em procedimentos (já que trata de atualização) e será visto mais adiante. Esta cláusula é executada se dentro do corpo do For each code1, atualizar um atributo que é chave candidata (possui índice único) e já existir um registro com esse valor. GeneXus utiliza o índice único para assegurar a unicidade dessa chave candidata e caso encontre duplicação, se o For each tem essa cláusula programada, executará seu código: code2. Não existindo a cláusula nenhum código será executado. When none Em caso de que não existam dados que cumpram as Condições de filtro não será executado os comandos do code1 e sim os bloque de código code3. Tanto para When Duplicate como para When none: incluindo um comando For each dentro dentro de um dos dois, não são inferidos nem joins nem filtros com respeito ao For each que o contêm (when none|when duplicate). São considerados navegações independentes (code1, code2 e code3).
  28. 28. Os code snippets são moldes de c digo que GeneXus tem predefinido, que nos ajudam a escrever o c digo fonte. Quando estamos trabalhando no Source de um procedimento, a Toolbox nos mostra vários snippets que nos facilitam a escrita do comando For each. Os snippets possui por sua vez um atalho (shorcut), que fazem que a escrita do c digo seja mais r pida todav a. Por exemplo, digitando simplesmente fe se escreve autom icamente o seguinte c digo: For each /*For each Code*/ Endfor Depois o usuário substitui a linha de comentários com o c digo necessário. A lista dos atalhos de cada snippet, é a seguinte: • fe (For each) • feu (For each using) • fen (For each When none) • feun (For each using When none) • few (For each where) • feuw (For each using where) • fewn (For each where When none) • feuwn (For each using where When none)
  29. 29. O For each é um comando como outros, e portanto pode aparecer várias vezes dentro do Source, tanto em forma paralela (independente), como aninhado a outro For each. Quando dentro do corpo do For each (code1) aparece outro For each, dizemos que trata-se de For eachs aninhados. GeneXus suporta vários níveis de aninhamento para os For eachs. Se um For each aparece no bloque de código code3, pode ser visto como For eachs aninhados porque um aparece dentro de outro, muda o comportamento, é igual a ter For eachs paralelos. Um For each “aninhado no when none” de outro, somente é executado se não tem nenhum registro da tabela base do For each que o contêm que cumpra as condições de filtro. No exemplo, “invoices” e “bill” são dois Printblocks do Layout que contêm os atributos das tabelas INVOICE e BILL (recibo) respectivamente. Temos definido dois For eachs paralelos. O primeiro percorre todas as faturas e o segundo todos os recibos.
  30. 30. O For each é uma estrutura repetitiva, que permite recuperar muitos registros de uma tabela. Quando pensamos em For eachs aninhados, é evidente que o que buscamos recuperar é, para cada registro do principal,muitos registros do aninhado. O que o GeneXus detecta que se quer fazer com este tipo de estruturas, de forma de inferir o comportamento automaticamente com o menor codificação possível? • para cada registro de uma tabela recuperar alguns de outra: os relacionados. • para cada registro de uma tabela recuperar todos de outra. • processar informação por grupos, isto é, agrupar os registros de uma tabela segundo o valor de um atributo ou conjunto de atributos e para cada grupo, recuperar alguns registros: os correspondentes ao grupo. Sempre a relação é um a muitos: para cada registro de uma tabela recuperar muitos da outra (podendo- se tratar da mesma tabela). Utilizando esta lógica é que o GeneXus infere as tabelas base e o comportamento dos For eachs aninhados, sabendo que o que desejamos é implementar alguma das três opções anteriores. Por exemplo, se queremos elaborar uma lista de todas as faturas do sistema, sendo que imprimiremos detalhe de cada uma, devemos navegar por duas tabelas: INVOICE (que armazena os cabeçalhos) e INVOICEDETAIL (que armazena as linhas), e o faremos de uma forma bem simples, sem colocar o nome das tabelas, e sem que explicar tudo, como veremos. Outro Exemplo é de uma lista de todos os clientes, onde para cada um se quer imprimir, além de seus dados pessoais, os dados de todas suas faturas. Este comportamento se dá com um par de For eachs aninhados, onde o primeiro navega pela tabela CUSTOMER e o segundo navega pela tabela INVOICE, recuperando somente as faturas desse cliente, como veremos em seguida.
  31. 31. Se queremos realizar uma lista de todas as faturas do sistema, onde para cada uma se mostre tanto a informação do cabeçalho como das linhas. No Source programado, percorremos à tabela INVOICE, imprimindo os atributos que nos interessam do cabeçalho e para cada registro dessa tabela, percorremos a tabela INVOICEDETAIL, para imprimir os atributos que nos interessam de suas linhas. Podemos perceber o quão simples é esse processo, simplesmente utilizando os atributos que queremos usar, ficando o GeneXus encarregado do resto. Observemos que nem sequer tivemos que especificar a condição de filtro sobre os registros de INVOICEDETAIL a recuperar. O GeneXus se dá conta da relação existente entre as tabelas, e aplica automaticamente a condição de filtro sobre os dados, de maneira que só sejam impressas as linhas “dessa” fatura (lembrar que algo idêntico acontece com as fórmulas verticais, como InvoiceAmount, onde a condição de filtro sobre os registros a serem somados ou contados ficava implícita, e não precisa que especificá-la).
  32. 32. Como para um For each simples, o GeneXus deve determinar para cada For each (principal e aninhado) qual é sua tabela base. E a partir dessa determinação, utilizará a lógica correspondente. Mais adiante veremos com exatidão como é que o GeneXus determina cada tabela base. Aqui ficaremos com idéia intuitiva de que o faz de forma similar como o fazia no caso de um For each simples. Encontra, pois, que deve percorrer as tabelas INVOICE e INVOICEDETAIL. Como essas tabelas estão relacionadas de acordo a uma relação 1-N infere mais que isso: infere também na aplicação da condição sobre os dados de INVOICEDETAIL que indicamos acima.
  33. 33. Quando temos dois For eachs aninhados,GeneXus deve determinar a tabela base de cada um, e essas serão as tabelas que se navegarão. Para cada registro da tabela base do For each principal, serão executados os comandos do corpo do mesmo. Entre esses comandos, encontra-se o For each interno, que será executado, como qualquer outro comando, no lugar onde estiver realizando uma navegação sobre sua tabela base. Para a determinação da tabela base do For each aninhado, influencia a tabela base do For each principal, mas as determinações não são por completo independentes, como se pode pensar equivocadamente. A partir da determinação das tabelas base de cada For each e das relações que encontre o GeneXus entre as tabelas envolvidas, surgem três possíveis casos de For eachs aninhados, que correspondem aos casos enunciados anteriormente: join, Produto cartesiano e corte de controle, respectivamente. Estudaremos cada um desses casos com exemplos.
  34. 34. Consideremos o caso mais simples, de um par de For eachs aninhados. • A determinação da tabela base do For each principal, é análoga ao caso de For each simples (sem aninhamentos). Neste caso consideram-se todos os atributos do For each principal, descartando os For eachs aninhados que este contenha (e todos seus atributos). GeneXus encontra a mínima tabela estendida que os contenha e define assim a tabela base através da qual chega a todas as outras. • Para determinar da tabela base do for each aninhado, GeneXus fixa os atributos utilizados dentro do corpo do mesmo, onde estão incluídos ou dentro da tabela estendida previamente determinada (a do For each principal). Em caso afirmativo, GeneXus determina que a tabela base do For each aninhado será a mesma que a do For each principal. Em caso contrário, busca-se a mínima tabela estendida que cumpra e contenha todos os atributos do For each aninhado e que tenha alguma relação com a tabela base do For each principal. A tabela base de dita tabela estendida, será a tabela base do For each. Se não puder encontrar uma tabela estendida mínima que cumpra ambas condições, mas cumpre que contenha os atributos, finalmente a escolhe, mas sempre busca a forma de encontrar relações entre ambas tabelas bases. Somente neste último caso de não se encontrar relações, se procede a determinar a tabela base como se fosse For eachs independentes. Estudaremos um esquema resumindo o anterior depois.
  35. 35. Para o exemplo apresentado anteriormente, mostramos acima como se determina cada tabela base. Para a do aninhado, como os atributos que figuram não estejam contidos na tabela estendida de INVOICE (que é a tabela base do principal), então se passa a determinar sua tabela base como se explicou anteriormente: se busca a tabela estendida mínima que contenha a ProductDescription, ProductPrice, InvoiceLineQuantity e InvoiceLineAmount, e caso possível esteja reacionada com INVOICE. A tabela que cumpre ambos requisitos em INVOICEDETAIL.
  36. 36. Da determinação das tabelas base, surgem os três casos de For eachs aninhados que foram mencionados e que estudaremos um a um em seguida. Este tema é de vital importância, já que a maioria das aplicações requerem navegações complexas sobre as tabelas, onde se requer uma mistura de todos estes casos.
  37. 37. Este é o caso em que o GeneXus determina que as tabelas base de cada For each são distintas e tem uma espécie de relação 1-N (podendo ser esta indireta) entre as tabelas que se percorre Ou seja, para cada registro da tabela base do For each principal, GeneXus encontra que tem N relacionados com ele, direta ou indiretamente, na tabela base do For each aninhado. Ao encontrar esta relação, aplicará condições de filtro automáticas no For each aninhado, de forma a ficar somente com esses registros relacionados. O exemplo do procedimento que imprime todas as faturas do sistema, com seus detalhes, cai dentro desta categoria. Nesse caso tem uma relação 1-N direta entre as tabelas que se percorre: para cada cabeçalho da fatura, lista-se o mesmo, junto com todas suas linhas da fatura, ou seja, todos os registros de INVOICEDETAIL que estão relacionados com o registro de INVOICE que se está posicionado em cada interação. Este é um dos casos mais comuns de For eachs aninhados, onde se quer percorrer uma tabela, e para cada registro da mesma, percorrer outra tabela, relacionada com a primeira por uma relação N-1. O GeneXus encontra nesta relação, e na navegação interna, somente recupera os registros associados, e aí o nome “join” para este caso. No exemplo apresentado acima ocorre o mesmo. A tabela base do primeiro For each é CUSTOMER, e a do segundo, INVOICE. Como encontra atributo em comum entre a tabela estendida do For each principal e a tabela base do aninhado1, CustomerId, determina que esse atributo atue como condição de filtro na percorrida da tabela do For each aninhado. ------------------------------------------------------------------------------------------------------------ 1 Esta é uma forma de expressar formalmente o que havíamos dito em términos informais: relação direta ou indireta 1-N entre as tabelas base. A relação será direta quando a tabela base do principal tenha relação 1-N com a tabela base do aninhado, isto é, seja superordinada desta última. A relação será indireta quando isto não exista uma relação direta entre as tabelas base, mas sim entre a tabela estendida do primeiro e a tabela base do segundo. Também será indireta quando a tabela estendida do aninhado inclua a tabela base do principal. Veremos exemplos em seguida.
  38. 38. Vejamos, como o GeneXus faz para determinar as tabelas base. Os atributos utilizados no For each externo são CustomerId e CustomerName, e a tabela base deste For each será CUSTOMER. Observemos que somente participam na determinação desta tabela base os atributos do For each principal,não os do aninhado. Depois, GeneXus deve encontrar a tabela base do For each aninhado. Os atributos que participam são InvoiceId, InvoiceDate e InvoiceAmount, ou seja, os atributos internos a este For each. Observemos que estes atributos não pertencem a tabela estendida do principal, que era CUSTOMER. Portanto passa a determinar sua tabela base como a de qualquer For each simples: a tabela estendida INVOICE contem todos os atributos do For each aninhado, e é a mínima tabela estendida que os contenha. Portanto, INVOICE será escolhida como tabela base do segundo For each. Observemos, novamente, que não explicitamos cláusula where no For each aninhado para filtrar as faturas do cliente do For each principal. Justamente, por tratar-se de tabelas relacionadas por uma relação 1-N direta, esta condição é aplicada implicitamente pelo GeneXus e pode ser visto na listagem de navegação acima. Outro fato interessante que pode ser observado nesta lista: no For each aninhado não foi especificado a cláusula order, GeneXus não escolheu a ordem da chave primária da tabela base e sim pelo atributo da relação, CustomerId. Desta maneira, está otimizando automaticamente a consulta. Esse caso é uma exceção a regra que fala que quando nenhuma cláusula order em um For each é especificada, GeneXus determina como ordem o atributo da chave primária da tabela base de dito For each.
  39. 39. Se queremos realizar uma listagem das faturas emitidas pelo país, teremos outro caso de For eachs aninhados com distintas tabelas base, onde a informação que queremos listar está relacionada. Aqui queremos percorrer às tabelas COUNTRY e INVOICE. Observemos que elas não estão relacionadas diretamente, mas estão de forma indireta. De fato, COUNTRY pertencem à tabela estendida de INVOICE. Portanto, para cada fatura pode-se encontrar somente um país relacionado à mesma. Este é um caso um pouco mais complexo que o anterior, porque a tabela estendida do For each principal não tem intersecção com a tabela base do aninhado (est(COUNTRY) ∩ INVOICE = φ), mas existe uma relação 1-N indireta, e o GeneXus a encontra. Neste caso, a tabela base do For each principal está incluída na estendida do aninhado (COUNTRY ⊂ est(INVOICE)), mas tem uma relação 1-N indireta. Por este motivo, não necessitamos especificar cláusula where no For each interno para filtrar as faturas do país do For each principal. Isto pode ser visto claramente na listagem de navegação, que mostrará o filtro: “CountryId = CountryId” para o segundo For each, mas desta vez como ‘Constraint’ visto que não pode otimizar a percorrida. O leitor pode testar este caso em GeneXus e estudar detalhadamente a listagem de navegação resultante.
  40. 40. Neste caso o GeneXus não encontra uma relação 1-N direta ou indireta entre as tabelas e portanto não aplica filtros implícitos aos registros do For each aninhado, ou seja, realiza um produto cartesiano entre as tabelas. O caso ocorre quando: • est(For each principal) ∩ base(For each aninhado) = φ e • base(For each principal) ⊄ est(For each aninhado) Para cada registro da tabela base do For each principal se recorre toda a tabela base do For each aninhado. Por exemplo, se a tabela base de um For each foi COUNTRY e a do aninhado PRODUCT, evidentemente não existirá relação e haverá um produto cartesiano e se percorre para cada país, todos os produtos. O programador pode estabelecer filtros sobre os dados a recuperar, mas estes já não serão condições implícitas inferidas pelo GeneXus, mas sim especificadas explicitamente pelo programador. .
  41. 41. No exemplo que vimos anteriormente, da listagem de clientes e suas faturas, o que acontece se um cliente não possuir faturas? Como a tabela base do For each principal é CUSTOMER, o cliente sai impresso antes de saber-se se tem ou não faturas. Se não desejamos que isto ocorra, isto é, que apareçam listados clientes que não tenham faturas, então a solução é acessar unicamente as faturas, pois se um cliente está nesta tabela, é porque está em uma fatura!. Mas para poder agrupar as faturas por cliente, de tal forma de poder mostrá-las desse modo, devemos percorrer a tabela INVOICE ordenada por CustomerId. Desta forma processaremos a informação de um cliente, e depois passaremos ao seguinte, para processar sua informação, e assim sucessivamente. Se imaginamos um ponteiro que se vai mostrando sequencialmente pela tabela INVOICE, podemos escrever o pseudocódigo de nosso procedimento como segue: 1. Para o registro apontado, reter o valor do atributo de corte ou agrupamento, CustomerId. 2. Acessar a tabela CUSTOMER (que está na estendida de INVOICE) para recuperar o CustomerName e imprimi-lo junto com o CustomerId (“print customer”) 3. Enquanto o valor de CustomerId do registro apontado coincida com o valor retido no passo 1 (aqui se processam todas as faturas do cliente) a. Imprimir InvoiceId, InvoiceDate e InvoiceAmount do registro apontado. (“print invoice”) b. Avançar o ponteiro ao seguinte registro e voltar ao passo 3. 4. Voltar ao passo 1. (quando se chega a este ponto, é porque se chegou no final da tabela ou mudou o cliente).
  42. 42. GeneXus oferece uma forma de programar o que foi visto de uma forma simples. O pseudocódigo visto na página anterior se programa em GeneXus com um par de For eachs aninhados que estão no slide acima. Comparando este código com o que vimos anteriormente para o caso de join, vemos que existem somente duas diferenças: a cláusula order que aparece neste código, junto com o defined by. Somente com essas mudanças da lista original, mudamos radicalmente o comportamento, neste caso somente serão listados os clientes que possuem faturas. Neste caso, ambas cláusulas (order e defined by) são indispensáveis para que este relatório funcione como queremos. Se agregamos somente uma delas, o resultado é outro. Não é em toda programação de controle de corte que deverá ter uma cláusula defined by no For each principal,mas sim uma cláusula order. A cláusula order é indispensável, porque é ela que especifica por qual atributo ou conjunto de atributos o corte (ou agrupamento) será realizado. Isto é, especifica essa informação comum ao grupo, que será processada uma única vez (dentro do código do For each externo). A cláusula defined by não é indispensável em todos os casos. Neste caso foi, porque não especificá- la, GeneXus determinaria como tabela base do For each principal CUSTOMER, que não é o que queremos (pois não queremos implementar um join, como foi feito antes, e sim um corte de controle, para ler somente a tabela INVOICE). Poderíamos ter utilizar outra solução para modificar a tabela base do For each principal: utilizar em vez do defined by o comando print if detail dentro do corpo do primeiro For each (este comando diz ao GeneXus que tome como tabela base do For each, a mesma que determinar para o aninhado).
  43. 43. Um controle de corte é fácil de programar e pode ser feito seguindo as considerações anteriores. Na ordem do For each mais extremo, devemos mencionar o “primeiro” atributo de corte, na ordem do segundo For each devemos mencionar o “segundo” atributo de corte e assim sucessivamente. Não é obrigatório mencionar atributo/s na ordem do For each mais interno (em todos os demais For each é assim). É utilizado quando desejamos trabalhar com a informação de uma tabela, mas agrupada por algum atributo ou conjunto de atributos. Os controles de cortes podem ser simples, duplos, triplos, etc. A seguir veremos um exemplo de um controle de corte duplo.
  44. 44. Exemplo: Controle de corte duplo Vamos supor que queremos como antes listar os clientes e suas faturas, mas queremos agrupar as faturas de cada cliente por data. Isto é, queremos mostrar, para cada cliente, para cada data, as faturas existentes. Exemplo: Customer: 1 João Silveira Date: 12/05/05 Invoice Id Invoice Amount 1 15 Date: 01/01/06 Invoice Id Invoice Amount 9 35 3 30 Customer: 3 Maria Silva Date: 06/06/05 Invoice Id Invoice Amount 2 20 Date: 12/08/05 Invoice Id Invoice Amount 4 40 8 15 Date: 02/02/06 Invoice Id Invoice Amount 7 20 … Como agora queremos agrupar por cliente, e dentro desse grupo por data de fatura, necessitamos três For eachs aninhados: For each order CustomerId defined by InvoiceDate print customer For each order InvoiceDate print date For each print invoice Endfor Endfor Endfor Como exercício, vamos seguir todos os passos que realiza GeneXus para inferir o comportamento do procedimento. 1. Determinação da tabela base de cada For each Como sempre, para determinar as tabelas base de For eachs aninhados, se começa de fora para dentro, determinando de cada For each, sem levar em consideração os atributos dos For eachs internos ao que se está considerando.
  45. 45. 2. Determinação da navegação Depois de determinadas as tabelas base, GeneXus determina a navegação. Como neste caso são três For eachs sobre a mesma tabela base, se trata de um controle de corte duplo. Podemos pensar que quando falamos de controle de corte, seja simples, duplo, triplo, quádruplo, etc, temos somente um ponteiro utilizado para avançar nos registros da tabela base. Lembremos que a cláusula order é fundamental para estabelecer o critério de corte em cada par de For eachs. Como em nosso caso queremos agrupar por CustomerId e depois, para todas as faturas com esse cliente, agrupar por InvoiceDate, então teremos que ordenar o primeiro For each por CustomerId e ou imediatamente aninhado por InvoiceDate. No exemplo estamos dizendo que: Enquanto não encontrar o final da tabela Imprimir os dados do cliente da fatura atual Enquanto não mude o cliente Imprimir a data da fatura atual Enquanto não mude a data Imprimir os dados da fatura atual (Id e Amount) Avançar o ponteiro ao seguinte registro !" # $ $ $ % " # " ! " "&!' "(! " # ' # " &" " &!) " " * + " # # "# & ", !" # - $ $ $ % " # " ! " "&!' "(! " # ' # " &" " &!) " " * + " # # "# & ", - $ $ $ . # " " " " ! " "&!' "(! " # ' * /) # & " (! " #, "0 1
  46. 46. Recomendamos programar em GeneXus este procedimento e observar cautelosamente a listagem de navegação. Verá que GeneXus escolhe uma única ordem, quando não tem um índice criado: o composto pela concatenação das ordens de cada For each com cláusula order. Este resultado é claro se pensarmos que um único ponteiro vai se deslocando pela tabela base. Uma vez determinado a Order do Controle de Corte se otimiza, buscando o melhor índice levando em consideração as condições explícitas ou implícitas do nível. Resumo: Determinação geral das tabelas Base Acontece de forma ordenada, determinando cada vez a tabela base de um nível aninhado, vendo de fora para dentro: primeiro determina-se a tabela base do For each mais externo, depois o que está aninhado a este e assim sucessivamente. Determinação da Tabela Base do For Each Externo Determina a partir dos atributos que aparecem dentro desse For each: cláusulas order, where, defined by e corpo do For each, exceto os atributos que estejam dentro do For each aninhado. Não participam os atributos que estão dentro do When none, no caso do For each principal ter esta cláusula. Como no caso de um For each simples, que se encontra a mínima tabela estendida que contenha ditos atributos. Determinação da Tabela Base do For Each Aninhado Podemos chegar a pensar por analogia que deveríamos extrair os atributos do For each aninhado, e fazer o mesmo que antes, ou seja, encontrar a mínima tabela estendida que contenha esses atributos, como se fosse um For each independente. Mas não são For eachs independentes! Para o For each aninhado, o GeneXus verifica se os atributos utilizados dentro do corpo do mesmo estão incluídos ou não dentro da tabela estendida previamente determinada. Em caso afirmativo, o GeneXus determina que a tabela base do For each aninhado será a mesma que a do For each principal ( e será um caso de controle de corte) Caso contrário, se determina da seguinte maneira: busca a mínima tabela estendida que contenha a todos os atributos do For each aninhado, tratando de encontrar aquela que tenha alguma relação com a tabela base do For each principal. Se não encontra uma tabela estendida que contenha a todos os atributos do For each aninhado e que além desta relacionada, fica com a tabela estendida mínima que contenha os atributos ainda que não tenha relação.
  47. 47. Os comandos introduzidos são similares aos existentes nas linguagens de Programação imperativa conhecidas, por isso não incluímos a documentação deste tema. Pode ser encontrada no curso não presencial ou no Help do GeneXus. Os dois últimos, incorporam alguns elementos importantes sobre administrar arrays e de coleções1 em GeneXus. Pelo qual mostraremos alguns exemplos: For to step: • inicio, fin são expressões num ricas • salto é uma constante num rica • var é alguma variável num rica • bloque é uma sucessão de comandos v lidos da linguagem Permite interagir certa quantidade de vezes: desde o valor início que toma a variável &var quando se ingressa ao loop, até o valor fim que toma a mesma quantidade de interações. De interação em interação a variável &var vai incrementando automaticamente numa quantidade igual a passo. O valor por default de salto é 1, senão especificarmos a cláusula step o incremento da variável será de um em um. O valor de salto pode ser negativo e nesse caso irá decrementando a variável de interação em interação. Exemplo For &i = 1 to 5 &ok = PInvoicing.udp( &month ) Endfor _____________________________________________________________________________ 1 As coleções representam listas de tamanho variável. Serão vistas quando estudarmos o tipo de dados estruturado (SDT).
  48. 48. For in Expression: • Expression é qualquer expressão cujo valor seja uma coleção, vetor ou matriz. • var é uma variável que deve ter o mesmo tipo de dados que os elementos de Expression. Esta estrutura de programação permite percorrer com menos c digo uma coleção ou uma variável vetor de uma ou mais dimensões. Se armazena na variável &var os valores de cada elemento da coleção, vetor ou matriz. Para o caso de vetores de uma dimensão: for &var in &varArray() … // code Endfor o c digo se expande (é equivalente) a: &x = 1 do while &x <= rows(&array()) &var = &Array(&x) bloque &x += 1 enddo No caso de duas dimensões: for &var in &varMatrix() … // code Endfor o comando se expande (é equivalente) a: &x = 1 do while &x <= rows( &array() ) &e = 1 do while &e <= cols( &array() ) &var = &array( &x, &e ) bloque &e += 1 enddo &x += 1 enddo Também pode ser uma variável coleção de qualquer tipo, incluindo uma variável com a propriedade Collection em 'False'mas de um tipo de dados 'SDT collection'. for &var in &collection ... endfor A expressão também pode não ser uma variável, por exemplo no caso do resultado de um DataProvider ou de um Procedure: for &var in DataProvider(parms) ... Endfor for &var in Procedure(parms) ... endfor Considerações: • Não é possível modificar os valores da coleção, vetor ou matriz na percorrida. Isto significa que alterações no valor de &var no alcance da estrutura, não afetam ao correspondente valor da &collection o do &array(&x), ou de &array(&x, &e)). • Não é possível obter a posição da coleção/array/matriz durante a percorrida, para isto é necessário definir uma variável que at e como contador. •Estas estruturas podem aninhar para percorrer vários arrays, matrizes ou coleções. Isto inclui o caso de que se chame uma Subrotina que tamb m possui um For In Expression. •Igual que em um For Each ou um Do While, é possível incluir comando que corte a percorrida, como Exit ou Return.
  49. 49. Aqui veremos alguns comandos de impressão, que permitem desenhar a saída do procedimento. Print Onde nomePrintBlock é o identificador de um Printblock do Layout. Não existindo no Layout um Printblock com esse nome, ao tentar salvar o objeto mostra-se uma mensagem de error informando sobre esta situação. Desta forma implementa-se a impressão na saída dos Printblocks do Layout. Quando o Printblock que se quer imprimir não contêm atributos, então este comando pode ser utilizado em qualquer lugar do Source. Os atributos indicam acesso a base de dados e este acesso não pode ser realizado em qualquer lugar, somente dentro do comando específico para ele, isto é, o comando For each. Portanto, não é correto escrever o comando print fora de um “For each” se o Printblock que desejamos imprimir contêm atributos. A nica exceção desta regra se produz quando os atributos do Printblock est incluídos entre os parâmetros recebidos pelo procedimento, pois neste caso não é necessário acessar a base de dados para recuperar seus valores, visto que já vem instanciados. Header Aqu se define o que se quer imprimir como cabeçalho de cada p gina da listagem. Este cabeçalho é opcional. Se não for especificada, então as p ginas da listagem não terão cabeçalho. Exemplo: No procedimento que quer amos imprimir uma listagem com o c digo, nome e pa s de cada um dos clientes de nosso sistema, se queremos que em cada p gina da listagem apareça o cabeçalho: CUSTOMERS REPORT , então é suficiente escrever no Source: Header Print title End Onde title é o nome de um Printblock do Layout que contem este texto.
  50. 50. Também poder amos ter escrito diretamente: Print title no início do Source, mas neste caso, se a listagem tem várias p ginas, somente sairá impresso este texto na primeira. No outro caso, sairá em cada uma das p ginas, como cabeçalho. Footer Define as linhas no rodapé da página a ser impressas ao final de cada página do procedimento. Os comandos do bloque de código são executados quando se chega ao final de uma página. Exemplo: Footer print endOfPageText end onde endOfPageText é o nome de um Printblock do Layout
  51. 51. Desenho da saída Existem alguns comandos para desenhar a saída do procedimento. Apresentamos aqui alguns efeitos da documentação. MT nlines: nlines é o número de linha em que se quer começar a imprimir a listagem. Caso não seja especificado um valor se assume o valor por default que é 0. MB nlines: nlines é o número de linhas que se deseja deixar como margem inferior. Caso não seja especificado um valor se assume o valor por default que é 6. PL nlines: Seta o tamanho de página. O número de linhas que será impresso é o número especificado menos a margem de baixo (valor por default é 6). Ex: PL 66 Seta o tamanho da página a 66 linhas, ainda que somente 60 linhas sejam impressas no form, com uma margem inferior de 6 linhas. CP nlines: Se quiser na página atual um número de linhas maior ou igual ao número especificado, continua imprimindo na mesma página. Do contrario, passa a imprimir na próxima página (força um salto de página). Lineno nlines: Define o número de linha onde vai ser impressa a seguinte linha. Se o número de linha atual é maior ao número especificado, então, a linha será impressa na próxima página. O de linhas começa na linha 0. Eject: Força a um salto de página. Noskip: Tem que estar imediatamente depois de um Printblock. Se o comando se encontra entre duas linhas, este comando as imprimirá na mesma linha.
  52. 52. Em Web as listagens somente podem ser PDF e devem ser configuradas as propriedades e regras anteriores para que funcionem. Definição de um objeto como main Ao definir que um objeto é main (neste caso um procedimento, mas poderia ser uma transação, web panel, etc.), GeneXus gera um programa executável com a lógica do objeto mesmo e a de todos os objetos chamados direta ou indiretamente por ele. O programa executável gerado pode compilar e executar de forma independente, isto é, ao selecionar Build / Run se verá como programa independente do Developer Menu e poderá compilar-se e executar-se. A definição de um objeto como main se realiza editando as propriedades do objeto, e configurando a propriedade Main program do mesmo com valor True.
  53. 53. Nesta seção se permite estabelecer condições que devem cumprir os dados para ser recuperados. Uma “condição” é equivalente a cláusula “where” do comando for each (tem a mesma sintaxe) com uma diferença: enquanto a cláusula “where” está ligada à um For each específico - aquele ao que pertence -, as “condições” estão ligadas à todos os For each do Source que tenha sentido aplicá-las. E para que For eachs tem sentido aplicá-las? As Condições geralmente envolvem atributos. Se na tabela estendida de um For each encontra-se algum dos atributos que interferem numa “condição”, então a mesma pode ser aplicada à este For each para filtrar os dados ficando unicamente com aqueles que satisfaçam tal condição. Na listagem de navegação do procedimento indicam-se os For eachs que aplicam-se a cada condição das especificadas na seção “Conditions”. Se no Source do procedimento “PrintCustomers” temos: For each Print customer Endfor Então ao especificar as condições que se mostram no slide, estaremos filtrando os clientes, de acordo com as condições (é equivalente ter as condições como “where”).
  54. 54. onde: • “customer” é um Printblock que contem os atributos CustomerId, CustomerName, CountryName • “invoice” é um Printblock que contem os atributos InvoiceId, CustomerId, CustomerName, InvoiceDate, InvoiceAmount • “product” é um Printblock que contem os atributos ProductId, ProductDescription, ProductStock Se o procedimento anterior tem definido as condições mostradas acima, o procedimento será equivalente a um que não tiver “condições” e com o Source que se mostra a direita. Observemos que neste caso as condições se traduzem em cláusulas where mas que se aplicam somente aos For eachs para os que tem sentido aplicá-las. Na tabela estendida do último For each não se trabalha com nome de cliente, nem com identificador de fatura. Não tem sentido aplicar as condições neste For each já que não existe nenhuma relação. No primeiro, somente tem sentido aplicar se utilizar CustomerName e não a outra. Observação Os atributos envolvidos nas “condições” não participam na determinação das tabelas base dos For eachs do Source (a diferença dos filtros que se especificam mediante cláusulas where). Isto é, as tabelas base dos For eachs que apareçam no Source se determinam sem olhar as “condições”. Uma vez determinadas, nesse momento as condições são examinadas para determinar a quais For eachs se aplicam e a quais não. O mesmo ocorre com os atributos recebido como parâmetro. Aplicam como filtro global por igualdade para os For eachs que tenha sentido, mas não participam na determinação das tabelas base.
  55. 55. Resumimos aqui as distintas formas de filtrar em um procedimento a informação da base de dados a ser recuperada: a. cláusulas where b. condições c. parm( att, ..., att ) Estudemos as diferenças e similaridades entre elas. 1. As cláusulas where aplicam exclusivamente ao For each em que se encontram, enquanto que os filtros especificados como condições ou os que ficam determinados pelos atributos na regra parm são globais, isto é, aplicam a todos os For eachs do Source em que faça sentido aplicá-las. 2. Os filtros que ficam determinados ao receber atributos na regra parm são filtros por igualdade, isto é, para os For eachs que tenha sentido aplicá-los, se instanciarão unicamente os registros que tenham o mesmo valor que o recebido por parâmetro. Por outro lado, os filtros especificados nas cláusulas where de um For each ou nas condições podem ser expressões booleanas quaisquer, incluso compostas. 3. Enquanto que os atributos que aparecem nas cláusulas where participam na determinação da tabela base do For each onde se encontram, os que aparecem nas condições ou na regra parm não o fazem. São aplicados ASSIM que determinadas as tabelas base dos For eachs do Source.

×