MySQL Show e Describe para inspecionar informações de tabelas

# present very basic information, but not custom indexes
DESCRIBE payments;

# basically the same as describe
SHOW COLUMNS FROM payments;
SHOW COLUMNS FROM production.payments;
SHOW COLUMNS FROM payments IN production;

# a more detailed information
SHOW FULL COLUMNS FROM payments;

# wanna see custom indexes?
SHOW INDEX FROM payments;
SHOW INDEX FROM payments FROM production;
SHOW INDEX FROM production.payments;

Sempre que eu quero inspecionar os detalhes de informações de uma tabela no MySQL eu somente consigo me lembrar do comando DESCRIBE, ou nem isso!

O Fato é que o MySQL fornece alguns recursos básicos mas muito eficientes para obter estas informações de forma simples e rápida.

Se você é developer certamente vai precisar de alguns destes recursos listados aí…

Os Databases Scientists e Database Administrators podem querer recorrer a recursos mais avançados como SELECT sobre as tabelas de SCHEMAS, mas isso é assunto para outro artigo.

Happy SQL!

GIT Configurando sua Identidade

git config --global user.email "myemail@gmail.com"
git config --global user.name "My Full Name"

Logo depois que instalamos o GIT no sistema operacional a primeira coisa que queremos fazer é o primeiro commit e push, não é mesmo?!

Pois é, só que tem este detalhe de que antes precisamos configurar nossa identidade no ambiente, para que o mínimo de segurança e organização necessária seja alcançada.

É claro que o GIT vai te fornecer um comentário indicando que esta configuração é necessária! Se for tentar o commit sem esta config ele vai berrar.

Na dúvida fica aí mais esta dica.

Linux Type e Which Para Localizar Path de Comandos e Executáveis

type -a bash
type -a zsh
type -a git
type -a asdf

which bash
which zsh
which git

Olha só que legal! Em algumas situações precisamos descobri o PATH de alguns comandos, pacotes ou programas instalados no seu sistema operacional.

Antigamente eu costumava fazer isso com o locate ou find, mas o resultado nem sempre era fácil de obter com estes dois recursos.

Recentemente eu estava configurando vários pacotes no meu Arch Linux Desktop e tive que encontrar o path de um aplicativo para poder configurar corretamente. Foi aí que me deparei com estes dois recursos mais modernos e mais fáceis de usar.

O type funcionou perfeitamente com os vários pacotes incluindo o asdf. Já o which falhou com o asdf não sei exatamente o porquê.

Na dúvida, vai de type… mas o which também manda bem pra caramba!

PHP array_filter Verificando se Conteúdo Possui Suporte ao array_flip

static function hasArrayFlipSupport(array $array): bool {
   return empty(
      array_filter(
         $array,
         static function ($value, $key) {
            return (!is_string($value) && !is_int($value)) || (!is_string($key) && !is_int($key));
         },
         ARRAY_FILTER_USE_BOTH
      )
   );
}

Recentemente estive trabalhando (e ainda estou) num projeto de importação de arquivos de dados, uma rotina bastante comum ainda nos dias de hoje apesar de toda a evolução tecnológica que temos vivido.

Pois bem, com a elaboração de classes mais genéricas que devem processar uma variedade quase infinita de possibilidades as coisas se complicam um pouco e precisam de mais rotinas de garantia de funcionalidade.

No meu caso, em um dado momento o uso do array_flip se mostrou ideal, só precisava inverter o array tornando as valores como chaves e as chaves como valores… simples né? Só que não.

O array_flip somente suporta a operação utilizando dados do tipo STRING e INT, e essa regra vale para a dupla chave/valor do array. Lembrando que NULL também não é suportado para a operação.

Então eu elaborei este método para verificar se o conteúdo do array suportaria o array_flip. Basicamente verifica-se o conteúdo de todo o array, ambos chave e valor, se eles possuem os tipos de dados suportados.

E foi neste momento que o array_filter se mostrou parrudo e eficiente. Por padrão o array_filter avalia apenas o valor do elemento.

array_filter($array, static function($value) { return true; });

Mas você consegue facilmente mudar este comportamento para avaliar a chave simplesmente adicionando o terceiro parâmetro ARRAY_FILTER_USE_KEY.

array_filter($array, static function($key) { return true; }, ARRAY_FILTER_USE_KEY);

Mas no meu caso eu precisei mesmo avaliar ambos simultaneamente, assim o trunfo final foi informar o terceiro parâmetro ARRY_FILTER_USE_BOTH.

