Hoje deparei com um pequeno problema no módulo EmailMessage do Django, eu precisava mandar um email oculto em uma certa funcionalidade, apesar do EmailMessage dar suporte a cópia oculta(BCC), ele definitvamente mostra para os outros destinatários do email quem foi enviado o email oculto(¬¬), dando uma buscadinha no Google encontrei essa snippet, FixedEmailMessage(http://www.djangosnippets.org/snippets/630/), que além de resolver este problema, também tem suporte a CC.

Vai la um exemplo:


FixedEmailMessage('titulo', 'corpo', from_email='ex@exemplo.com', to=['para@exemplo.com'], bcc=['oculto@email.com'], cc=['copiado@exemplo.com']).send()

Referência: http://www.djangosnippets.org/snippets/630/

Indíces em PostgreSQL

Rafael SDM Sierra

Um dos grandes problemas de performances em bancos de dados são os indíces, ou mais precisamente, a falta deles.

Antes de mais nada, o que raios é um índice?

Um índice nada mais é do que um conjunto de tuplas ordenadas, vamos pegar como exemplo a seguinte tabela:

CREATE TABLE usuarios (
id INTEGER,
login VARCHAR(20),
senha VARCHAR(40)
);

Nessa tabela, nenhum índice foi criado, então, vamos inserir alguns registros:

INSERT INTO usuarios (id, login, senha) VALUES (1, 'usuario1', 'senha1');
INSERT INTO usuarios (id, login, senha) VALUES (2, 'usuario2', 'senha2');
INSERT INTO usuarios (id, login, senha) VALUES (3, 'usuario3', 'senha3');
...
INSERT INTO usuarios (id, login, senha) VALUES (300000, 'usuario300000', 'senha300000');

Agora temos 300,000 usuários no nosso banco de dados, uma quantidade pequena, mas já serve ao nosso proposito, vamos fazer um select que nos retorne um usuário qualquer:

=# SELECT login FROM usuarios WHERE login='usuario2';
login
----------
usuario2
(1 row)

Time: 179.047 ms

Se saiu bem….179ms pra encontrar um resultado, agora vamos fazer outro:

=# SELECT login FROM usuarios WHERE login='usuario223432';
login
---------------
usuario223432
(1 row)

Time: 191.386 ms

Hmmm….subiu pra 191, o que aconteceria se eu precisasse pegar o registro número mais alto, tipo 9999999? Teriamos um sério problema certo? Mas porque isso acontece?? Vamos entrar no cerne do PostgreSQL, vou mudar meu SQL:

=# EXPLAIN SELECT login FROM usuarios WHERE login='usuario223432';
QUERY PLAN
------------------------------------------------------------
Seq Scan on usuarios (cost=0.00..5910.00 rows=1 width=13)
Filter: ((login)::text = 'usuario223432'::text)
(2 rows)

Time: 0.792 ms

Repare na primeira linha do resultado, ele está fazendo uma busca sequencial até encontrar o resultado desejado. De forma resumida, isso significa que ele vai comparar cada linha que tiver na tabela até encontrar o que você, desde o primeiro até o último (ou até encontrar), se o seu resultado estiver nos primeiros registros legal, mas se for um dos ultimos bilhões….

Pra resolver esse problema, basta criar um índice na tabela:

=# CREATE INDEX idx_usuarios_login ON usuarios(login);
CREATE INDEX
Time: 4020.891 ms

Agora vamos repetir o mesmo SQL:

=# SELECT login FROM usuarios WHERE login='usuario223432';
login
---------------
usuario223432
(1 row)

Time: 1.528 ms

OMFG!!!! Caiu de 190 pra 1!!!! UM!!!! Preciso falar mais alguma coisa?

“Puxa vida Rafael, você resolveu todos os problemas da minha vida, vou criar indíces em todas os campos de todas as minhas tabelas e em todas as combinações possiveis!!”
Você ta loco??? Se você fizer isso sua performance em modificações (INSERT, UPDATE, DELETE) vai sofrer muito. Isso porque se sempre que você modificar os valores, o PostgreSQL vai precisar reorganizar os indíces pra garantir que a performance no SELECT seja alta.

“Carai, como infernos eu vou saber em que campos criar um índice então?”
Fácil, crie apenas em campos que você use WHERE e ORDER BY, dessa forma você vai manter o equilibrio na performance.

“Pow cara, mas e as chaves primárias e estrangeiras?”
Bom, uma chave primaria naturalmente já é um indíce, porém, uma chave estrangeira não, então, se você tiver um campo que seja uma chave estrangeira para um campo em outra tabela, crie um índice nele também.

Internacionalização de software: i18n

Gean Yoshio

Fiquei um tempo sem colocar nada no blog, mas voltei. Recebi até um comunicado oficial do Rafael que se não colocasse nada no blog, era para pegar minhas coisas e passar no RH (ou era para passar no RH e depois pegar as minhas coisas?).

Introdução
Imagine que seu professor de linguagem C++ (ou outra de sua preferência, mas uma linguagem descente, por favor) lhe passe uma lição de casa: “Faça um MMORPG em 5 idiomas diferentes”.
Simples não? Qualquer um faz um MMORPG em C++ da noite para o dia. O detalhe desta simples aplicação é permitir a utilização ou diversão (já que é um game) em outros idiomas.
Se você pensou em um colocar vários “IFs” ou “CASEs” para cada linha que contenha uma string…
Continue Reading »

Estava Rafael Monteiro, que não é o Rafael SDM aqui da Stiod, mas um amigo que trabalha comigo, utilizando a versão 5 do MySQL em um projeto, onde era necessário retornar a diferença entre datas de uma tabela. Até ai tudo bem, ele estava utilizando o datediff, cuja definição da documentação[1] fala:

- “DATEDIFF() retorna o número de dias entre a data inicial expr e a data final expr2. expr e expr2 são expressões de datas ou data e hora. Apenas a parte da data dos valores são usados no cálculo.”

Dando como exemplo:

mysql> SELECT DATEDIFF('1997-12-31 23:59:59','1997-12-30');
-> 1

Porém quando chegou na hora de colocar o site em produção no ambiente disponibilizado pelo cliente, tinhamos apenas a versão 4.0 do MySQL, que não suporta o comando DATEDIFF.
Uma alternativa que encontramos para isto foi converter as datas para unix_timestamp e dividir a diferença pelo número segundos de um dia (86400)

select floor((unix_timestamp(data_final) - unix_timestamp(data_inicial)) / 86400) as diferenca from tabela;

É isso ae.. Rafael Monteiro e seus códigos elegantes… xD

[1] http://dev.mysql.com/doc/refman/4.1/pt/date-and-time-functions.html