Rede Social

SQL: Selecionar apenas os decimais dos números

SQL Server Express - Backup, Recursos, Comparando edições
No post de hoje vamos ver como selecionar apenas os decimais de um número no SQL Server.

É bem simples, para demonstrar isso vou declarar uma variável decimal.
declare @teste decimal(4,3)

Setar um valor com casas decimais.
select @teste = 2.938

E executar o comando:
select PARSENAME(@teste, 1)

Na verdade essa é uma função mais utilizada para tratamento de texto, mas para esse caso funciona, o que o parsename faz é separar o texto pelo "." no caso separando o "2" do "938".

Confira também a documentação oficial.

Python: Uma função Pythônica para validar CPF

 


Algoritmos para validação de CPF são alguns dos primeiros algoritmos que aprendemos em cursos de programação (pelo menos foi assim comigo).

CPFs são compostos por 11 dígitos, sendo que os 2 últimos são dígitos verificadores. Portanto, para sabermos se um CPF é ou não válido, precisamos realizar o cálculo (que foi definido pela Receita Federal) e comparar o resultado obtido com os valores do dígito verificador.
 
O cálculo é bem simples! Vamos imaginar o seguinte CPF:
504.065.090-69 (CPF gerado aleatoriamente)
Primeiro pegamos os 9 primeiros dígitos e aplicamos a cada um deles um peso que se inicia em 2, da direita para a esquerda.

5

0

4

0

6

5

0

9

0

10

9

8

7

6

5

4

3

2

 
Após isso, basta somarmos cada dígito multiplicado por seu peso:
5 * 10 + 0 * 9 + 4 * 8 + ... + 0 * 2 = 170
 
Com este resultado, podemos optar entre 2 cálculos:
 
11 - 170 % 11

ou
 
170 * 10 % 11
 
Obs.: Lembrando que "%" é usado para calcular o resto (módulo) da divisão na maioria das linguagens de programação.
Ambos os cálculos nos levará ao valor do primeiro dígito verificador, que para este exemplo é 6.

Agora basta fazermos o mesmo calculo anterior, mas considerando também o primeiro dígito.
Os pesos ficarão conforme abaixo:
 

 5

0

4

0

6

5

0

9

0

6

11

10

9

8

7

6

5

4

3

2

 

Como a partir daqui o cálculo é exatamente o mesmo, vamos então para o algoritmo em Python!

TAMANHO_CPF = 11


def is_cpf_valido(cpf: str) -> bool:
if len(cpf) != TAMANHO_CPF:
return False

if cpf in (c * TAMANHO_CPF for c in "1234567890"):
return False

cpf_reverso = cpf[::-1]
for i in range(2, 0, -1):
cpf_enumerado = enumerate(cpf_reverso[i:], start=2)
dv_calculado = sum(map(lambda x: int(x[1]) * x[0], cpf_enumerado)) * 10 % 11
if cpf_reverso[i - 1:i] != str(dv_calculado % 10):
return False

return True


if __name__ == "__main__":
print(is_cpf_valido("0"))
print(is_cpf_valido("123456789012345"))
print(is_cpf_valido("55555555555"))
print(is_cpf_valido("19627722000"))
print(is_cpf_valido("19627722091"))
print(is_cpf_valido("19627722090"))
print(is_cpf_valido("02927367035"))
print(is_cpf_valido("64935775009"))
print(is_cpf_valido("45508834052"))
print(is_cpf_valido("50406509069"))

Na linha 1 apenas definimos uma constante com o tamanho do CPF.
 
Na linha 4 temos a declaração da nossa função.
 
Na linha 5 realizamos nossa primeira validação. Se o CPF não tiver exatamente 11 caracteres retornamos que o CPF informado é inválido. (lembre-se de limpar a mascara do CPF antes de chamar esta função).
 
Na linha 8 validamos se não é um CPF inválido conhecido (111.111.111-11, 222.222.222-22, ..., 000.000.000-00). Esta validação é feita a partir de um iterável gerado com uma comprehension. A comprehension irá iterar sobre cada valor da string "1234567890" e irá multiplicar cada caractere pelo tamanho do CPF. Aqui temos 2 particularidades sobre strings no Python:
1. Strings são iteráveis
2. Quando multiplicamos uma string por um valor inteiro, o resultado obtido será aquela string repetida x vezes.
 
Exemplo:
"a" * 10 = 'aaaaaaaaaa'
 
Na linha 11 o CPF informado é invertido. Exemplo:
De 12345678910 para 01987654321
A inversão é feita para facilitar a aplicação dos pesos.
 
Na linha 12 nós declaramos um for que irá iterar 2x, iniciando em 2 e finalizando em 1.