array_filter($array, static function($value, $key) { return true; }, ARRAY_FILTER_USE_BOTH);

Observe que, ao acionarmos o uso de ambos chave e valor, a sequência de de entrada da dupla chave/valor ocorre na ordem inversa valor/chave. Eu não tenho detalhes para explicar este comportamento, mas acredito que seja para manter uma certa compatibilidade com o uso padrão do array_filter, o qual filtra o valor por padrão.

Alguns irão dizer que estas características são sinais de ausência de padronização e arquitetura no PHP, mas para mim esse é o tremendo charme da linguagem!

World Class Technology S2 PHP!

ASDF Atualizando versões específicas do Elixir e ERLang

# install asdf plugins for desired packages
asdf plugin-add erlang
asdf plugin-add elixir

# see plugins installed
asdf list

# see current versions already set
asdf current

# install desired versions for each pack
asdf install erlang 24.0.2
asdf install elixir 1.12.1

# now you see installed versions
asdf list

# set desired version as global setting
asdf global erlang 24.0.2
asdf global elixir 1.12.1

# check again the versions installed
asdf list
asdf current

# in my case I needed to run a more specific command, but this was also suggested by asdf when listing
asdf install elixir 1.12.1-otp-24

# done! checking final installation
asdf list
asdf current

Aqui um passo a passo de atualização dos pacotes Elixir e ERLang para versões específicas desejadas. no meu caso eu estava trabalhando num app onde as versões de cada pacote eram requerimentos para o projeto.

Como eu tinha realizado uma instalação inicial com brew, eu preferi fazer a remoção das versões mais recentes, utilizei os seguintes comandos.

brew cleanup -n
brew uninstall elixir --force
brew uninstall erlang --force

Lembrando que o Elixir depende do ERLang, então remova eles na sequência correta, caso contrário o sistema vai berrar por conflito de dependências.

No final bastou carregar as dependências do projeto e pronto…

# starts elixir console 
iex -S mix 
CTRL + C 

# needed to load dependencies 
iex -S mix get.deps 
CTRL + C 

# finally loading the app environment 
ies -S mix 

Uma dica! Observe sempre os comentários deixados no terminal durante a instalação do packs ou plugins, as vezes algo não funciona de primeira por que não observamos os detalhes nas entrelinhas!

ASDF O Gerenciador de Pacotes com Controle de Versões

# make sure your brew is fully updated
brew update
brew upgrade

# install asdf pack manager using brew of course
brew install asdf

# see lastest comments after installation, asdf path will be shown over there
# asdf path may vary depending on your environment

# add asdf path to your shell environment, this is for .zsh
echo -e '\n. /usr/local/opt/asdf/libexec/asdf.sh' >> ~/.zshrc
echo -e '\n. /usr/local/share/zsh/site-functions/_asdf' >> ~/.zshrc

# add path to your shell environment, this is for .bash
echo -e '\n. /usr/local/opt/asdf/libexec/asdf.sh' >> ~/.bash_profile
echo -e '\n. /usr/local/share/zsh/site-functions/_asdf' >> ~/.bash_profile

# reload your shell environment or open a new terminal, your shell will be loaded correctly
source ~/.zshrc
source ~/.bash_profile

# that's it, now check asdf version installed
asdf --version

# check plugins installed, this command will show data after plugins installation
asdf list

# check current packages installed, this command will show data after setting a pack version
asdf current

Esta semana eu precisei atualizar alguns pacotes no meu MacBook, mas desta vez houve um requisito de utilizar uma versão específica do Elixir e ERLang. Foi aí que descobri que o amplamente difundido “brew” não suportava um gerenciamento de versões eficiente, praticamente o brew instala a “latest version” dos pacotes e pronto.

Percorri muitos artigos na web onde a maioria deles indicava o uso do “asdf” para um gerenciamento efetivo de versões. ASDF? What the hack is that?

Para quem, assim como eu, não era familiar a este gerenciador de pacotes, apenas pense nele como sendo um outro brew da vida, mas que tem as opções de escolher a versão que você deseja instalar.

Mais que isso, múltiplas versões dos pacotes podem ser baixadas e você pode fazer a troca de uma versão para outra sob demanda. Isso é muito comum para quem trabalha com múltiplos projetos onde versões específicas são requeridas em cada projeto.

No final das contas foi muito fácil instalar o gerenciador de pacotes asdf, basta seguir a receita do bolo.

