terça-feira, 23 de outubro de 2012

Contando linhas com Perl

Eu queria contar as linhas de um arquivo de uns 4GB no Windows. No Linux, o wc resolveria a questão com sobra. Eu tentei usar o GNU Coreutils, mas não achei uma versão binária para o Sistema Operacional de Redmond.

Então, recorri ao camelo e com uma linha de Perl, resolvi o problema:

C:\perl -pe "}{$_=$." meu_arquivo_grande.txt
8738433

Não podia ser mais simples e claro.

quarta-feira, 17 de outubro de 2012

Relatórios no Oracle

Considere uma tabela PESSOAS com estas colunas:

  (NASC DATE, SEXO VARCHAR2(1), NOME VARCHAR2(100))

A coluna NASC guarda a data de nascimento e a coluna SEXO pode conter 'M' ou 'F'. Se eu quiser montar um relatório com o número de aniversários para cada dia do ano, vou ter que fazer uns contorcionismos, mas se eu quiser, ademais, separar o número de aniversários por sexo, a consulta vai ficar ilegível.

Escondido na documentação da Oracle estava um tipo de join que permite resolver esse problema com relativa facilidade: o partitioned outer join. Esse join tem a seguinte forma:

select *
from a 
     left outer join b partition by (cz) on a.ca=b.ca

E isso significa que será executado um cross-join dos valores distintos de CZ com as linhas da tabela A e, a seguir, o resultado desse cross-join será usado no outer-join com a tabela B.

De volta ao problema original, inicio com uma contagem dos aniversários por dia e por sexo:

  select
    to_char(nasc,'MMDD') nasc, 
    sexo, 
    count(1) total
  from pessoas
  group by to_char(nasc,'MMDD'), sexo

Além disso, preciso de uma tabela com todos os dias do ano:

  select to_char(trunc(sysdate, 'YYYY')+rownum-1, 'MMDD') dia
  from all_tables
  where rownum<367

E junto tudo com o partitioned outer join:

select dia, sexo, nvl(total, 0)
from (
  select to_char(trunc(sysdate, 'YYYY')+rownum-1, 'MMDD') dia
  from all_tables
  where rownum<367
) dias
  left outer join (
    select to_char(nasc,'MMDD') nasc, sexo, count(1) total
    from pessoas
    group by to_char(nasc,'MMDD'), sexo) p partition by (sexo) on p.nasc=dia
order by dia, sexo

Essa consulta produz duas linhas para cada dia do ano: uma para o número de aniversários de homens e outro para o número de aniversários de mulheres:

0101F2
0101M0
0102F1
0102M3

Armado desta nova ferramenta, decidi resolver um problema ainda maior: enumerar os aniversariantes de cada dia. Comecei com uma lista dos aniversariantes para cada dia:

  select nasc, sexo, substr(SYS_CONNECT_BY_PATH(nome,', '),3) nomes  
  from (
    select 
      nasc, nome, sexo,
      row_number() over (partition by nasc, sexo order by nome) ordem,
      row_number() over (partition by nasc, sexo order by nome desc) medro 
    from (
      select to_char(nasc, 'MMDD') nasc, sexo, nome from pessoas
    ) 
  )
  where medro=1
  start with ordem=1
  connect by prior nasc=nasc and prior sexo=sexo and prior ordem=ordem-1

Juntando com a lista dos dias do ano, descubro o seguinte:

select dia, sexo, nvl(nomes,'-') nomes
from (
  select to_char(trunc(sysdate, 'YYYY')+rownum-1, 'MMDD') dia
  from all_tables
  where rownum<367
) dias
  left outer join (
    select nasc, sexo, substr(SYS_CONNECT_BY_PATH(nome,', '),3) nomes  
    from (
      select 
        nasc, nome, sexo,
        row_number() over (partition by nasc, sexo order by nome) ordem,
        row_number() over (partition by nasc, sexo order by nome desc) medro 
      from (
        select to_char(nasc, 'MMDD') nasc, sexo, nome from pessoas
      ) 
    )
    where medro=1
    start with ordem=1
    connect by prior nasc=nasc and prior sexo=sexo and prior ordem=ordem-1
  ) p partition by (sexo) on p.nasc=dia