Na linha 13 é onde aplicamos os pesos de cada digito do CPF.
Primeiro nós pegamos o CPF invertido e fazemos uma substring delete que vai de "i" até o final.
Então pegamos esta substring e aplicamos os pesos nela, através da função "enumerate".
A função enumerate merece um post só para ela. Mas, de maneira resumida, ela recebe um iterável e enumera cada posição deste iterável. Ela retorna um "enumerate object" (que é também um iterável) de tuplas. Podemos converter este "enumarete object" em uma lista, por exemplo.
 
Exemplo:
list(enumerate([1, "b", 3])) = [(0, 1), (1, 'b'), (2, 3)]

Obs.: Vale atentar que no nosso caso nós iniciamos a função enumerate em 2 e não em 0.
 
A linha 14 é onde aplicamos o cálculo explicado no começo do post.
Primeiro multiplicamos os dígitos por seus respectivos pesos através da função "map". Assim como a função enumerate, a função map merece um post só para falar dela, mas vamos para um resumo. Ela recebe uma função e um iterável, e a partir disso gera um novo iterável.
No nosso exemplo, a função informada é uma expressão lambda (que resumidamente é uma função anônima) e o enumerate object criado na linha anterior. É na expressão lambda que ocorre a multiplicação dos digitos pelos seus pesos.
Usamos então função sum para somar o iterável retornado pela função map e aplicamos o resto da fórmula.

Na linha 15 nós comparamos o dígito do CPF informado com o dígito calculado. Caso os valores sejam diferentes, retornamos que o CPF é inválido.
A única observação que cabe aqui (além da conversão do dígito calculado para string) é que nós comparamos o módulo do dígito calculado por 10. Fazemos isso, pois quando o módulo da divisão por 11 resulta em 10, o valor que deve ser considerado é 0.

Como dito anteriormente, o loop será executado 2x. E caso não haja nenhuma inconsistência, a função irá retornar que o CPF é válido (linha 18).

Gestão de projetos: Gráfico de Gantt o que é e como funciona

 


Gráfico de Gantt ou Diagrama de Gantt é usado para controlar visualmente um cronograma, muito conhecido e utilizado na gestão de projetos, pois ele auxilia a se ter uma avaliação rápida do status do projeto, quais tarefas foram executadas, prazos de entrega, dependências, tudo com intervalo de tempo.

O gráfico foi criado pelo norte americano Henry Gantt na década de 20 que se inspirou em Harmonogram criado pelo engenheiro polonês Karol Adamiecki. A ideia de Henry era evitar atrasos na produção de fábricas.
Ele trás vários benefícios como por exemplo:
  • Mostrar de maneira clara e visual o andamento do projeto
  • Segmenta tarefas, definindo a ordem e interdependência que elas devem ser executadas
  • Distribui responsabilidades (caso deseje adicionar isso ao gráfico)
  • Fácil visualização dos prazos das tarefas
Resumindo seu funcionamento:
Para criar seu gráfico de Gantt você precisará enumerar as tarefas que serão realizadas, colocá-las em sequência de cima para baixo (eixo Y) e na horizontal (eixo X) o tempo (pode ser definido em horas, período de trabalho, dias, etc) e conforme as tarefas vão sendo concluídas você vai preenchendo com retângulos esse espaço de tempo que a tarefa demorou a ser executada. Vejamos no exemplo abaixo um gráfico um pouco mais detalhado e extenso.
Existem várias ferramentas online para criação desses gráficos e gestão de projetos, inclusive o Excel também tem como criar gráficos de Gantt.

CSS: Filtros (drop-shadow, grayscale, saturate, sepia e invert)

 


No post de hoje continuaremos falando de mais alguns filtros do CSS, caso não tenha visto o post anterior CSS: Filtros (opacity, brightness, contrast e blur)


Começando com o sepia, como o nome já diz ele vai converter a imagem em sépia, onde 0% significa a imagem original e 100% é sépia por completo, qualquer valor entre isso é uma mistura da original com o tom de sépia.

.filtered {

  filter:sepia(100%);

}


O filtro saturate vai mexer com a saturação da imagem onde eu posso adicionar ou remover a saturação onde 100% é a imagem original, então caso queira aumentar deve ser valores maiores que isso (200% por exemplo) e para diminuir pode colocar entre 0 e 99%.

.filtered {

  filter:saturate(80%);

}


Converte a imagem em escala de cinza quanto maior a porcentagem mais evidente fica a escala de cinza.

.filtered {

  filter:grayscale(100%);

}


Esse filtro cria uma sombra no objeto, nesse exemplo vou colocar uma sombra um pouco abaixo, para a direita com um leve desfoque e na cor cinza. A ordem dos parâmetros é a seguinte, sombra horizontal, sombra vertical, desfoque e cor.

.filtered {

  filter:drop-shadow(8px 8px 10px gray);

}