ZOOM Gravando Video Treinamentos sem Complicações

Foi por um acaso que eu tive a grande ideia de utilizar o aplicativo para reuniões remotas ZOOM para gravar vídeo treinamentos para compartilhar conhecimento… um verdadeiro achado!

Há muito tempo eu já tinha vontade de gravar videos com este objetivo de compartilhar na internet, mas sempre me deparei com aplicativos para gravação muito complexos ou que exigiam muito esforço para uma finalidade tão simples.

Foi durante a pandemia de 2020 onde começamos a utilizar o ZOOM para reuniões remotas quando o Home Office se tornou uma realidade absoluta. O ZOOM ganhou espaço rapidamente por sua enorme simplicidade de operação o que ganhou minha simpatia imediata. Em um dado momento começamos a gravar algumas reuniões em grupo, mas de repente eu me perguntei… será???

Será que se eu abrir uma reunião só pra mim, eu comigo mesmo, e iniciar uma gravação isso irá funcionar? Fiquei de boca aberta quando o resultado foi sim!!! Um mundo de possibilidades se abriu na minha mente, então pensei… #agoravai

Bom o resto é história, estamos a mais de um ano gravando treinamentos com o ZOOM, já temos uma coleção de mais de vinte vídeos de treinamento e continua crescendo.

Abaixo os screenshots com dicas para você obter sucesso na empreitada de gravação de treinamentos.

Ingresse na reunião pressionando normalmente o botão para utilizar o audio do computador.

Inicie sua câmera para que sua imagem apareça já no início da gravação. Este passo não é obrigatório, se você preferir ministrar o treinamento com a câmera fechada tudo bem, a gravação irá funcionar normalmente.

Neste caso a sua foto registrada na plataforma do ZOOM aparecerá ao invés de sua imagem.

Ao pressionar o botão Record a gravação se iniciará imediatamente, assim eu particularmente recomendo que a locução do treinamento inicie-se imediatamente também…

Como dica fica a seguinte sugestão; inicie dizendo qual o objetivo do treinamento de forma sucinta e adicione a isso que você vai compartilhar sua tela para fazer a demonstração.

Enquanto você vai desenvolvendo a locução, ao mesmo tempo deve clicar no botão Share Screen. Até que a tela seja realmente compartilhada alguns passos serão necessários como escolher qual o monitor que será compartilhado entre outros. Você pode ainda compartilhar a tela inteira do monitor ou apenas uma porção desta tela.

A vantagem de usar apenas uma porção da tela é que, ao selecionar uma pequena área do monitor o conteúdo final ficará maior e mais visível, isto é particularmente interessante se você quiser que o vídeo seja legível no caso de você estar demonstrando um sistema ou mesmo códigos fontes.

Olha que legal, você sozinho desenvolvendo o treinamento. Não precisa de platéia para ministrar um treinamento certo? Poís é, mas se tiver platéia também vai funcionar.

Você pode usar o recurso de pausar a gravação, isto é particularmente útil se você tiver uma etapa muito demorada, tipo um download por exemplo. Além de não tornar o video sacal e entediante você ainda não vai desperdiçar o tempo de quem irá assistir o treinamento posteriormente.

Antes de finalizar, é recomendado que seja feito um fechamento do treinamento com os comentários pertinentes antes de se utilizar a opção Stop Recording. Diferentemente da opção Pause Recording que permite você retomar a gravação a qualquer momento, uma vez parada a gravação não há como retomar a mesma.

Para que o video final seja compilado e gravado no disco, será necessário encerrar a reunião… pressione o botão End Meeting e a compilação irá iniciar automaticamente.

Pronto, o seu video será gravado no disco rígido em alguma área com Downloads ou Desktop… não me recordo se isto é configurável ou não. Enfim, meus vídeos são gravados no formato .mp4

E antes que alguém me pergunte por que eu não gravei este artigo ao invés de escrever… bom a gravação somente seria realizada a partir do passo 5 em diante, assim achei melhor fazer o artigo inteiro no formato tradicional mesmo!

Agora sem desculpas para não gravar e compartilhar seus conhecimentos, ok?!… Zoom it!

PHP Array Combinando a Dupla Key Value como String

function getArgsAsString(array $args): string {
    return implode(', ', array_map(static function($v, $k){ return $k.'='.$v; }, $args, array_keys($args)));
}

