MySQL Contando Registros com Múltiplas Condições

SELECT COUNT(id) AS count_total
     , SUM(IF(message = 'content1',1,0)) AS count_message1
     , SUM(CASE 
              WHEN message LIKE 'content1' THEN 1 ELSE 0
           END) AS count_message1a
FROM mytable;

Sempre que precisamos observar e analisar dados podemos fazer uso de comandos mais avançados de SQL.

É claro que este meu exemplo está longe de ser um SQL complexo, mas sempre é bom deixar registrado as possibilidades a partir de um ponto simples também.

Neste caso estou fazendo uma contagem total de registros da tabela com o COUNT(id) simples, como nenhuma condição foi posta no WHERE serão contados todos os registros.

Então quero comparar lado a lado este total de registros com o total de mensagens do tipo 1. Para obter esta contagem parcial precisamos combinar o SUM() com um IF() interno condicionando a contagem apenas para as mensagens com o conteúdo que combinem com o que eu quero.

Opcionalmente ao IF() podemos utilizar a instrução CASE(); o resultado será o mesmo, apenas estamos utilizando uma sintaxe diferente.

É claro que a instrução CASE() vai ter melhor aplicabilidade em determinadas situações, assim como o IF() também tem sua aplicação para funcionalidades de menor complexidade.

Fica a seu critério qual sintaxe aplicar; este é só o ponta-pé inicial na utilização de SQL para análise de dados.

Souriez s’il vous plait!

MySQL Resetar Sequência AutoIncrement Sem Informar Próxima Sequência

-- first, remove auto-increment attribute if it still exists
ALTER TABLE `mytable` MODIFY COLUMN `id` INT(10) UNSIGNED;

-- second, add auto-increment attribute back
ALTER TABLE `mytable` MODIFY COLUMN `id` INT(10) UNSIGNED AUTO_INCREMENT;

Recentemente enfrentei um problema de deixar os cabelos em pé onde uma das tabelas acabou perdendo o atributo auto-increment durante uma operação de movimentação na nuvem.

Bom, o deixar os cabelos em pé foi por que demorei muito para descobrir a ocorrência deste evento de perda do atributo, porém a correção do schema é bastante simples.

Apesar do meu caso se tratar de uma tabela de pouca movimentação eu quis utilizar uma solução que encontrei durante minhas buscas. Achei esta solução bastante interessante pelo fato de não ser preciso atribuir o valor do auto-increment com um HARD SET utilizando o MAX VALUE da coluna id correntemente na tabela.

Ao invés disto, a operação consiste em remover o atributo auto-increment do campo e simplesmente adicioná-lo novamente. Durante esta operação o MySQL realiza um auto-reset atribuindo o valor correto de acordo com os dados existentes na tabela.

No meu caso o atributo auto-increment já tinha sido removido em outra operação, então foi só executar o segundo comando mesmo para adicionar o auto-increment de volta a coluna id.

Caso você queira simplesmente resetar a sequência auto-increment utilizando os comandos mais tradicionais você poderia utilizar, por exemplo, os comandos abaixo.

SELECT MAX(id)+1 INTO @next_id FROM `mytable`;

ALTER TABLE `mytable` AUTO_INCREMENT = @next_id;

Prontinho… Case Closed!

MySQL Restaurar um Arquivo de Backup exportado pelo MySQLDump

mysql -u <my-user> -p <dbname-wanted-to-restore> < file-data-dump.sql
(password will be asked later)

Esse aqui parece simples, só que é muito fácil de esquecer quando não se usa com frequência.

Acontece que o comando para realizar a exportação de dados do MySQL é bem intuitivo, se chama mysqldump.

Só que para restaurar não tem um mysqlrestore não, o que se usa é o mesmo comando local mysql client. E é um saco ter que lembrar disso ou então ter que pesquisar nos longos sites tutoriais ou manuais técnicos.

Agora taí… mysqldump para exportar e mysql para restaurar.

MySQL Montar Script SQL CREATE TABLE a partir de uma Tabela Existente

SHOW CREATE TABLE usuario;

Eu sempre costumo escrever meus scripts SQL para criação de tabelas a partir do zero, normalmente utilizando alguma ferramenta de diagramação como DBDesigner por exemplo.

Mas trabalhando em um projeto recente minha tarefa era transferir algumas tabelas de um determinado banco de dados para outro, assim o SCHEMA a ser criado deveria ser exatamente o mesmo incluíndo os índices para evitar qualquer tipo de impacto na aplicação.

Ai que medo de digitar errado!!!

Pesquisando rapidamente encontrei artigos orientando o uso de um comando específico pra isso. Em um só comando o script para criação do SCHEMA sem dados incluíndo chaves primárias, chaves estrangeiras, chaves únicas e índices, e ainda atributos de CHARTSET e outros penduricalhos.