Esse filtro inverte as cores da imagem, semelhante ao que tínhamos nos negativos de fotos. O valor de 0% é da imagem original e 100% é a imagem totalmente invertida.

.filtered {

  filter:invert(100%);

}

CSS: Filtros (opacity, brightness, contrast e blur)

No post de hoje irei mostrar alguns filtros bem interessantes do CSS. Principalmente utilizados em imagens.

Primeiro um dos filtros mais usados é o opecity, ele controla a opacidade do objeto, ele é muito utilizado para fotos, principalmente para fotos de fundo onde um texto ficará na sua frente dando assim uma melhor leitura. Para utilizá-lo é bem simples, apenas coloque a porcentagem de opacidade que deseja que o objeto fique.

.filtered {

  filter: opacity(70%);

}


Outro filtro seria o de brilho, bastante utilizado para dar um retoque de claridade a imagem ou para clarear a cor de um objeto de uma forma um pouco diferente do que apenas trocar a cor. No exemplo abaixo irei remover um pouco do brilho, por isso utilizo valores abaixo de 100%

.filtered {

  filter: brightness(50%);

}


Agora se não for o brilho que esteja faltando na imagem, mas sim o contraste o filtro a ser usado é o contrast, também definindo em porcentagem, no caso vou adicionar 40% ao contraste original da foto.

.filtered {

  filter: contrast(140%);

}


Este filtro causa um desfoque na foto, também muito interessante para usar em imagens de fundo com algum texto na frente. Segue um exemplo:

.blured {

  filter: blur(5px);

}

Certificados digitais ganham cada vez mais importância




Os Certificados Digitais são como chaves eletrônicas que abrem também "abrem portas" garantindo a autenticidade e a segurança no mundo online. Ele está sendo cada vez mais usado por empresas de todos os tipos pois é muito vantajoso seu uso e de certa forma com um custo baixo fazendo com que o mercado cresça bastante.

Existem vários tipos de certificados, vamos falar sobre os principais:

Certificado A
Esse certificado vem na forma de assitura digital, como se fosse quando você assina um documento com caneta, ele é utilizado para "assinar" um documento ou transação, mostrando que é autentica com seu titular possuindo inclusive validade jurídica. É muito conhecido por ser usado em Notas fiscais, prescrições médicas digitais, leilões online, enfim uma infinidade de documentos digitais que se comparados com os físicos requiririam assinatura.

Ele existe em vários formatos onde cada um pode variar o tempo de validade ou a forma de se instalar no computador, sendo os mais conhecidos os A1 e A3.


Certificado S
Esse certificado é de sigilo, ou seja, ele criptografa os dados enviados na transação ou que serão armazenados com uma dupla de chaves onde apenas quem tem a chave pode decriptar. Ele também tem alguns subtipos S1, S2, S3 e S4, e suas diferenças estão ligadas ao armazenamento.


Certificado T
O certificado tipo T são de certa forma parecidos com o certificado do tipo A, mas com um adicional de time-stamping que significa “marca de tempo”. Então além de assinar um documento ou transação ele marca quando isso ocorreu com data e hora. Ele tem duas opções T3 e T4.

Python: List Comprehension

 

 Recentemente comecei a estudar Python, e um dos recursos que mais achei interessantes foram as Comprehensions.

As comprehensions nada mais são que uma maneira sucinta de gerar novos objetos a partir de outros objetos iteráveis (listas, sets, dicts, etc). São muito utilizadas para substituir instruções for mais simples, ou funções como map e filter.

Neste artigo vou exemplificar apenas as List Comprehension, pois, além de serem as mais simples, a lógica para as demais (set, dict, etc) é a mesma, mudando apenas o tipo de dado a ser tratado.

A sintaxe mais básica de uma list comprehension é:

[expressão for item in iterável]

Exemplo:

 

Podemos condicionar os dados que queremos retornar com if.

[expressão for item in iterável if condição]

Exemplo:

 

Também podemos utilizar o else quando queremos condicionar nossos dados

[expressão se verdadeiro if condição else expressão se falso for item in iterável]

 

Para finalizarmos nosso post, vale ressaltar que onde temos a "expressão", podemos fazer uso de funções, mudar o tipo de dado a ser retornado, utilizar outra comprehension, enfim, as possibilidades são muitas. Mas é muito importante ter em mente que devemos sempre seguir os princípios descritos no "The Zen Of Python". Então se sua comprehension está ficando muito complicada e/ou complexa, talvez seja melhor fazer uso da boa e velha instrução for.


Até a próxima!

SQL: DateDiff calculando datas

No post de hoje irei mostrar uma forma de se calcular datas no SQL, esse calcular seria realmente fazer a diferença de uma data para outra.

Para isso iremos usar a função DATEDIFF que como o nome bem intuitivo diz, nos retorn a diferença entre uma data inicial para uma data final e ainda permite que eu escolha entre vários tipos de retorno qual desejo (segundos, horas, meses etc.).

