Translate

sábado, 17 de dezembro de 2011

Java NIO - Acesso randômico a arquivo

O Java NIO é uma das melhorias mais significantes para manipulação de I/O. A sigla NIO é de New I/O, ou seja, renovação do pacote java.io.* em relação ao acesso a recursos de memórias externas como um arquivo ou um cliente remoto (host). O pacote NIO está aninhado em java.nio.*.

Nota: I/O é o termo utilizado para denominar a troca de dados de uma área para outra em computação. Esta operação pode ser a comunicação ministrada por uma CPU para transitar dados da sua RAM para um HD, CD R/W, DVD R/W, pasta de rede, um PenDrive (flash), outro computador ou banco de dados. Numa linguagem de programação, este acesso é classificado de alto risco, pois quando os dados saem ou chegam de um recurso externo, muitas coisas podem ocorrer como o desligamento deste recurso, queda do meio físico da comunicação, falha no sistema lógico etc.

Durante minha experiência com Java NIO, me despertou atenção a sua capacidade de acesso não bloqueante. Isso mesmo, você escreve os dados em um recurso I/O como é um arquivo e pode programar para o gerenciador da escrita não deixar em espera sua linha de execução (thread). A mesma espera não é necessária se os dados enviados pelo recurso ainda não estiverem prontos.

O acesso não bloqueante torna possível a terceirização do controle de múltiplos I/O para o Java NIO sem requerer de você a criação de múltiplas threads (linhas de execução paralelas) para gerenciar este acesso. Entretanto, esta terceirização requer cuidados e domínio do tipo de recurso I/O referentes as cerimônias para abertura da comunicação, transferência de dados e encerramento da comunicação. O Java NIO não abstrai estes controles e caberá a você entender bem como funciona o tipo de recurso a operar.

Segue um exemplo de acesso randômico a arquivo para escrita (criação automática quando não existe) e posterior leitura. É necessário ter uma pasta "c:\temp" em seu PC ou mude este caminho no código:

import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.RandomAccessFile;
import java.nio.MappedByteBuffer;
import java.nio.channels.FileChannel;


public class TesteFileChannel {


    public static void main(String[] args) {

        TesteFileChannel tfc = new TesteFileChannel();
       
        tfc.writeText();
       
        tfc.readText();
       
       
    }
   
    public void writeText() {

        String text = null;
        FileChannel rwChannel = null;
        MappedByteBuffer rwBuffer = null;

        try {

            // Texto para guardar
            text = "Olá mundo";

            // Abre canal de comunicação
            rwChannel = new RandomAccessFile("c:/temp/datafile.txt", "rw").getChannel();

            // Aloca uma fatia bytes para guardar o texto a partir do canal do arquivo
            rwBuffer = rwChannel.map(FileChannel.MapMode.READ_WRITE, 0, text.length());

            // Coloca o ponteiro na posição ZERO da memória mapeada
            rwBuffer.position(0);

            // Escreve os bytes do texto no arquivo
            rwBuffer.put(text.getBytes(), 0, text.getBytes().length);

            // Como o acesso é não bloqueante, neste caso é forçada
            // a efetivação escrita caso esteja pendente
            rwChannel.force(true);

            // Encerra canal de comunicação
            rwChannel.close();

            // Destrói o objeto representante da memória alocada 
            rwBuffer.clear();

            System.out.println("Concluída a escrita com sucesso: " + text);

        } catch (FileNotFoundException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        }

    }

    public void readText() {

        byte[] bytes = null;
        String text = null;
        FileChannel rwChannel = null;
        MappedByteBuffer rwBuffer = null;

        try {

            // Abre canal de comunicação
            rwChannel = new RandomAccessFile("c:/temp/datafile.txt", "rw").getChannel();

            // Aloca uma fatia bytes para guardar o texto a partir do canal do arquivo
            rwBuffer = rwChannel.map(FileChannel.MapMode.READ_WRITE, 0, rwChannel.size());

            // Coloca o ponteiro na posição ZERO da memória mapeada
            rwBuffer.position(0);

            // Vetor para leitura
            bytes = new byte[(int) rwChannel.size()];

            // Lê os bytes do texto no arquivo
            rwBuffer.get(bytes, 0, bytes.length);

            // Converte bytes para texto
            text = new String(bytes);

            // Encerra canal de comunicação
            rwChannel.close();

            // Destrói o objeto representante da memória alocada 
            rwBuffer.clear();

            System.out.println("Concluída a leitura com sucesso: " + text);
           

        } catch (FileNotFoundException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        }

    }

}