Oxxê! Até o título deste artigo ficou estranho, mas primeiro vou explicar onde eu precisei usar isso pra ver se facilita o entendimento do porquê esse código pode ser útil pra você.

Quando escrevemos queries SQL em PHP é bastante comum fazermos o bind dos argumentos por nome associativo ao invés de posição. Essa técnica facilita não só a legibilidade do código como também ajuda a manter um código limpo e enxuto.

Pois bem, em um dado momento eu me recordo que precisei registrar um log da operação de SQL, daí além de registrar a string contendo a Query Statement, precisei também salvar os argumentos utilizados na operação. Foi neste momento que me dei conta de que o PHP não fornece uma solução nativa para este caso.

Rapidamente pensei nas funções nativas de parsing de Array do PHP, então o array_map caiu como uma luva. Lembrando que o array_map despreza os índices associativos do array original produzindo um novo array com índices sequenciais, mas como meu objetivo era produzir uma string com a combinação KEY=VALUE, eu não tive problemas com este fato.

Porém, é possível fazermos a mesma operação preservando os índices associativos originais do array, para isto basta usar a sintaxe abaixo com o comando array_walk.

array_walk($mapped_row, function(&$value, $key) {
    $value = sprintf('%s=%s', $value, $key);
}); 

$args_string = implode(', ', $args);

Lembrando que array_walk atua sobre o array original modificando o mesmo, diferentemente do array_map que produz um array completamente novo. Se você tiver problemas em modificar o seu array original crie então uma função passando o array como parâmetro.

Diferentemente de Objetos, os Arrays não são passados por referência por default, assim uma nova array será criada antes do parsing do array_walk, o que irá preservar o array original intacto.

function getArgsAsString(array $args): string {
    array_walk($args, function(&$value, $key) {
        $value = sprintf('%s=%s', $value, $key);
    });
    
    return implode(', ', $args);
}

Bom, fora este detalhe você pode ficar a vontade para utilizar a sintaxe que preferir… aproveite!

PHP Array Usando RESET para Extrair a Primeira Linha de um Array

$y[0] = [ 'col1' => 'A', 'col2' => 'B', 'col3' => 'C' ];
$y[1] = [ 'col1' => 'value1', 'col2' => 'value2', 'col3' => 'value3' ];
$y[2] = [ 'col1' => 'value4', 'col2' => 'value5', 'col3' => 'value6' ];
$y[3] = [ 'col1' => 'value7', 'col2' => 'value8', 'col3' => 'value9' ];

$header = reset($y);
var_dump($header);

// array(3) { 
//   ["col1"] => string(1) "A"
//   ["col2"] => string(1) "B"
//   ["col3"] => string(1) "C"
// }

Pode parecer uma tarefa super simples pegar o primeiro elemento de um array, e realmente é!!! Desde a semana passada o reset() é o meu jeito preferido de fazer isso.

Recentemente quando estive implementando a importação de um arquivo tipo planilha de dados naturalmente tive a necessidade de processar a primeira linha a qual continha o HEADER dos dados. Então me deparei com um código existente que me pareceu muito estranho a princípio, dá uma olhada nisso…

$mapped_headers = $this->map_headers(reset($spreadsheet_rows));

Para quem é familiar a este tipo de processamento sabe que estamos falando de um array bidimensional no melhor estilo linhas x colunas. O que eu não entendi foi aquele reset() ali na invocação do map_headers… que janho!!!

Como de praxe fui debugar e entender como a combinação funcionava… Para o meu espanto, somente agora eu entendi que o comando reset, além de resetar o ponteiro interno do array, retorna também o primeiro elemento do array.

Isto é fantástico!!! Eu sempre me perguntei por que o PHP não tinha um comando first() para pegar o primeiro elemento de um array já que temos o comando current() e o comando end().

Uma alternativa ao reset() seria inverter a array e usar o comando end(), mas acho que não tem necessidade de complicar o código com essa miscelânea pra atingir o mesmo resultado.

$r = array_reverse($y);
$header = end($r);
var_dump($header); 

// array(3) { 
//   ["col1"] => string(1) "A"
//   ["col2"] => string(1) "B"
//   ["col3"] => string(1) "C"
// }

Outra combinação possível seria combinar current() com reset(), mas também seria um desperdício já que o reset já faz o trabalho esperado.

reset($y);
$header = current($y);
var_dump($header); 

// array(3) { 
//   ["col1"] => string(1) "A"
//   ["col2"] => string(1) "B"
//   ["col3"] => string(1) "C"
// }