A sintaxe é DATEDIFF (TipoDeDado, DataInicial, DataFinal)

No tipo de dado os mais comuns são:
  1. year
  2. month
  3. day
  4. hour
  5. minute
  6. second
  7. millisecond

Um exemplo para ilustrar seria SELECT DATEDIFF(day, '2020-09-01', '2020-09-03')

A lista completa pode ser vista na documentação oficial: Microsoft

C#: Limitando retorno de registros LINQ

 Seguindo com o LINQ vamos ver hoje como limitar o número de registros.

Caso não tenha visto os posts anteriores sugiro que dê uma conferida, neles falamos de Ordenação e Agregação.


Tendo como base a mesma classe e lista do exemplo anterior:

Aula
{
    Id int
    NomeAula string
    Curso int
    TempoDuracao datetime
}

List<Aula> aulas;

Imagine que quero saber quais são as 5 aulas com maior tempo de duração, primeiramente tenho que ordenar pelo tempo de duração, no caso como quero as de maior tempo a ordenação é descendente (do maior para o menor) e utilizo o método Take passando 5 como parâmetro.

var cursos = db.Cursos.OrderByDescending(c => c.TempoDuracao).Take(5);


Ou caso eu queira apenas a de maior tempo de duração, mas quero também me certificar de que caso eu não tenha nenhuma aula cadastrada no curso não me retorne um erro, uso o método FirstOrDefault. E indico sempre usá-lo ao invés do First como garantia, por mais que saiba que a lista tenha valores.

var curso = db.Cursos.OrderByDescending(c => c.TempoDuracao).FirstOrDefault();

Instalando o Ubuntu Server 20.04 LTS no Oracle VM VirtualBox

 

 

No post de hoje mostraremos uma maneira bem básica de se criar um servidor Ubuntu no Oracle VM VirtualBox.

Apesar da popularização dos ambientes em nuvem, uma VM não deixa de ser uma opção bem interessante para ambientes de desenvolvimento e/ou aprendizagem. Eu mesmo gosto bastante de criar VMs quando quero testar algum OS diferente, ou realizar alguns testes que podem acabar "crashando" o OS, ou ainda quando estou desenvolvendo algum projeto. Neste último caso eu geralmente configuro uma VM como um espelho do ambiente que terei em produção. Com isso posso realizar vários testes a vontade, sem me preocupar se vou ou não fazer alguma besteira, uma vez que basta voltar para o último snapshot válido, ou, no pior dos casos, reinstalar a VM.

Bom, mas chega de falação e vamos ao que interessa!

Antes de mais nada você precisará baixar e instalar o Oracle VM VirtualBox. Você pode baixar clicando aqui, ou pela loja de apps do seu OS.

Neste post não iremos mostrar como instalar o VirtualBox, mas é basicamente o padrão Next, Next, Next...

Uma observação importante! Talvez seja necessário que você habilite a configuração de virtualização na BIOS do seu computador.

 

O Ubuntu Server pode ser baixado clicando aqui, ou, se preferir, pelo site oficial https://ubuntu.com/.

 

Ao abrir o Oracle VM VirtualBox, você verá uma tela similar a esta:

 

Vamos agora criar a nossa VM clicando em Novo

Aqui iremos fazer a configuração básica da nossa VM, dar um nome, dizer o local onde os arquivos da VM ficarão salvos, o tipo do OS e a versão do OS (geralmente essas informações serão sugeridas de acordo com o nome que você der a sua VM). Também devemos informar a quantidade de memória RAM (neste caso vou usar 4GB) e se iremos criar um HD virtual ou utilizar um existente (ou não utilizar nenhum). Neste caso criaremos um novo.


Feito essas configurações, clicaremos em "Criar".

Agora a configuração é relacionada ao HD. Basicamente pede-se que informemos onde será salvo o HD virtual, o tamanho, o tipo e se será alocado dinamicamente ou não. Usaremos as configurações padrão, então basta clicar em "Criar".

 

Pronto! Com isso temos nossa configuração base. Agora vamos para a instalação do OS. Basta "Iniciar" a VM.


Ao iniciarmos, a primeira coisa que precisaremos fazer é informar o local da ISO que baixamos anteriormente. Após isso podemos então iniciar a instalação clicando em "Iniciar".


O processo será iniciado com o Ubuntu fazendo algumas checagens. E após isso, com tudo estando OK, ele pedirá para que selecionemos o idioma de instalação.

Obs: A partir daqui toda as configurações serão feitas pelo teclado.

Selecione o idioma desejado através das setas do teclado e pressione "Enter".


O próximo passo é selecionar o layout do teclado. Provavelmente o Ubuntu já conseguiu identificar sozinho, mas caso não basta alterar a configuração para a do seu teclado.