domingo, 11 de dezembro de 2011

Java - Vetor simples

A classe a seguir pode ser escrita em Java para testar a criação de vetores por três formas. O tipo utilizado é int, mas você pode adaptar para qualquer outro tipo simples ou complexos. Os objetos complexos quando criados, podem exigir um pouco mais de domínio para você destruí-los da memória, enquanto os simples são destruídos automaticamente.

public class TesteVetor {

    public static void main(String[] args) {

        TesteVetor t = new TesteVetor();

        System.out.println("Iniciar teste A:\n");
       
        t.testaVetorA();

        System.out.println("\nIniciar teste B:\n");
       
        t.testaVetorB();

        System.out.println("\nIniciar teste C:\n");
       
        t.testaVetorC();
       
    }

    public void testaVetorA() {

        // Declara vetor e aloca 10 vezes um espaço de int na memória
        int [] vetorA = new int[10];

        // Preencher cada endeço do vetor (inicia com 0)
        for (int endereco = 0; endereco < vetorA.length; endereco++) {
            vetorA[endereco] = (endereco + 1);
        }

        // Exibe os dados do vetor A
        for (int endereco = 0; endereco < vetorA.length; endereco++) {
            System.out.println("Vetor A -> Endereço: " + endereco + " - Valor: " + vetorA[endereco]);
        }

    }

    public void testaVetorB() {

        // Declara vetor, faz alocação e preenchimento de uma só vez
        int [] vetorB = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10};

        // Preencher cada endeço do vetor (inicia com 0)
        for (int endereco = 0; endereco < vetorB.length; endereco++) {
            vetorB[endereco] = (endereco + 1);
        }

        // Exibe os dados do vetor B
        for (int endereco = 0; endereco < vetorB.length; endereco++) {
            System.out.println("Vetor B -> Endereço: " + endereco + " - Valor: " + vetorB[endereco]);
        }
               
    }

    public void testaVetorC() {

        // Declara vetor B, faz alocação e preenchimento de uma só vez
        int [] vetorB = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10};


        // Declara vetor C e cria conteúdo a partir da clonagem do vetor B
       
int [] vetorC = vetorB.clone();

        // Exibe os dados do vetor B
        for (int endereco = 0; endereco < vetorC.length; endereco++) {
            System.out.println("Vetor C -> Endereço: " + endereco + " - Valor: " + vetorC[endereco]);
        }
               
    }

}

Oracle - Cruzando dados com SQL

Uma das utilidades principais do SQL é sua capacidade de cruzar grandes volumes de dados de duas ou mais fontes. Sem o SQL, este trabalho seria árduo para analistas e desenvolvedores porque teriam preocupações como realizar varreduras registro-por-registro.

Como exemplo, vamos usar duas tabelas consideradas como fontes de dados:

       GRUPO_A - Com a coluna chamada ELEMENTO do tipo numérico
       GRUPO_B - Também com uma coluna chamada ELEMENTO do tipo numérico

A partir destas tabelas, é possível realizar os seguintes relacionamentos:

1) Procurar elementos comuns as tabelas GRUPO_A e GRUPO_B:

       SELECT
         GRUPO_A.ELEMENTO
       FROM
         GRUPO_A
         , GRUPO_B
       WHERE
         GRUPO_A.ELEMENTO = GRUPO_B.ELEMENTO

       Nota: Os registros comuns aos grupos A e B são buscados pela cláusula WHERE com o uso do operador de igualdade "=".

2) Procurar elementos que existem apenas no GRUPO_A:

       SELECT
         GRUPO_A.ELEMENTO
       FROM
         GRUPO_A
         , GRUPO_B
       WHERE
         GRUPO_A.ELEMENTO = GRUPO_B.ELEMENTO (+)
         AND GRUPO_B.ELEMENTO IS NULL

       Nota: Os registros apenas existentes no grupo A são buscados pela cláusula WHERE com o uso do operador de igualdade "=", com "(+)" para trazer tudo de A e de B apenas quando encontrar e o filtro "IS NULL" aplicado na coluna elemento de B para mostrar tudo de A sem correspondente em B.

