Blog do Neis

Fevereiro 28, 2008

MySQL Truncate

Arquivado em: sql — danielneis @ 2:18 pm

Olá!!!

[Um dos meus trabalhos atuais](http://www.ead.ufsc.br) envolve passar um bom tempo na frente do shell do MySQL, testando e re-testando consultas para sincronizar dois bancos de dados distintos.
Ontem mesmo fui remover todos os campos de uma tabela, para re-testar uma dessas consultas e fui aconselhado pelo meu parceiro de trabalho a usar o `TRUNCATE` ao invés do `DELETE`. Na hora ele não soube me dizer exatamente qual a diferença entre os dois, exceto que o `TRUNCATE` fazia uma série de limpezas a mais.
Hoje fui ver o [manual](http://dev.mysql.com/doc/refman/5.0/en/truncate.html) pra ver o que realmente muda.
Exceto nas tabelas [InnoDB](http://www.innodb.com/), o `TRUNCATE` faz o seguinte:

* Remove e re-cria a tabela
* Não é seguro em transações: se você está no meio de uma transação ou de um `table lock`, receberá um erro
* Não retorna o número de linhas removidas
* Se você tem um arquivo `tbl_name.frm` válido, a tabela poderá ser recriada vazia mesmo que os dados estejam corrompidos
* O gerenciador de tabelas não se lembrará do último valor `AUTO_INCREMENT` usado. Ou seja, as novas inserções começarão do início novamente. (Essa vale tanto pra **MyISAM** quanto para **InnoDB**)
* Como o `TRUNCATE` não usa o `DELETE` os gatilhos(triggers) `ON DELETE` não serão acionados.

É isso aí… realmente tudo o que eu queria para limpar as tabelas e continuar testando em paz…

Fevereiro 8, 2008

Coisas que acontecem #2: SQL – índices: vale ou não?

Arquivado em: coisasqueacontecem, sql — danielneis @ 9:05 pm

**Atenção: Este post contém um pensamento sem solução. Talvez você ache algo aqui que lhe ajude, mas a idéia principal
do texto abaixo é apenas mapear um questionamento que ainda não solucionei. Portanto, leia por sua conta e risco.
Se você tiver algo para contribuir, desde indicar partes que não ficaram claras até responder todas as dúvidas contidas
aqui, superficial ou aprofundadamente, fique a vontade para usar os comentários.**

Estava eu aqui filosofando um pouco sobre banco de dados, repassando os últimos acontecimentos que me proporcionaram algum
aprendizado sobre SQL e pensei no seguinte:

Digamos que exista uma tabela com os campos `config, value, usr`.
Dessa forma se forem feitas pesquisas do seguinte estilo:

SELECT usr
FROM tabela
WHERE (config = ‘a’ AND value > 34)
AND (config = ‘b’ AND value < 48)
OR (config = ‘d’ AND value between 10 AND 30)
AND (config = ‘c’ AND value = 53)
OR (config = ‘e’ AND value != 30)
…..

É verdade que, no caso específico desse tipo de consulta, gerar índices para `config` e `value` é algo que faz sentido
no que diz respeito a otimização?
Seria algo como não precisar varrer a tabela inteira na hora de pesquisar os caras que tem `config` ‘a’,'b’,e/ou ‘c’
e assim por diante, não?
Afinal, os índices podem ser vistos como a mesma tabela,
ordenada pelo campo em questão; desse modo é possível, ao menos, fazer busca binária; certo ?
Isso facilitaria na hora de separar os conjuntos para depois fazer a interseção.
Mas talvez isso só valha realmente apena, ou mostre-se menos trabalhoso do que a própria consulta,
apenas para um dado número `config * user`.
Isso para poder calcular a complexidade do trabalho.

Na prática, se tivermos o seguinte:

select avg(value), max(value), min(value), stddev(value)
from (select count(*) value, usr
from formcidadesum_answers
group by usr
) as value
+————+————+————+—————+
| avg(value) | max(value) | min(value) | stddev(value) |
+————+————+————+—————+
| 10.3687 | 17 | 9 | 1.6727 |
+————+————+————+—————+

Ou seja:
uma média de 10 `config` por `usr` (lembrando que cada a cardinalidade entre `usr` e `config` config é

[m, n] / 0 <= m <= count(usr) && 0 <= m <= count(config)

Teremos, então, no caso da tabela acima, com 6000 usr, a tabela proposta no início teria cerca de 62200 registros.
Ou seja, cada índice teria que ordenar 62200 registros.
Será que, para essa quantidade, o trabalho gasto para indexação já é superado pela economia nesse tipo de consulta?

MySQL e porcentagens

Arquivado em: sql — danielneis @ 6:43 pm

Opa!
Acho que quase deixei isso aqui morrer…
Mas sabem como é: férias das aulas, ano novo, carnaval, dando um jeito de ganhar dinheiro (agora em mais de um
lugar ao mesmo tempo).
Bom, chega de churumelas. Na verdade vim passar por aqui rapidinho só pra deixar uma dica de MySQL.
Hoje, [em um dos meus estágios](http://www.sead.ufsc.br), estava fazendo uns levantamentos de inscrições e precisava,
dentre outras coisas, “quantos porcento dos inscritos está em cada grupo”.
Bom, na verdade a consulta começou simples, só usando o `COUNT(*)` para saber quantos camaradas estavam cadastrados
em cada grupo.

Então segui assim (metaforicamente):

SELECT count(id) as total,
if(eh_uma_pessoa_boa = 1, “sim”, “nao”) as pessoas_boas
FROM pessoas
GROUP BY pessoas_boas;

E me resultou algo como isso:

+——-+———————–+
| total | pesoas_boas |
+——-+———————–+
| 69 | nao |
| 33 | sim |
+——-+———————–+

Ah! Mas isso é fácil. E agora, como faço a porcentagem?
Pegamos cada parte, dividimos pelo total e multiplicamos por 100. Certo. Mas e em SQL, como expressar isso?
Foi aí que, antes de me desesperar, fui à internet e achei
[essa dica](http://forums.mysql.com/read.php?52,134684,134741#msg-134741) que, abaixo, reproduzo o trecho mais importante:

SELECT foo.c, COUNT(*) * t.factor AS pct
FROM foo
JOIN (SELECT 100/COUNT(*) AS factor FROM foo) AS t
GROUP BY foo.c;

Opa! Então bastava fazer um JOIN para pegar o número de usuários total, aplicar alguma matemática e voilá!
Voltando à minha situação prático-metafórica, temos:

SELECT count(id_candidato) * t.factor AS pct, eh_uma_pessoa_boa
FROM pessoas
JOIN (SELECT 100/COUNT(*) AS factor FROM pessoas) AS t
GROUP BY eh_uma_pessoa_boa;

E recebi a seguinte resposta:

+———+———————–+
| pct | eh_uma_pessoa_boa |
+———+———————–+
| 67.6476 | 0 |
| 32.3532 | 1 |
+———+———————–+

Fácil, não?
SQL é o que há…

Blog no WordPress.com.