A próxima configuração é a de rede. Manteremos a configuração padrão.


Agora nos é pedido uma configuração de proxy. Caso você utilize algum proxy, basta informá-lo no respectivo campo. Senão, basta seguir com a instalação.


A próxima configuração é a do mirror do Ubuntu. De maneira simples, este mirror é onde está localizado o repositório do Ubuntu. Aqui temos uma lista dos principais mirrors disponíveis por país. Mas não é necessário alterar esta configuração, portanto apenas continuaremos com a instalação.


Chegamos agora na configuração do storage do sistema. Aqui podemos particionar o disco que criamos anteriormente como bem entendermos. Porém, como não é a intenção deste post falar sobre sistemas de arquivos e partições, iremos adotar a configuração sugerida na instalação.


Temos agora o resumo das configurações do storage.


Ao seguir com a instalação será exibida uma mensagem de confirmação com alguns alertas sobre a instalação.


O próximo passo é informar os nossos dados de usuário e o nome do nosso computador.


Temos agora a opção de instalar um servidor OpenSSH. Como também não é intenção deste post falar sobre SSH, continuaremos o processo sem instalar o OpenSSH


Por fim, temos a possibilidade de instalar vários outros recursos, como por exemplo o aws-cli, que realiza integração com o Amazon AWS, ou o cliente para ingreção com o Heroku, entre outros. Caso você utilize algum dos serviços listados, você pode selecioná-los neste momento. Ou também pode instalá-los posteriormente.


A partir deste momento a instalação será iniciada, e, quando for finalizada, será solicitado que reiniciemos a máquina.

Obs: É comum acontecer um erro na hora de reiniciar, geralmente quando o sistema tenta "desmontar" a ISO. Caso isso ocorra com você, basta pressionar "Enter" que o sistema irá reiniciar normalmanete. Mas se mesmo assim o sistema não reiniciar, basta fechar a janela pedindo para desligar a máquina, e então iniciar novamente o servidor.


Pronto! Após a reinicialização poderemos logar no sistema utilizando o usuário que criamos durante a instalação.


Mas antes de finalizarmos o post aqui, vamos criar um snapshot do nosso servidor. Então vamos começar desligar nosso servidor digitando o comando "shutdown now".

Quando o servidor for desligado, sua janela será fechada automaticamente.

Voltamos agora para o VirtualBox e vamos clicar nas opções do nosso servidor e selecionar a opção "Snapshots".


Basta então clicar "Criar". Será pedido para darmos um nome para nosso snapshot, assim como uma descrição.


Feito! A partir daqui podemos utilizar nosso servidor recém criado, e caso haja algum problema, basta restaurar este snapshot.

 

É interessante sempre criar snapshots antes e após alterações relevantes do sistema, como uma atualização, por exemplo. Isso fará com que não tenhamos muitos "prejuízos" se precisarmos restaurar o sistema.


E com isso finalizamos nosso post! Até a próxima!

C#: Funções de agregação LINQ

Seguindo o post anterior de LINQ sobre ordenações  vamos ver algumas funções de agregação.

Tendo como base a mesma classe e lista do exemplo anterior:

Aula
{
    Id int
    NomeAula string
    Curso int
    TempoDuracao datetime
}

List<Aula> aulas;

Primeiramente quero saber quais aulas tem o maior tempo, a que tem o menor tempo e a média de tempo de todas elas.

Para saber a de maior usarei o Max:
var max = aulas.Max(c => c.TempoDuracao);

Para saber a de menor o Min
var min = aulas.Min(c => c.TempoDuracao);

E para a média o Average
var media= aulas.Average(c => c.TempoDuracao);

Uma outra opção bem interessante é contar quantos registros existem seguinte uma regra que eu definir, por exemplo, quero saber quantas aulas tem Engenharia no nome, para isso utilizo o Count para contar e dentro dele utilizo o Contains para comparar as strings.
int total = aulas.Count(c => c.NomeAula.Contains("Engenharia"));

Transferência de dados CLOB entre bancos Oracle, via DB Link



Uma vez, ao implementar um programa que precisava comunicar dois bancos de dados Oracle 11g diferentes, e em tempo real, uma limitação do próprio banco de dados em enviar de dados CLOB de um banco para o outro via DB link tornou-se um grande problema. Caso este problema não fosse contornado de alguma maneira, não seria possível fazer a integração entre os bancos em tempo real. Como esta limitação está relacionada ao tamanho que os dados CLOB comportam, a solução foi dividir as informações a serem transferidas em blocos informações menores e em um tipo de dado que permitisse a transferência das informações entre os bancos de dados, neste caso, o VARCHAR2.