É claro que você consegue chegar ao mesmo resultado por outros meios como utilizando o MYSQLDUMP sem dados de uma única tabela, mas este comando aqui já faz tudo isso sem maiores complicações.

Enjoy Your Day!

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!

MySQL Update Join para Alterar Múltiplas Linhas de uma só vez

// explicit inner join
UPDATE product AS p
INNER JOIN product_price AS pp ON (pp.product_id = p.product_id)
SET pp.old_price = p.current_price,
    p.current_price = pp.base_price * 0.8
WHERE p.created_at < '2021-01-01';

// implicit inner join
UPDATE product AS p, product_price AS pp
SET pp.old_price = p.current_price,
    p.current_price = pp.base_price * 0.8
WHERE pp.product_id = p.product_id
p.created_at < '2021-01-01';

// explicit left join, only left side is updatable
UPDATE product AS p
LEFT JOIN product_price AS pp ON (pp.product_id = p.product_id)
SET p.deleted = 1
WHERE p.created_at < '2021-01-01';

Toda vez que eu estudo um banco de dados novo uma das coisas que sempre busco em todos eles é entender como escrever a sintaxe de um update em massa apenas relacionando tabelas sem a necessidade de laços for ou foreach bastante comuns em Stored Procedures. Esta preferência é porque a performance de uma instrução SQL é amplamente superior a execução de um laço de programação.

Eu peguei esta mania por que quando iniciei na linguagem SQL com banco de dados Informix, este banco de dados possuía uma sintaxe fora de serie para fazer update com dados entre tabelas. A sintaxe era bastante comum e intuitiva que permitia a alteração de diversas colunas e diversas linhas num mesmo comando.

Como o pessoal da MySQL AB também era fora de série eles implementaram uma funcionalidade chamada de Cross Table Update, que nada mais é que um update que permite join com outras tabelas. Resumindo, esta funcionalidade do MySQL preenche com perfeição o comportamento que eu conseguia executar com o Informix.

Outra forma de se fazer update em massa é utilizando-se sub-selects, porém as instruções do update com join são infinitamente mais simples de serem escritas e também a performance com o update join é bem melhor.

E para que não finquem dúvidas sobre a autoria deste recurso, esta funcionalidade foi disponibilizada desde a versão 4.0 do MySQL.

Uma salva de palma pra eles… genial!

MySQL Criando banco de dados e liberando acesso para usuário

cd /mariadb/bin
mysql -u root -p

CREATE DATABASE mydb;
GRANT ALL ON mydb.* TO myuser@localhost IDENTIFIED BY 'mypassword';
FLUSH PRIVILEGES;

quit

Atualmente é muito comum que os desenvolvedores necessitem lidar com uma infinidade de tecnologias dentre elas instalação e configuração de banco de dados.

Até aqui tudo bem, só que não… o mais corriqueiro é que depois de instalarmos o MySQL passemos a utilizar a conexão com o usuário root e pior, sem senha.

E quem já não tentou liberar o acesso para usuário com o PHPMYADMIN e acabou se frustando por que não funcionou corretamente?

Ao criar um novo banco de dados podemos imediatamente liberar o acesso para um usuário específico evitando assim o acesso com o infame root user.

Os comandos acima são praticamente intuitivos, vou apenas reforçar que a liberação foi feita para o usuário myuser provenientes do ambiente localhost, ou seja conexão interna.

Para liberar acesso mais genérico poderíamos utilizar um asterisco (*) no lugar de localhost, assim estaríamos liberando o acesso remoto a partir de outros hosts.

Agora você já sabe, nada de usar o usuário root sem senha.

MySQL Comparando colunas Case Insensitive diferenciando maiúsculas e minúsculas

SELECT *
FROM tabela_nome
WHERE (coluna_nome COLLATE latin1_general_cs) = 'TexTo';

O MySQL tem um recurso que acho muito show que é com muita facilidade configurar o recurso COLLATE CI (case insensitive) nas colunas texto, o que facilita muito a busca de informações pois não precisamos nos preocupar com maiúsculas e minúsculas no momento das pesquisas.

Mas se em algum momento por alguma razão precisamos fazer uma pesquisa fazendo distinção de maiúsculas e minúsculas?

Neste caso podemos utilizar o recurso COLLATE associando dinamicamente a coluna que queremos fazer a comparação do conteúdo.

Neste exemplo estamos claramente solicitando que o banco de dados faça a consulta comparando o conteúdo da coluna coluna_nome epsis litteris a string “TexTo” discriminando exatamente as maiúsculas e minúsculas.

Eu entendo que muitos profissionais preferem que o banco de dados tenha o comportamento CASE SENSITIVE nativamente como no caso do ORACLE e PostGre, mas o mundo web tem destas facilidades que o MySQL incorporou na gente.