3) Procurar elementos que existem apenas no GRUPO_B:

       SELECT
         GRUPO_B.ELEMENTO
       FROM
         GRUPO_B
         , GRUPO_A
       WHERE
         GRUPO_B.ELEMENTO = GRUPO_A.ELEMENTO (+)
         AND GRUPO_A.ELEMENTO IS NULL

       Nota: Os registros apenas existentes no grupo B são buscados pela cláusula WHERE com o uso do operador de igualdade "="; mais o uso de "(+)" para trazer tudo de B e de A apenas quando encontrar; e o filtro "IS NULL" aplicado na coluna elemento de A para mostrar tudo de B sem correspondente em A.

Existem outras alternativas para relacionar dados com o SQL, mas todas estas requerem um pouco mais de escrita. Caso tenha curiosidade, procure saber mais sobre INNER JOIN, LEFT JOIN e RIGHT JOIN, EXISTS e NOT EXISTS como palavras do SQL.

sexta-feira, 9 de dezembro de 2011

PL/SQL - Comando simples

Para executar qualquer comando PL/SQL é preciso no mínimo iniciar com um comando BEGIN e finalizar com um END. Exemplo:

       BEGIN
         -- Call function
       END;

Se você fizer uma chamada a uma procedure, siga a mesma instrução:

       BEGIN
         PRC_EXEC();
       END;

SQL - Identificar registros duplicados

Para identificar cadastros duplicados, é necessário saber em quais atributos se basear para definir uma chave de exclusividade do registro.

No exemplo a seguir, o cadastro é uma tabela de empregados. Há o pressuposto de que não há unique ou primary key definidas para a tabela, desta maneira, é possível um empregado ter mais de um cadastro na tabela EMPREGADOS.

A query a seguir mostra  os empregados repetidos pelo CPF e número de repetições superiores a um.

       SELECT
         E.CPF, COUNT(1) NUM_CADASTROS
       FROM 
         EMPREGADOS E
       GROUP BY
         E.CPF
       HAVING COUNT(1) > 1

A cláusula HAVING restringe o agrupamento de E.CPF a ocorrências maior do que um.

quarta-feira, 7 de dezembro de 2011

Procurar SQL pobres

Nem sempre é possível manter o banco de dados livre da má utilização de seus recursos por parte de programas como aplicações externas, usuários diretos ou rotinas particulares agendadas para execução nativa no banco de dados. 

Esta condição de "nem sempre será possível" independe da qualidade dos profissionais usuários do banco de dados, por isso não é construtivo culpá-los e nem mesmo exigir de todos o domínio das boas práticas de banco de dados, todavia, é necessário envolvê-los num processo contínuo de capacitação.

As querys a seguir fazem alguns ranks importantes para apoiar a descoberta de SQL problemáticos sobre o banco de dados. Nem tudo o que aparecer no rank será um problema, por isso é necessário interpretar cada situação.

1) A query a seguir verifica os 10 comandos mais executados
 
SELECT
  SQL_TEXT
  , EXECUTIONS
FROM (
  SELECT
    SQL_TEXT
    , EXECUTIONS
    , DENSE_RANK() OVER (ORDER BY EXECUTIONS DESC) EXEC_RANK
  FROM V$SQL
)
WHERE EXEC_RANK <= 10

2) A query a seguir verifica os 10 comandos que mais fazem leituras em disco (I/O) 

SELECT SQL_TEXT, DISK_READS FROM (
  SELECT
  SQL_TEXT
  , DISK_READS
  , DENSE_RANK() OVER (ORDER BY DISK_READS DESC) DISK_READS_RANK
  FROM V$SQL
)
WHERE DISK_READS_RANK <= 10

3) A query a seguir verifica os 10 comandos que mais fazem leituras lógicas
 
SELECT SQL_TEXT, BUFFER_GETS FROM (
  SELECT
  SQL_TEXT
  , BUFFER_GETS
  , DENSE_RANK() OVER (ORDER BY BUFFER_GETS DESC) BUFFER_GETS_RANK
  FROM V$SQL
)
WHERE BUFFER_GETS_RANK <= 10