Mas o que é um DB Link?
Conforme a documentação fornecida pela Oracle, um DB Link é um objeto de esquema que permite o acesso aos objetos à partir de outro banco de dados.
Após a criação do link, é possível acessar os objetos do outro banco através de instruções SQL, adicionando "@nomedblink" logo após o nome do objeto. Também é possível executar outras instruções SQL, como por exemplo INSERT, UPDATE, DELETE, LOCK TABLE.

Por sorte, a própria Oracle já fornece algumas funções para a manipulação de dados de tipo "LOB" (Large Objects). Estas funções se encontram dentro do pacote "dbms_lob".

Para a solução deste problema foram usadas apenas 4 funções, que são:

Função "dbms_lob.substr"
Utilizada para ler um tipo de dado CLOB, NCLOB ou BLOB à partir de um offset ("deslocamento").
"[…] É uma função passada-por-valor que retorna um tipo de dados RAW para tipos BFILE e BLOB e VARCHAR2 para CLOB ou NCLOB. [...]" (MCLAUGHLIN, 2009, p.311).
Em outras palavras, com esta função é possível retornar um pedaço de um texto armazenado dentro de um campo CLOB, por exemplo.

Função "dbms_lob.createtemporary"
Cria um BLOB, CLOB ou NCLOB temporário na memória. "[…] Os LOBs temporários são entidades de limite de tempo[...]" (MCLAUGHLIN, 2009, p.313), ou seja, somem da memória assim que o processo é finalizado (ou assim que são explicitamente "destruídos").

Função "dbms_lob.append"
É utilizado para adicionar conteúdos ao final de um LOB. (MCLAUGHLIN, 2009, p.304).

Função "dbms_lob.freetemporary"
É utilizado para liberar a memória consumida por um BLOB, CLOB ou NCLOB criado através da função "dbms_lob.createtemporary". Recomenda-se que esta função sempre deva ser utilizada ao trabalhar com LOBs temporários. (MCLAUGHLIN, 2009, p.313).

Implementação

Para o desenvolvimento deste exemplo fez-se uso do banco Oracle 11g Express Edition, por ser uma versão gratuita.
A título de exemplificação, foram criados 2 usuários. "user_emissor" e "user_externo". O usuário "user_emissor" representará o banco de dados que contém a origem das informações, ou seja, que contém a tabela com uma coluna do tipo de dado CLOB. Já o usuário "user_externo" representará um banco de dados externo que precisa fazer o acesso ao banco proprietário das informações ("user_emissor").
Para que a comunicação entre os usuário seja possível, é necessária a criação de pelo menos 2 objetos. Sendo um pacote que fará a serialização do CLOB num vetor de VARCHAR2 no usuário "user_emissor", e uma função que concatenará o vetor novamente em CLOB no usuário "user_externo". No exemplo também será criada uma view no usuário "user_externo". A implementação desta view não é obrigatória, mas facilitará a exibição dos dados.

Estrutura da tabela "exemplo_clob":
Dentro do esquema do usuário "user_emissor" existe a tabela "exemplo_clob".

Diagrama Tabela "exemplo_clob".

Esta é a tabela que precisa ser acessada pelo usuário "user_externo".
Ela possui apenas duas colunas, sendo uma para a identificação de seus registros ("id_exemplo_clob"), e outra coluna ("texto") que armazena as informações que precisam ser lidas pelo usuário "usuario_externo".


Pacote "PKG_LOB"

Para que a integração entre os bancos de dados seja possível, é necessária a implementação de um pacote que contenha a definição de um objeto do tipo TABLE. É através deste pacote que o usuário "user_externo" irá ter acesso à estrutura de dados em que o CLOB foi serializado.

Estrutura do pacote "pkg_lob". É através dela que o usuário "user_externo" terá acesso às informações do CLOB serializado.


A figura acima tem um exemplo da definição do pacote, e abaixo o detalhamento desta estrutura:
A linha 1 corresponde à instrução de criação do pacote
A linha 2 marca o início das definições do pacote. É a partir dela que todos os objetos e procedimento são definidos. Todos os objetos e procedimento públicos, ou seja, que podem ser acessados por outros objetos/usuário, devem estar descritos neste bloco.
Na linha 4 está a declaração de uma estrutura de dados do tipo TABLE OF VARCHAR2(4000). É a partir desta estrutura que os objetos de manipulação do CLOB serão montados. O tamanho do VARCHAR2 é de 4000, pois é o limite que uma instrução SELECT pode manipular.
Nas linhas 8 à 10 tem-se a definição da função que irá fazer a serialização do CLOB num vetor de VARCHAR2. Entre elas, na linha 9, está a declaração de um parâmetro de entrada que será utilizado para filtrar a informação desejada. Deve-se atentar à linha 10, pois ela define o retorno da função como sendo a estrutura de dados criada anteriomente.
Por fim, a linha 12 marca o final da declaração do pacote.


Corpo do pacote