order by dia, sexo

Não é das coisas mais simples, mas ele produz bastante para o seu tamanho. O resultado é parecido com:

0101FHelena, Maria
0101M-
0102FFernanda
0102MHenrique, João, Pedro

Por enquanto, apenas o Oracle oferece esse join.

quinta-feira, 11 de outubro de 2012

Dialética do software corporativo

Durante a implementação de uma série de regras de negócio ocorreu-me que seria muito mais interessante desenvolver software como uma ferramenta: o usuário que decida como usá-lo e o usuário que decida quais são as formas válidas de usá-lo.

Os usuários já se permitem pequenas transgressões como escrever nomes próprios em caixa alta e sem acentos. Na língua portuguesa, só há uma maneira de escrever João da Silva (exatamente essa). Tanto "joao da silva" como "JOAO DA SILVA" ou "XOAU d4 $y)v4" estão errados. Mas como não é prático implementar um conjunto de regras para validar os nomes próprios, essa tarefa raramente é ensaiada, embora frequentemente seja contemplada (principalmente quando alguém quer um relatório esteticamente aceitável).

Esse objetivo pode ser abandonado porque é claramente inatingível, mas por que o software continua sendo escrito sob a ilusão de que é possível (e desejável) limitar o usuário apenas a interações válidas (conforme o arcabouço vigente no momento de sua concepção)?

No momento em que o ambiente do software muda (muitas vezes antes que ele entre em produção), as regras de negócio precisam ser reavaliadas e recodificadas.

Creio que a resposta esteja na dinâmica das empresas. A criação de regras rígidas para a operação de sistemas está assentada sobre as disputas de poder. O analista de sistemas não tenta apenas sintetizar as regras de negócio, ele também frequentemente testemunha o processo de disputa de poder no qual duas ou mais partes tentam impor suas visões de mundo não com o intuito de produzir o melhor para a corporação, mas para validar e avançar seu poder.

Pode-se até dizer que alguns softwares incorporam as contradições internas de suas respectivas corporações e que num momento de realinhamento de forças eles serão, inevitavelmente, redesenhados.

Além disso, existe a falta de confiança explícita no proletariado da empresa. Já se espera que o usuário final vá subverter as regras usando o sistema de forma errônea e que, como acontece com frequência, a culpa será atribuida ao sistema. E isso é visto como natural, mesmo quando for provado que o usuário agiu de má-fé (ninguém culpa o fabricante de martelos quando o marceneiro erra o prego e quebra algo). Por isso, o próprio proletariado abdica de assumir responsabilidade pelo negócio e o delega todo ao sistema (já tratam o trabalhador com desconfiança desde o início e ele sabe igualmente que pode tercerizar qualquer culpa ao software).

Ocorre também que um proletário iluminado tente impor novas regras como forma de abraçar o sistema, tomar posse dele e do poder que ele representa. Já testemunhei, por exemplo, uma tentativa de eliminar colunas desnecessárias de um relatório que, no entanto, eram importantes para outras pessoas. Se o relatório fosse visto apenas como uma ferramenta, as colunas extras poderiam muito bem ser ignoradas, mas a posse do relatório servia também como delimitação de território. Evidentemente, esse tipo de atitude representa uma traição do trabalhador à sua própria classe, numa tentativa de emular a classe dominante e, quem sabe, um dia ser aceito nela.

As metodologias de gerência de projetos apontam como importante a definição dos stakeholders, porque as empresas de software já identificaram o problema dos conflitos internos e de como eles atrapalham o processo de análise. Para a fábrica de software, não é realmente importante qual regra de negócio será implementada, desde que algum representante do cliente aceite a responsabilidade de assinar o cheque.

O que precisamos agora é introduzir o marxismo nos cursos de informática.