Outra alternativa segura de pegarmos o primeiro elemento da array seria fatiar o array com array_slice(), lembrando que este comando extrai o elemento como bidimensional preservando o índice da linha. Ainda assim teria que combinar com array_shift() ou array_pop() para pegar o primeiro elemento como array simples.

$first_row = array_slice($y, 0, 1);
var_dump($first_row); 

// array(1) {
//   [0]=>
//   array(3) {
//     ["col1"]=> string(1) "A"
//     ["col2"]=> string(1) "B"
//     ["col3"]=> string(1) "C"
//   }
// }

$header = array_shift($first_row);
var_dump($header); 

// array(3) { 
//   ["col1"] => string(1) "A"
//   ["col2"] => string(1) "B"
//   ["col3"] => string(1) "C"
// }

Pra finalizar uma última alternativa segura que podemos considerar seria utilizar array_values(). Sabemos que a primeira linha do array representam os headers não importa qual é a chave utilizada na indexação destas linhas. Então array_values() garante que o índice da primeira linha seja ZERO através do re-sequenciamento do array sem alterar a ordem de seus elementos.

$y['Z'] = [ 'col1' => 'A', 'col2' => 'B', 'col3' => 'C' ];
$y['X'] = [ 'col1' => 'value1', 'col2' => 'value2', 'col3' => 'value3' ];
$y['S'] = [ 'col1' => 'value4', 'col2' => 'value5', 'col3' => 'value6' ];
$y['J'] = [ 'col1' => 'value7', 'col2' => 'value8', 'col3' => 'value9' ];

$header = array_values($y)[0];
var_dump($header); 

// array(3) { 
//   ["col1"] => string(1) "A"
//   ["col2"] => string(1) "B"
//   ["col3"] => string(1) "C"
// }

Não exploramos outras alternativas como loop foreach() e for() mas nem precisa, com o PHP certamente encontraremos pelo menos mais meia dúzia de outras formas para atingir o mesmo resultado.

Agora convenhamos, o reset() arrasou!

INFORMIX Alterar Múltiplas Linhas e Múltiplas Colunas com SubSelect

UPDATE princing SET
(price, increasepercentage, datenewrate)
=
(SELECT nrpp.increasepercentage * princing.price AS price
      , nrpp.increasepercentage
      , SYSDATE
 FROM newrateperproduct AS nrpp
 WHERE nrpp.productid = pricing.productid
   AND nrpp.increasepercentage IS NOT NULL
)
WHERE EXISTS
(SELECT 1
 FROM newrateperproduct AS nrpp
 WHERE nrpp.productid = pricing.productid
   AND nrpp.increasepercentage IS NOT NULL
);

Minha jornada com bancos de dados iniciou-se com o sensacional INFORMIX, não por que eu queria, mas naquela época tratava-se se um processo de downsizing onde tínhamos que iniciar a desativação de um Mainframe UNISYS. O novo sistema escolhido pela empresa trazia consigo o requisito de utilizar também este banco de dados.

Dentre as inúmeras características, a capacidade de processar update com “subselects” de forma genial com uma sintaxe praticamente ANSII me encheu os olhos e me proporcionou uma evolução fantástica na programação com SQL puro. Cheguei a desenvolver sistemas inteiros eliminando a necessidade de linguagem de programação, somente SHELL para interface com usuário e SQL statements para cálculos e transformações de dados. O update com subselect tomou um importante papel neste caso.

Toda vez que aprendo um banco de dados novo eu acabou invariavelmente procurando saber se ele tem suporte a esta capacidade. Com o MySQL você alcança este resultado com o Update Join, o que não é uma sintaxe tão nativa assim, mas o pessoal da MySQL AB mandou muito bem neste caso, eu até já escrevi um artigo aqui no Blog demonstrando isso.

Explicando um pouco sobre a sintaxe, a grande sacada do INFORMIX é permitir mesmo que sejam postas várias colunas para serem alteradas, quando for mais de uma coluna o requerimento é recobrir as colunas com parênteses e pronto. No subselect então basta fazer o “match” quantitivo das colunas e, é claro o match de tipos. Naturalmente a atribuição das colunas é feita por posicionamento simplesmente.

Outra sacada que você não pode ignorar é que, para não incorrer em erros devemos repetir o subselect na cláusula WHERE do UPDATE para que tenhamos a mesma equivalência de registros tanto subselect quanto no update.

E o que mais? Mais nada!