Estrutura do corpo do pacote "pkg_lob". Contém o algoritmo que faz a serialização do CLOB num vetor VARHCAR2.

O corpo do pacote é onde está contida toda a lógica (figura acima). Abaixo segue o detalhamento de sua estrutura:

A linha 1 corresponde à instrução de criação do corpo do pacote.
A linha 2 marca o início das declarações de todos os objetos e procedimentos acessíveis dentro do pacote. Nenhum objeto e/ou procedimento que esteja dentro desta declaração será público, exceto para os casos dos procedimentos estejam declarados na estrutura do pacote que permite o acesso externo.
Entre as linhas 6 e 38 está a função responsável por fazer a serialização do CLOB no array de VARCHAR2.
As linhas 6 a 8 criam a definição da função. Esta deve estar exatamente igual ao que foi definido na estrutura principal do pacote.
Entre as linhas 9 e 11 está a declaração da variável "v_array" do tipo TabArray, onde TabArray é o objeto TYPE criado na definição do pacote, e a declaração de uma constante ("c_max_length"), que armazena o valor máximo de cada bloco VARCHAR2 do vetor.
A linha 14 faz a inicialização do objeto vetor.
Na linha 17 está a declaração do loop que fará a serialização do CLOB em VARCHAR2.
Entre as linhas 18 e 24 estão as instruções SELECT que fazem a serialização do CLOB em VARCHAR2. Para que seja possível o entendimento desta instrução, precisa-se olhar do SELECT interno para o SELECT externo. O SELECT interno faz uso da função CEIL com a função LENGTH (linha 20). A função LENGTH faz a contagem da quantidade de caracteres contidos no CLOB. Este resultado é divido pela constante "c_max_length", que é o limite de caracteres definido para este exemplo. Ao resultado desta divisão é aplicada a função CEIL. Esta função faz com que um valor que contenha casas decimais seja arredondado para o próximo valor inteiro. Exemplo: CEIL(4,01) = 5. A este resultado da-se o alias (apelido) de "qtd_linhas"
Logo abaixo, na linha 21, o SELECT retorna a coluna "texto", que é a coluna que contém o(s) valor(es) CLOB(s) a ser(em) serializado(s).
Na linha 23 o parâmetro "pi_id_exemplo_clob" é utilizado para filtrar apenas um registro da tabela e fazer a serialização das informações deste registro.
Com base nas informações retornada pelo SELECT interno, o SELECT externo faz a serialização dos dados CLOB através da combinação da clausula CONNECT BY com a função "dbms_lob.substr". Ao utilizar a cláusula CONNECT BY com o resultado de "qtd_linhas" faz-se com que o SELECT retorne uma quantidade de linhas igual ao valor de "qtd_linhas", ou seja, se "qtd_linhas" for igual a 7, o SELECT retornará 7 linhas, por exemplo. O identificador de cada linha fica por conta da função "LEVEL", que retorna qual é a posição de cada linha na hierarquia.
O resultado de "LEVEL" possui 2 utilidades, definir o índice das posições do vetor (linha 19), e, junto da função "dbms_lob.subtr", realizar a divisão do CLOB em blocos de VARCHAR2 (linha 21).
As linhas 25 e 33 marcam, respectivamente, o início e o fim do bloco que carrega o vetor.
A linha 28 cria, através da função "EXTEND", uma nova posição para o vetor "v_array".
A linha 31 define o índice de "v_array" com o valor de "LEVEL" (ao qual foi dado alias de "i") e atribui na posição deste índice o bloco de VARCHAR2 gerado pelo SELECT.
Ao finalizar todas as iterações do loop, o vetor "v_array" é retornado através da instrução "RETURN" (linha 37).
A linha 38 marca o final da função, e a linha 44 marca o final da declaração do corpo do pacote.

Partimos agora para a função "get_exemplo_clob_texto". É através dela que a desserialização do CLOB é feita. Ela também é responsável por criar o vínculo entre os usuários "user_externo" e "user_emissor".

Estrutura da função "get_exemplo_clob_texto". É responsável por fazer a desserialização do vetor e retornar um CLOB.


As linhas 1 e 3 marcam definição da função, que é igual à função que está dentro do pacote "pkg_lob" no usuário "user_emissor", exceto pelo tipo de dado a ser retornado, que, neste caso, é um CLOB.
Entre as linhas 5 e 7 estão declaradas 3 variáveis. Uma do tipo TabArray ("v_array") e duas do tipo CLOB ("v_temp" e "v_texto"). É importante perceber que a declaração da variável "v_array" é feita com o uso do DB link, fazendo referência ao objeto TabArray do pacote "pkg_lob", que pertence ao usuário "user_emissor". É aqui onde encontra-se todo o segredo por trás do algoritmo, pois a estrutura que é utilizada para criar o vetor passa a ser compartilhado por ambos os usuários.
Entre as linhas 12 e 14 está a chamada para a função "pkg_lob.get_exemplo_clob_texto" e armazena o valor que ela retorna dentro da variável "v_array".
Após isso, na linha 17, um objeto temporário do tipo CLOB é criado através da variável "v_temp" e a função "dbms_lob.createtemporary".
O bloco entre as linhas 20 e 22 é responsável por varrer todo o vetor, e concatenar os dados contidos dentro de cada um dos blocos deste vetor na variável "v_temp", através da função "dbms_lob.append".
Após este processamento, o resultado obtido em "v_temp" é atribuído à variável "v_texto" (linha 25), e, logo em seguida, a variável temporária "v_temp" é eliminada da memória com o uso da função "dbms_lob.freetemporary" (linha 28).
Por fim, na linha 31, a função devolve os dados que foram inicialmente recebidos de maneira serializada, como se fosse originalmente um dado do tipo CLOB.
A linha 33 marca o final de todo o bloco correspondente à função.


Visualização dos dados através da view "VIEW_EXEMPLO_CLOB"

A estrutura da view é feita de maneira que de a impressão ao usuário que ela nada mais é que um "espelho" da tabela "exemplo_clob". Assim como a função "get_exemplo_clob_texto", ela também é implementada no lado do usuário "user_externo".

Estrutura da view "view_exemplo_clob". Criada para facilitar o acesso às informações da tabela "exemplo_clob" do usuário "user_emissor".

Abaixo segue o detalhamento de sua estrutura:
A primeira linha é onde defíne-se o nome da view.
A linha 2 marca o ponto à partir de onde será criado a instrução da consulta.
Entre as linhas 3 e 5 está a instrução da consulta utilizada para retornar os dados. Na linha 3 para o ínico da consulta através da cláusula "SELECT". Junto da instrução "SELECT" está a coluna "id_exemplo_clob" da tabela "exemplo_clob". A linha 4 faz uso da função "get_exemplo_clob_texto" e informa o "id_exemplo_clob" como parâmetro da função. Isso significa que será buscado o texto correspondente à identificação da linha que está sendo processada. Por fim, na linha 5 está identificada a fonte dos dados, ou seja, a tabela "exemplo_clob". Como esta tabela não pertence ao usuário "user_externo", faz-se necessário o uso do DB link para que as informações sejam acessdas.
A linha 6 marca o final do script de criação da view.

Comparando os resultados

A fim de mostrar a eficácia deste método, duas consultas exatamente iguais são executadas, uma para cada um dos usuários. Exceto que para o usuário "user_emissor" a consulta será em cima da tabela "exemplo_clob", enquanto no usuário "user_externo" a consulta é em cima da view "view_exemplo_clob". Porém isto não afeta o resultado, uma vez que a fonte de dados da view é a tabela "exempl_clob", ou seja, extamente a mesma fonte de dados utilizada para o usuário "user_emissor".

Execução da consulta no usuário "user_emissor":

Consulta no usuário "user_emissor"

Execução da consulta no usuário "user_externo":

Consulta no usuário "user_externo".


Como uma coluna CLOB pode armazenar um valor muito grande (e que talvez não seja possível visualizar por completo, como é o caso deste exemplo), a função "LENGHT" foi utilizada para realizar a comparação do tamanho do texto retornado em cada usuário. Como podemos observar, o resultado para ambos os usuários foi exetamente igual, "18346" caracteres. Isso mostra que toda a serialização e desserialização destes dados ocorreu como o esperado, uma vez que nenhuma informação foi perdida, ou acressida.

Referências

WATSON, John; RAMKLASS, Roopesh. OCA Oracle Database 11g: Fundamentos I SQL. Rio de Janeiro: Alta Books, 2008. 564 p.

MCLAUGHLIN, Michael. Oracle Database 11g: PL/SQL Programação. Rio de Janeiro: Alta Books, 2009. 830 p.

ORACLE. CREATE DATABASE LINK. 2005. Disponível em: <https://docs.oracle.com/cd/B19306_01/server.102/b14200/statements_5005.htm>. Acesso em: 15 abr. 2017.

ORACLE. HIERARCHICAL QUERIES 2005. Disponível em: <https://docs.oracle.com/cd/B19306_01/server.102/b14200/queries003.htm>. Acesso em: 18 mai. 2017.

ORACLE. HIERARCHICAL QUERIES PSEUDOCOLUMNS 2005. Disponível em: <https://docs.oracle.com/cd/B19306_01/server.102/b14200/pseudocolumns001.htm#i1009261>. Acesso em: 18 mai. 2017.

ORACLE. DBMS_LOB 2005. Disponível em: <http://docs.oracle.com/cd/B19306_01/appdev.102/b14258/d_lob.htm>. Acesso em: 18 mai. 2017.