DatabaseCast 52: Resultado da PodPesquisa 2014

VitrineDatabaseCast52Neste episódio do DatabaseCast, Mauro Pichiliani (Twitter | Blog) e Wagner Crivelini (@wcrivelini) discutem o resultado da PodPesquisa 2014. Neste episódio você vai saber quem faz cosplay de papai noel, qual é o perfil do ouvinte do DatabaseCast, como é estar entre os 100 maiores podcasts do Brasil e como o desvio padrão, variância e outras medidas estatísticas fazem falta.

Não deixe de nos incentivar digitando o seu comentário no final deste artigo, mandando um e-mail para  databasecast@gmail.com, seguindo o nosso twitter @databasecast, vendo informações de bastidores e as músicas do programa no nosso Tumblr e curtindo a nossa página no Facebook e no Google+.

Clique aqui ou na figura abaixo para obter o endereço do feed RSS e assinar o DatabaseCast

feed-rss

Veja no gráfico abaixo a duração e os tempos aproximados de início e fim de cada bloco:

GraficoTamanhoDatabaseCastEpisodio52Veja na tag cloud abaixo a contagem das palavras mais usadas nos emails, comentários e tweets do episódio anterior:

TagCloudEp52Livro Conversando sobre Banco de dados do Mauro Pichiliani (Impresso e PDF, EPUB e MOBI)

cover_front_medium

Você pode comprar a camiseta com estampa fractal Fluxo Matrix e Sonho Fractal diretamente neste link. Veja também:

Links do episódio:




Publicado em DatabaseCast, Podcast, SQL | Com a tag , , , , , , , | Deixar um comentário

Histórias de código fonte

Figura1_badass_programmerO código fonte é um dos artefatos mais comuns durante um projeto de desenvolvimento de software. Assim como uma relíquia, este artefato muitas vezes tem algumas histórias importantes por trás da sua criação, manutenção e uso.

A criação de um código fonte é feita por um processo de codificação de um algoritmo em alguma linguagem de programação ou recurso tecnológico semelhante. Apesar de existirem diferentes formas de geração automática de código fonte, a maioria dos programas ainda é produzida por um ser humano que passa por diversas situações (algumas inusitadas) enquanto programa.

Eu já escrevi um pouco sobre programação e código fonte, inclusive destacando algumas maneiras de como pode ser a programação no futuro, como trabalhar com código legado e até como medir e visualizar a evolução do código fonte, mas nunca abordei algumas histórias por trás deste artefato.

Figura2

As histórias por trás de um código fonte tem um paralelo com algumas histórias de itens peculiares tão bem exploradas em programas transmitidos pelo History Channel, tal como Trato Feito (Pawn Wars), Mestres da Restauração (American Restoration) e Caçadores de Relíquias (American Pickers). De fato, a parte do programa associada com a história dos itens atrai muito a audiência e, na minha opinião, é mais interessante do que a negociação, comercialização e até a precificação do item. Imagino se alguém já pensou em criar um programa de TV (ou canal do YouTube!) falando sobre histórias de algoritmos…

Devido ao fato que um código fonte não é algo tangível e a sua utilização é algo meramente prática, é difícil ter histórias cativantes como os itens apresentados nos programas televisivos. Contudo, existem casos onde algumas histórias são, no mínimo, curiosas.

Um desses casos é o mistério da função escrita em C para cálculo do inverso da raiz quadrada encontrada no código fonte do jogo Quake 3 Arena. Em uma época pré-internet criou-se uma lenda por trás desta história devido a dois fatos curiosos desta função. Em primeiro lugar ela era muito, mas muito rápida em comparação com outras abordagens. E o mais curioso: ela usava uma constante numérica sem justificativa e que foi considerada “magia negra” por muito tempo.

Figura3_John_Carmack

A autoria desta função foi atribuída a John Carmack, um dos criados do Quake 3 Arena, e durante muito tempo houve um debate sobre os detalhes de como este cálculo funcionava. Atualmente já se sabe mais sobre esta função e alguns dos seus “mistérios” já foram resolvidos ou pelo menos clarificados como mostra este link. De qualquer modo, é instigador ver este tipo de arqueologia envolvida com o código fonte.

O que gostaria de destacar neste post é a maneira de encarar o código fonte por meio de sua história, pois se uma determinada história pode aumentar um valor de um item físico (talvez até fazer com que ele se torne único) acredito que uma boa história também pode agregar algum tipo de valor ao código. Talvez esse valor possa ser educacional ou mesmo característico da maneira como se desenvolvia na época, de forma semelhante a como características de uso do metal podem indicar quando uma arma de fogo foi produzida.

Do ponto de vista de dados já até existe uma boa definição de como o histórico da informação é importante, chamada de Data Provenance. Quem trabalha com banco de dados ou está diretamente envolvido com análise forence mais cedo ou mais tarde vai acabar ouvindo falar sobre este assunto.

Por fim, acredito este tópico de histórias relacionadas código fonte é relevante e que, de certa forma, lembra muito aquele antigo ancião que sempre tinha na cabeça uma história para contar sobre alguém, alguma coisa ou algum lugar. Agora é com você leitor: deixe sua história de código fonte nos comentários.



Publicado em Programação | Com a tag , , , , , , , | Deixar um comentário

Experimento com desenvolvedores Android

usability-testingEstou eu uma fase da minha pesquisa que requer a realização de um experimento científico. Depois de estudar bastande sobre este assunto, conversar com pessoas e fazer todo o design do experimento preciso contatar os possíveis interessados em participar do experimento.

Já divulguei um convite de participação em alguns locais, mas vou apresentá-lo novamente aqui. Para resumir: preciso de desenvolvedores na plataforma Android que tenham ao menos seis meses de experiência e que possam ir presencialmente até a sede do iMasters em São Paulo (fica perto do metrô Faria Lima) para uma sessão do experimento de até duas horas. Este experimento será realizado em Novembro/Dezembro de 2014. Quem se interessar pode entrar em contato comigo no e-mail pichiliani@gmail.com

Segue o convite completo:

“Estou realizado um experimento científico de pesquisa do departamento de ciência da computação do ITA (Instituto de Tecnologia da Aeronáutica) para avaliar uma nova maneira de desenvolver aplicações na plataforma Android e, para isso, preciso testar esta nova maneira com desenvolvedores nesta plataforma.

Este experimento funcionará da seguinte maneira: durante um período de no máximo 2 horas você utilizaria a nossa proposta para modificar uma aplicação Android simples. Iremos monitorar seu progresso durante o experimento. Você não será avaliado e o que for produzido não será comercializado.

O experimento será conduzido em São Paulo na sede do iMasters (região perto do metrô Faria Lima). Forneceremos todos os recursos (computador, tablets e internet) para o desenvolvimento durante o experimento. Além de ajudar a evoluir a pesquisa acadêmica aplicada no Brasil, todos que participarem deste experimento vão receber as seguintes recompensas:

•    Uma cópia digital (PDF ou EPUB) do livro “Conversando sobre Banco de dados”  no valor de R$ 10,00;
•    Acesso gratuito ao curso OnLine “Lógica de Programação – Primeiros passos”  no valor de R$ 79,00;
•    Acesso gratuito ao curso OnLine “MySQL para Desenvolvedores – Principal Gerenciador de Banco de Dados” no valor de R$ 79,00;
•    Um voucher de R$ 15,00 em dinheiro;
•    Participação de um sorteio de uma valise para carregar um notebook de valor estimado em R$ 50,00.

Neste momento estamos nos concentrando em desenvolvedores Android (qualquer versão) que já tenham, no mínimo, 6 meses de experiência no desenvolvimento desta plataforma. Não é necessário que você seja estudante ou que já tenha publicado alguma aplicação ou jogo na loja Google Play.

Por favor, considere com carinho este convite. Se você estiver interessado entre em contato conosco para que possamos agendar sua participação em uma data adequada.

Não resposta a este e-mail. O contado deverá feito com o pesquisador Mauro Pichiliani no e-mail pichiliani@gmail.com ou mauro@pichiliani.com.br indicando a vontade de participar no experimento.

Muito obrigado por sua atenção.”


Publicado em Programação | Com a tag , , , , , | Deixar um comentário

DatabaseCast 51: Delphi com banco de dados

VitrineDatabaseCast51

Olá, pessoal! Neste episódio do DatabaseCast Mauro Pichiliani (Twitter | Blog) e Wagner Crivelini (@wcrivelini) programam visualmente em Delphi com o convidado Guintar Pauli (@GuintherPauli). Neste episódio você vai saber como presentear uma criança de 9 anos, quem foi conhecido como VBK, como a geração “sisteminha” se deparou com novas interfaces gráficas e como anda o Delphi nos dias atuais.

Não deixe de nos incentivar digitando o seu comentário no final deste artigo, mandando um e-mail para  databasecast@gmail.com, seguindo o nosso twitter @databasecast, vendo informações de bastidores e as músicas do programa no nosso Tumblr e curtindo a nossa página no Facebook e no Google+.

Clique aqui para obter o endereço do feed RSS e assinar o DatabaseCastVeja no gráfico abaixo a duração e os tempos aproximados de início e fim de cada bloco:

GraficoTamanhoDatabaseCastEpisodio51

Veja na tag cloud abaixo a contagem das palavras mais usadas nos emails, comentários e tweets do episódio anterior:

TagCloudEp51

Livro Conversando sobre Banco de dados do Mauro Pichiliani (Impresso e PDF, EPUB e MOBI)

cover_front_medium

Você pode comprar a camiseta com estampa fractal Fluxo Matrix e Sonho Fractal diretamente neste link. Veja também:

Links do episódio:




Publicado em DatabaseCast, Podcast | Com a tag , , , , , , , , , | Deixar um comentário

Pérolas no código fonte do .NET Framework

Figura1_CapaPostRecentemente a Microsoft liberou o código fonte do .NET Framework. Neste post vou mostrar algumas pérolas e preciosidades que encontrei nos arquivos com o código fonte após uma análise simples.

Desde que lancei o meu curso de introdução à programação tenho estudado, pensado e escrito muito sobre vários aspectos relacionados à programação, design e codificação. Quando vi a notícia que a Microsoft liberou o código fonte do .NET Framework logo tive a ideia de fazer uma pequena análise e ver o que podia descobrir de interessante.

Neste artigo eu mostro uma análise simples de todos os arquivos .CS (de C#) encontrados no GitHub do .NET Reference Source. Logo no começo pude notar que foi difícil encontrar palavras de baixo calão, palavrões e outros xingamentos, o pode significar que: a) alguém fez um serviço de limpeza antes de liberar o código fonte; ou b) os desenvolvedores realmente não quiseram se expressar desta maneira.

Contudo, ainda assim descobri algumas pérolas e curiosidades quando fui analisar os comentários, pois este é o local onde é mais provável encontrar certas, digamos assim, expressões incomuns. Como na na internet the zuera neves ends resolvi expor estas expressões dos programadores e mostrar que eles também são seres humanos como cada um de nós.

É importante destacar que mesmo com estas pérolas pouco se pode afirmar sobre a qualidade ou outros atributos tanto do código fonte como do .NET Framework em si. Bem, vamos os textos mais curiosos que encontrei.

Agora que estamos condenados

Figura2_EstamosCondenadosFonte: Arquivo \mscorlib\system\threading\Tasks\ConcurrentExclusiveSchedulerPair.CS linha 243

Fuja dos produtores zumbificados

Figura3_ZumbiArquivo \System.Core\System\Linq\Parallel\Scheduling\Scheduling.CS linha 52

Desvie da bola!

Figura4_BolaFonte: Arquivo \System.Xml\System\Xml\BinaryXml\XmlBinaryReader.cs linha 626

Ah, aquele legado feio!

Figura5_LegadoFonte: Arquivo \mscorlib\system\reflection\emit\ismwrappercore.cs linha 27

Eu não consigo provar…

Figura6_ProvarFonte: Arquivo \System\services\monitoring\system\diagnostics\EventLogInternal.cs linhas 904 e 1097

Não acho que é o melhor jeito, mas vou deixar assim mesmo

Figura7_MelhorJeitoFonte: Arquivo \mscorlib\system\globalization\compareinfo.cs linha 361

Não entendo essa mudança, mas vou manter a lógica do jeito que está

Figura8_DeixarFonte: Arquivo mscorlib\system\reflection\emit\methodbuilder.cs linha 736   

E para finalizar, o melhor de todos: Eu não vou tentar dar manutenção nisso

Figura9_ManutencaoFonte: Arquivo \mscorlib\system\threading\Tasks\Parallel.cs linha 3421




Publicado em Programação | Com a tag , , , , , , , , , | 17 comentários

Minha palestra no SQLSat 329

MatrixMonitor

No último sábado (25/10/2014) fui até o Rio de Janeiro para ministrar uma palestra no evento SQLSat329. Quem acompanha meus conteúdos sabe que sempre que posso vou a este tipo de evento para compartilhar conhecimento, aprender e encontrar com o pessoal da comunidade.

SQLSat

A minha palestra falou sobre um assunto pouco explorado na área: questões de user interface (UI) e user experience (UX) na ferramenta Management Studio do SQL Server. Acredito que muitas pessoas utilizam este poderoso recurso, mas quem realmente consegue observar alguns problemas nesta interface são poucos.

Para não ficar somente criticando apresentei várias sugestões de como resolver alguns problemas comuns. Talvez somente pelos slides (mostrados abaixo) seja possível compreender muitas das ideias que proponho. Sei que muito do que sugeri pode ser apenas sonho e que há uma probabilidade muito baixa de tais sugestões se tornarem realidade, mas pelo menos eu toquei no assunto e mostrei que ainda há muito a ser melhorado no Management Studio.



Publicado em Apresentação, Evento, SQL | Com a tag , , , , , , , , , , | Deixar um comentário

DatabaseCast 50: Melhores episódios

VitrineDatabaseCast50Neste episódio do DatabaseCast Mauro Pichiliani (Twitter | Blog) e Wagner Crivelini (@wcrivelini) gravam presencialmente pela primeira vez e comentam quais são seus episódios preferidos do DatabaseCast. Neste episódio você vai saber quem é fã de Rock’n’Roll, como uma água pode ser blended, dicas para reduzir seu ego e mandar um alô para possíveis anunciantes.

Não deixe de nos incentivar digitando o seu comentário no final deste artigo, mandando um e-mail para  databasecast@gmail.com, seguindo o nosso twitter @databasecast, vendo informações de bastidores e as músicas do programa no nosso Tumblr e curtindo a nossa página no Facebook e no Google+.

Clique aqui para obter o endereço do feed RSS e assinar o DatabaseCast

Veja no gráfico abaixo a duração e os tempos aproximados de início e fim de cada bloco:

GraficoTamanhoDatabaseCastEpisodio50

Veja na tag cloud abaixo a contagem das palavras mais usadas nos emails, comentários e tweets do episódio anterior:

TagCloudEp50

Livro Conversando sobre Banco de dados do Mauro Pichiliani (Impresso e PDF, EPUB e MOBI)

cover_front_medium

Você pode comprar a camiseta com estampa fractal Fluxo Matrix e Sonho Fractal diretamente neste link. Veja também:

Links do episódio:

Publicado em DatabaseCast, Podcast | Com a tag , , , , , , , | 1 comentário

Top 10 piores instruções SQL

Capa3Já faz algum tempo que trabalho com banco de dados e programação e durante a minha carreira já encontrei muita instrução SQL rui… err estranha. Neste post resolvi listar as 10 piores instruções SQL com as quais já me deparei ou ouvi falar.

Antes de começar a apresentar as instruções é importante dizer que o SQL é uma linguagem de domínio específico e de alto nível. Isso quer dizer que você deve especificar quais dados você deseja obter e não como. Esta característica aliada com os diversos elementos da sintaxe (cláusulas, opções, operadores ect) faz com que o SQL seja muito versátil e adequado para a manipulação de dados. Contudo, muitas pessoas acabam abusando da linguagem e cometendo verdadeiras atrocidades quando acreditam que apenas uma instrução SELECT deve ser escrita para resolver um problema.

Para facilitar a compreensão das instruções SQL eu as formatei utilizando o ótimo SQL Formatter. Desta maneira a instrução fica legível e pode-se ao menos tentar compreender o que ela faz.

10) O mistério das tabela BR

Figura2

A instrução SQL abaixo foi enviada a mim por um leitor dos meus conteúdos em 2004. A dúvida era em relação a um produto cartesiano. Esta preciosidade abaixo contém joins em 5 tabelas, três funções de agregação e nove colunas não agregadas. Outro destaque vai para os nomes pouco explicativos das colunas e dos aliases das tabelas.

SELECT T1.”dt_ciclo”                                         ”c1″,
T2.”cd_categoria”
|| ’ - ’
|| T2.”ds_categoria”                                  ”c2″,
T3.”cd_marca”
|| ’ - ’
|| T3.”ds_marca”                                      ”c3″,
T4.”cd_sku”
|| ’ - ’
|| T4.”ds_sku”                                        ”c4″,
T5.”cd_regiao”                                        ”c5″,
Sum(T1.”qt_caixas_trade”)                             ”c6″,
Sum(T1.”qt_caixas_campo”)                             ”c7″,
Sum(T1.”qt_caixas_campo”) - Sum(T1.”qt_caixas_trade”) ”c9″
FROM   ”BRPI05P”.”tb_estimativa” T1,
“BRPI05P”.”tb_sku_produto” T4,
“BRPI05P”.”tb_categorias_produto” T2,
“BRPI05P”.”tb_marcas_produto” T3,
“BRPI05P”.”tb_regioes” T5
WHERE  T1.”dt_ciclo” = T4.”dt_ciclo”
AND T1.”cd_sku” = T4.”cd_sku”
AND T4.”dt_ciclo” = T2.”dt_ciclo”
AND T4.”cd_categoria” = T2.”cd_categoria”
AND T4.”dt_ciclo” = T3.”dt_ciclo”
AND T4.”cd_categoria” = T3.”cd_categoria”
AND T4.”cd_grupo_produto” = T3.”cd_grupo_produto”
AND T4.”cd_marca” = T3.”cd_marca”
AND T2.”cd_categoria” IN ( ’3′ )
GROUP  BY T1.”dt_ciclo”,
T2.”cd_categoria”
|| ’ - ’
|| T2.”ds_categoria”,
T3.”cd_marca”
|| ’ - ’
|| T3.”ds_marca”,
T4.”cd_sku”
|| ’ - ’
|| T4.”ds_sku”,
T5.”cd_regiao”
ORDER  BY 1 ASC,
2 ASC,
3 ASC,
4 ASC,
5 ASC

9) O ERP. É sempre o ERP

Figura3Instruções SQL incompreensíveis geradas por ERPs são um clássico. Houve uma época que eu até me preocupava com isso, mas atualmente já nem perco o meu tempo. Basta dizer que o nome das tabelas é críptico, há um excesso de colunas desnecessárias, o modelo de dados é confuso e há diversos problemas relacionados à manutenabilidade da instrução, que deve ser analisada por um ser humano e não uma máquina. A instrução SELECT abaixo é um dos exemplos mais ‘simples’ de como queries geradas para trabalhar com modelos de ERP podem ser um pesadelo do ponto de vista de manutenção.

SELECT   d2_grupo                            grupo,
Cast(Sum(d2_total) AS NUMERIC(10,2))total,
pa1_tptrat                          tipo
FROM     sd2tb0 D2,
se1tb0 E1,
pa1tb0 PA1
WHERE    d2_emissao>=’20060201′
AND      d2_emissao<=’20060228′
AND      d2.d_e_l_e_t_<>’*’
AND      pa1.d_e_l_e_t_<>’*’
AND      e1.d_e_l_e_t_<>’*’
AND      d2_cliente>=’      ’
AND      d2_cliente<=’zzzzzz’
AND      Substring(e1_tipo,1,2)=’NF’
AND      e1_contr<>”
AND      d2_loja>=’  ’
AND      d2_loja<=’  ’
AND      e1_emissao=d2_emissao
AND      e1_parcela IN (”,
‘A’)
AND      e1_naturez NOT IN (‘COFINS’,
‘PIS’,
‘CSLL’,
‘INSS’)
AND      e1_num=d2_doc
AND      e1_contr<>”
AND      pa1_contr=e1_contr
AND      d2_filial=’EC’
AND      e1_contr>=’         ’
AND      e1_contr<=’zzzzzzzzz’
AND      sa1->a1_regiao>=’1  ’
AND      sa1->a1_regiao<=’1  ’

GROUP BY pa1_tptrat,
d2_grupo
ORDER BY pa1_tptrat,
d2_grupo

8) Subconsultas. Subconsultas Everywhere

Figura4

O uso de subconsultas em uma instrução SQL é algo que possui um potencial enorme para adicionar complexidade à instrução. Em particular se subconsultas são utilizadas na lista de colunas. A instrução abaixo é um desses exemplos onde provavelmenteo SELECT foi montado por várias pessoas diferentes ou por uma ferramenta, pois fica difícil imaginar alguém que tenha um linha de pensamento tão confusa quanto a instrução, que foi obtida a partir desta thread do StackOverflow.

SELECT ’%c%’                                                AS Chapter,
(SELECT Count(ticket.id) AS Matches
FROM   engine.ticket
INNER JOIN engine.ticket_custom
ON ticket.id = ticket_custom.ticket
WHERE  ticket_custom.NAME = ’chapter’
AND ticket_custom.value LIKE ’%c%’
AND type = ’New material’
AND milestone = ’1.1.12′
AND component NOT LIKE ’internal_engine’
AND ticket.status IN ( ’new’, ’assigned’ ))  AS ’New’,
(SELECT Count(ticket.id) AS Matches
FROM   engine.ticket
INNER JOIN engine.ticket_custom
ON ticket.id = ticket_custom.ticket
WHERE  ticket_custom.NAME = ’chapter’
AND ticket_custom.value LIKE ’%c%’
AND type = ’New material’
AND milestone = ’1.1.12′
AND component NOT LIKE ’internal_engine’
AND ticket.status = ’document_interface’)    AS ’Document\  Interface’,
(SELECT Count(ticket.id) AS Matches
FROM   engine.ticket
INNER JOIN engine.ticket_custom
ON ticket.id = ticket_custom.ticket
WHERE  ticket_custom.NAME = ’chapter’
AND ticket_custom.value LIKE ’%c%’
AND type = ’New material’
AND milestone = ’1.1.12′
AND component NOT LIKE ’internal_engine’
AND ticket.status = ’interface_development’) AS ’Inter\ face Development’
,
(SELECT Count(ticket.id) AS Matches
FROM   engine.ticket
INNER JOIN engine.ticket_custom
ON ticket.id = ticket_custom.ticket
WHERE  ticket_custom.NAME = ’chapter’
AND ticket_custom.value LIKE ’%c%’
AND type = ’New material’
AND milestone = ’1.1.12′
AND component NOT LIKE ’internal_engine’
AND ticket.status = ’interface_check’)       AS ’Interface C\ heck’,
(SELECT Count(ticket.id) AS Matches
FROM   engine.ticket
INNER JOIN engine.ticket_custom
ON ticket.id = ticket_custom.ticket
WHERE  ticket_custom.NAME = ’chapter’
AND ticket_custom.value LIKE ’%c%’
AND type = ’New material’
AND milestone = ’1.1.12′
AND component NOT LIKE ’internal_engine’
AND ticket.status = ’document_routine’)      AS ’Document R\ outine’,
(SELECT Count(ticket.id) AS Matches
FROM   engine.ticket
INNER JOIN engine.ticket_custom
ON ticket.id = ticket_custom.ticket
WHERE  ticket_custom.NAME = ’chapter’
AND ticket_custom.value LIKE ’%c%’
AND type = ’New material’
AND milestone = ’1.1.12′
AND component NOT LIKE ’internal_engine’
AND ticket.status = ’full_development’)      AS ’Full Devel\ opment’,
(SELECT Count(ticket.id) AS Matches
FROM   engine.ticket
INNER JOIN engine.ticket_custom
ON ticket.id = ticket_custom.ticket
WHERE  ticket_custom.NAME = ’chapter’
AND ticket_custom.value LIKE ’%c%’
AND type = ’New material’
AND milestone = ’1.1.12′
AND component NOT LIKE ’internal_engine’
AND ticket.status = ’peer_review_1′)         AS ’Peer Review O\ ne’,
(SELECT Count(ticket.id) AS Matches
FROM   engine.ticket
INNER JOIN engine.ticket_custom
ON ticket.id = ticket_custom.ticket
WHERE  ticket_custom.NAME = ’chapter’
AND ticket_custom.value LIKE ’%c%’
AND type = ’New material’
AND milestone = ’1.1.12′
AND component NOT LIKE ’internal_engine’
AND ticket.status = ’peer_review_2′)         AS ’Peer Review Tw\ o’,
(SELECT Count(ticket.id) AS Matches
FROM   engine.ticket
INNER JOIN engine.ticket_custom
ON ticket.id = ticket_custom.ticket
WHERE  ticket_custom.NAME = ’chapter’
AND ticket_custom.value LIKE ’%c%’
AND type = ’New material’
AND milestone = ’1.1.12′
AND component NOT LIKE ’internal_engine’
AND ticket.status = ’qa’)                    AS ’QA’,
(SELECT Count(ticket.id) AS Matches
FROM   engine.ticket
INNER JOIN engine.ticket_custom
ON ticket.id = ticket_custom.ticket
WHERE  ticket_custom.NAME = ’chapter’
AND ticket_custom.value LIKE ’%c%’
AND type = ’New material’
AND milestone = ’1.1.12′
AND component NOT LIKE ’internal_engine’
AND ticket.status = ’closed’)                AS ’Closed’,
Count(id)                                            AS Total,
ticket.id                                            AS _id
FROM   engine.ticket
INNER JOIN engine.ticket_custom
ON ticket.id = ticket_custom.ticket
WHERE  ticket_custom.NAME = ’chapter’
AND ticket_custom.value LIKE ’%c%’
AND type = ’New material’
AND milestone = ’1.1.12′
AND component NOT LIKE ’internal_engine’

7) O pesadelo outer join

Figura5

Muitas pessoas tem dificuldade de entender outer joins e, por isso, muita gente acaba usando este recurso errado. O Oracle, em particular, possui uma sintaxe muito estranha ( (+) = e = (+) ) para lidar com outer joins, o que pode acabar gerando verdadeiras atrocidades. A instrução abaixo foi obtida a partir de um fórum da Oracle  e o que mais impressiona nem é tanto a instrução, mas o plano de execução insano que foi gerado.

SELECT sid ,
Max(gsid)      gsid ,
Max(firstname) firstname ,
Max(lastname)lastname … <-lines removed FOR sanity … ,
max(manager_name)manager_name
FROM   (
SELECT s1.objectid   sid ,
s1.paramvalue gsid ,
s2.paramvalue firstname ,
s3.paramvalue lastname … <- ines removed FOR sanity …
FROM   (
SELECT objectid,
paramvalue
FROM   param p
WHERE  typeentity=’USER’
AND    paramname=’GSID’
AND    p.startdate =
(
SELECT max(startdate)
FROM   param p1
WHERE  typeentity=’USER’
AND    paramname=’GSID’
AND    p1.objectid = p.objectid )) s1 ,
(
SELECT objectid,
paramvalue
FROM   param p
WHERE  typeentity=’USER’
AND    paramname=’FIRSTNAME’
AND    p.startdate =
(
SELECT max(startdate)
FROM   param p1
WHERE  typeentity=’USER’
AND    paramname=’FIRSTNAME’
AND    p1.objectid = p.objectid )) s2 ,
(
SELECT objectid,
paramvalue
FROM   param p
WHERE  typeentity=’USER’
AND    paramname=’LASTNAME’
AND    p.startdate =
(
SELECT max(startdate)
FROM   param p1
WHERE  typeentity=’USER’
AND    paramname=’LASTNAME’
AND    p1.objectid = p.objectid )) s3 …. <- same thing FOR ALL 22 attibutes ) s22
WHERE  s1.objectid = s2.objectid (+)
AND    s1.objectid= s3.objectid (+)
AND    s1.objectid= s4.objectid (+)
AND    s1.objectid= s5.objectid (+)
AND    s1.objectid= s6.objectid (+)
AND    s1.objectid= s7.objectid (+)
AND    s1.objectid= s8.objectid (+)
AND    s1.objectid= s9.objectid (+)
AND    s1.objectid= s10.objectid (+)
AND    s1.objectid= s11.objectid (+)
AND    s1.objectid= s12.objectid (+)
AND    s1.objectid= s13.objectid (+)
AND    s1.objectid= s14.objectid (+)
AND    s1.objectid= s15.objectid (+)
AND    s1.objectid= s16.objectid (+)
AND    s1.objectid= s17.objectid (+)
AND    s1.objectid= s18.objectid (+)
AND    s1.objectid= s19.objectid (+)
AND    s1.objectid= s20.objectid (+)
AND    s1.objectid= s21.objectid (+)
AND    s21.paramvalue = s22.objectid (+) )alldata GROUP BY alldata.sid

6) Views e joins: você está fazendo isso da maneira errada

Figura6

Eu gosto muito de views quando estou simplificando uma instrução SQL. Elas permitem a abstração de algumas instruções complexas e podem facilitar muito a vida de quem trabalha com SQL. Contudo, muitas pessoas acabam usando este recurso de forma errada. O número 6 desta lista traz uma perigosa  utilização conjunto de uma view com left outer joins no SQL Server, como pode ser visto na instrução abaixo que foi obtida a partir desta thread do StackOverflow onde o usuário que a postou pediu ajuda para reduzir este ‘mostro’.

CREATE VIEW [dbo].[V_BIGGEST_VIEW_EVER]
AS
SELECT {many many columns}
FROM   (SELECT *
FROM   dbo.t_cus_tsk_task
WHERE  is_deleted = 0) T
INNER JOIN dbo.v_cus_grp_group G
ON ( T.group_id = G.group_id )
INNER JOIN dbo.t_bkk_discount_type DT
ON ( DT.discount_type_id = T.discount_type_id )
INNER JOIN dbo.t_bkk_currency DC
ON ( T.debit_currency_id = DC.currency_id )
INNER JOIN dbo.t_bkk_currency PC
ON ( T.payback_currency_id = PC.currency_id )
INNER JOIN dbo.t_bkk_currency FC
ON ( T.final_debit_currency_id = FC.currency_id )
INNER JOIN dbo.t_global_counter D1C
ON ( D1C.company_id = T.company_id
AND D1C.counter_name = ’PROFORMA_INVOICE_COUNTER’ )
INNER JOIN dbo.t_global_counter D2C
ON ( D2C.company_id = T.company_id
AND D2C.counter_name = ’TAX_INVOICE_COUNTER’ )
INNER JOIN dbo.t_global_counter D3C
ON ( D3C.company_id = T.company_id
AND D3C.counter_name = ’INVOICE_RECEIPT_COUNTER’ )
INNER JOIN dbo.t_global_counter D4C
ON ( D4C.company_id = T.company_id
AND D4C.counter_name = ’DELIVERY_NOTE_COUNTER’ )
INNER JOIN dbo.t_global_counter D5C
ON ( D5C.company_id = T.company_id
AND D5C.counter_name = ’BILL_OF_LADING_COUNTER’ )
INNER JOIN dbo.t_global_counter D6C
ON ( D6C.company_id = T.company_id
AND D6C.counter_name = ’CREDIT_INVOICE_COUNTER’ )
LEFT JOIN dbo.v_sys_branch BR
ON ( T.branch_id = BR.branch_id )
LEFT JOIN dbo.t_cus_tsk_tasks_array AR
ON ( T.array_id = AR.array_id )
LEFT JOIN dbo.t_driver D
ON ( T.driver_id = D.driver_id )
LEFT JOIN dbo.t_vehicle V
ON ( T.vehicle_id = V.vehicle_id )
LEFT JOIN dbo.t_stf_inviter I
ON ( T.inviter_id = I.inviter_id )
LEFT JOIN dbo.t_stf_subcontractor SC1
ON ( SC1.subcontractor_id = D.subcontractor_id )
LEFT JOIN dbo.t_stf_subcontractor SC2
ON ( SC2.subcontractor_id = T.subcontractor_id )
LEFT JOIN dbo.t_cus_tsk_task_status S
ON ( S.task_status_id = T.task_status_id )
LEFT JOIN dbo.v_stf_sub_location SL1
ON ( SL1.sub_location_id = T.start_sub_location_id )
LEFT JOIN dbo.v_stf_sub_location SL2
ON ( SL2.sub_location_id = T.end_sub_location_id )
LEFT JOIN dbo.t_stf_customer CU
ON ( CU.customer_id = T.customer_id )
LEFT JOIN dbo.t_stf_customer_splitting_code SP
ON ( SP.splitting_id = T.splitting_id )
LEFT JOIN dbo.v_cus_tsk_credit_for_task CR
ON CR.task_id = T.task_id
LEFT JOIN dbo.t_bkk_proforma_invoice D1
ON ( T.proforma_invoice_id = D1.proforma_invoice_id )
LEFT JOIN dbo.t_bkk_tax_invoice D2
ON ( T.tax_invoice_id = D2.tax_invoice_id )
LEFT JOIN dbo.t_bkk_invoice_receipt D3
ON ( T.invoice_receipt_id = D3.invoice_receipt_id )
LEFT JOIN dbo.t_bkk_delivery_note D4
ON ( T.delivery_note_id = D4.delivery_note_id )
LEFT JOIN dbo.t_bkk_bill_of_lading D5
ON ( T.bill_of_lading_id = D5.bill_of_lading_id )
LEFT JOIN dbo.v_cus_tsk_container CONTAINER1
ON ( CONTAINER1.container_id = T.container1_id )
LEFT JOIN dbo.v_cus_tsk_container CONTAINER2
ON ( CONTAINER2.container_id = T.container2_id )
LEFT JOIN dbo.v_stf_trailer TRAILER1
ON ( TRAILER1.trailer_id = T.trailer1_id )
LEFT JOIN dbo.v_stf_trailer TRAILER2
ON ( TRAILER2.trailer_id = T.trailer2_id )
LEFT JOIN dbo.t_stf_luggage_type LUGGAGE_TYPE
ON ( LUGGAGE_TYPE.luggage_type_id = T.luggage_type_id )




5) Joins e mais joins no WordPress

Figura7descrevi em um artigo algumas características do modelo de dados do WordPress. Infelizmente muita gente que acaba escrevendo instruções SQL para consultar dados no MySQL acaba pecando por fazer muitos joins, como é o caso da instrução abaixo que contém 10 joins  chegando muito próximo do limite máximo recomendado no padrão SQL (que é 16). Esta instrução SQL foi obtida neste post que descreve quais foram os passos tomados pelo desenvolvedor para simplificá-la.

SELECT    p.id,
p.post_content,
p.post_title,
p.post_name,
p.guid,
m1.meta_value AS date,
m2.meta_value AS alt_dates,
m3.meta_value AS days,
t1.name       AS company,
t2.name       AS asset
FROM      $wpdb->posts p
JOIN      $wpdb->postmeta m1
ON        (
p.id = m1.post_id
AND       m1.meta_key = ’date’)
LEFT JOIN $wpdb->postmeta m2
ON        (
p.id = m2.post_id
AND       m2.meta_key = ’alt_dates’)
JOIN      $wpdb->postmeta m3
ON        (
p.id = m3.post_id
AND       m3.meta_key = ’number_days’)
JOIN      $wpdb->term_relationships tr1
ON        p.id = tr1.object_id
JOIN      $wpdb->term_taxonomy tt1
ON        tr1.term_taxonomy_id = tt1.term_taxonomy_id
JOIN      $wpdb->terms t1
ON        tt1.term_id = t1.term_id
JOIN      $wpdb->term_relationships tr2
ON        p.id = tr2.object_id
JOIN      $wpdb->term_taxonomy tt2
ON        tr2.term_taxonomy_id = tt2.term_taxonomy_id
JOIN      $wpdb->terms t2
ON        tt2.term_id = t2.term_id
WHERE     p.post_type = ’companies’
AND       p.post_status = ’publish’
AND       tr1.term_taxonomy_id={$pageinfo['term_ID']}
AND       tt2.parent = {$pageinfo['term_ID']}
AND       m1.meta_value > ’$old_dates’
ORDER BY  date

4) Dado duplicado? Bota o DISTINCT!

Figura8Um erro muito comum em instruções SELECT acontece quando se utiliza outer joins e acaba-se ficando com um conjunto duplicado de linhas. A solução? Bota um DISTINCT! Isso é tão comum que toda a vez que eu analiso uma instrução SELECT e já vejo um DISTINCT logo de cara isso me cheia a uso errado do outer join, como na instrução SELECT abaixo (obtida daqui) que consegue misturar os três: inner join, left outer join e right outer join. E ainda tem estruturas CASE na lista de colunas!

SELECT DISTINCT t_order_main.orderid,
t_order_ship.payment,
t_order_main.orderdate,
t_customer.fullname,
t_customer.country,
t_customer.emailaddress,
t_order_ship.remark,
t_order_remark.remarktxt,
v_ordercountcustomer.ordercount,
v_ordercustomerreturned.orderreturned,
v_ordernotshipped.notshipped,
v_order_inreadytosend.ready,
v_order_neworders.neworder,
Chargeback = CASE
WHEN V_OrderChargeback_2.ip <> ”
AND V_OrderChargeback_2.ip <> ’0′ THEN 1
WHEN v_orderchargeback.emailaddress <> ” THEN 1
WHEN V_OrderChargeback_1.fullname <> ” THEN 1
ELSE 0
END,
Westernunion = CASE
WHEN t_order_ship.payment = ’westernunion’
OR t_order_ship.payment = ’btransfer’
OR t_order_ship.payment = ’cash’ THEN 1
ELSE 0
END,
t_order_main.customerid,
Blacklisted = CASE
WHEN s.street IS NOT NULL THEN 1
WHEN i.ip IS NOT NULL
AND i.ip <> ’0′ THEN 1
WHEN f.fullname IS NOT NULL THEN 1
WHEN t_customer.blacklisted = 1 THEN 1
ELSE 0
END,
CouponID = CASE
WHEN t_order_main.couponid = 0 THEN 0
ELSE 1
END,
t_customer.site
FROM   t_order_main
INNER JOIN t_customer
ON t_order_main.customerid = t_customer.customerid
INNER JOIN t_order_ship
ON t_order_main.orderid = t_order_ship.orderid
LEFT OUTER JOIN v_orderchargeback V_OrderChargeback_1
ON t_customer.fullname = V_OrderChargeback_1.fullname
LEFT OUTER JOIN v_orderchargeback V_OrderChargeback_2
ON t_customer.ip = V_OrderChargeback_2.ip
LEFT OUTER JOIN t_order_remark
ON t_order_main.orderid = t_order_remark.orderid
LEFT OUTER JOIN v_ordercountcustomer
ON t_order_main.customerid = v_ordercountcustomer.customerid
LEFT OUTER JOIN v_ordercustomerreturned
ON t_order_main.customerid =
v_ordercustomerreturned.customerid
LEFT OUTER JOIN v_ordernotshipped
ON t_order_main.customerid = v_ordernotshipped.customerid
LEFT OUTER JOIN v_order_inreadytosend
ON t_order_main.customerid =
v_order_inreadytosend.customerid
LEFT OUTER JOIN v_order_neworders
ON t_order_main.customerid = v_order_neworders.customerid
LEFT OUTER JOIN v_orderchargeback
ON t_customer.emailaddress = v_orderchargeback.emailaddress
LEFT OUTER JOIN v_blacklist s
ON t_customer.street = s.street
LEFT OUTER JOIN v_blacklist i
ON t_customer.ip = i.ip
LEFT OUTER JOIN v_blacklist f
ON t_customer.fullname = f.fullname
WHERE  ( t_order_main.orderstatus = 1 )
ORDER  BY t_order_main.orderid ASC

3) CTE é evil, muito evil.

Figura9

Um CTE (Commom Table Expression) é um recuso do SQL muito mal compreendido por muitas pessoas, especialmente por desenvolvedores que não possuem sólidos conhecimentos de recursividade. Portando, é de se esperar que este recurso leve a instruções SQL confusas, mal escritas e que são muito difíceis de serem compreendidas e reescritas. Este é o caso da instrução abaixo que além de ter um tamanho descomunal ainda usa um query hint (OPTION (MAXDOP 1)).

WITH sys_partitions
AS (SELECT partition_scheme_name = SPS.NAME,
partition_function_name = SPF.NAME,
data_space_id = SPS.data_space_id
FROM   sys.partition_schemes AS SPS
INNER JOIN sys.partition_functions AS SPF
ON SPS.function_id = SPF.function_id),
sys_indexes
AS (SELECT O.[object_id],
I.index_id,
I.is_disabled,
I.fill_factor,– fullness at page level
I.is_padded,– affects upper levels of b-tree
[Type] = Cast(CASE WHEN I.index_id = 1 THEN ’clustered’ WHEN
I.index_id
= 0 THEN
‘heap’
ELSE
‘nonclustered’ END + CASE WHEN I.[ignore_dup_key]
<> 0
THEN
‘, ignore duplicate keys’ ELSE ” END + CASE WHEN
I.is_unique <> 0
THEN
‘, unique’ ELSE ” END + CASE WHEN
I.is_primary_key <>
0
THEN
‘, primary key’
ELSE ” END + CASE WHEN I.has_filter = 1 THEN
‘, filtered’
ELSE ”
END +
CASE
WHEN FI.[object_id] IS NOT NULL THEN ’, fulltext’
ELSE

END AS
VARCHAR(
210)),
[table_name] = O.[name],
[schema] = SC.[name],
[table_created] = O.create_date,
[table_modified] = O.modify_date,
[table_has_quotedident_on] =
Objectproperty(O.object_id, ’IsQuotedIdentOn’)
FROM   sys.indexes AS I
INNER JOIN sys.objects AS O
ON O.[object_id] = I.[object_id]
INNER JOIN sys.schemas AS SC
ON O.[schema_id] = SC.[schema_id]
LEFT OUTER JOIN sys.fulltext_indexes AS FI
ON I.[object_id] = FI.[object_id]
AND I.index_id = FI.unique_index_id
WHERE  O.[type] IN ( ’U', ’V' )),
sys_index_operational_stats
AS (SELECT [object_id],
index_id,
leaf_allocation_count,
– this equates to page splits, and it counts both good and bad
range_scan_count,
singleton_lookup_count,
forwarded_fetch_count,
lob_fetch_in_pages,
lob_fetch_in_bytes,
row_lock_count,
row_lock_wait_count,
row_lock_wait_in_ms,
page_lock_count,
page_lock_wait_count,
page_lock_wait_in_ms,
page_latch_wait_count,
page_io_latch_wait_count
FROM   [sys].[Dm_db_index_operational_stats](Db_id(), NULL, NULL, NULL)
),
sys_index_usage_stats
AS (SELECT o.[object_id],
i.index_id,
index_name = i.[name],
user_seeks = u.user_seeks,
last_user_seek = u.last_user_seek,
last_system_seek = u.last_system_seek,
user_scans = u.user_scans,
last_user_scan = u.last_user_scan,
last_system_scan = u.last_system_scan,
user_lookups = u.user_lookups,
last_user_lookup = u.last_user_lookup,
last_system_lookup = u.last_system_lookup,
user_updates = u.user_updates
FROM   sys.indexes AS I
INNER JOIN sys.objects AS O
ON I.[object_id] = O.[object_id]
INNER JOIN sys.dm_db_index_usage_stats AS U
ON I.[object_id] = U.[object_id]
– Statistics are zeroed during online rebuilds in 2012
AND I.index_id = U.index_id
WHERE  U.database_id = Db_id()
AND O.[type] IN ( ’U', ’V' )),
sys_index_physical_stats
AS (SELECT [object_id],
index_id,
pages = Sum(page_count),
[page_density] = Sum(Round(avg_page_space_used_in_percent, 2)),
page_fragmentation = CASE
WHEN Max(index_type_desc) <> ’HEAP’ THEN
Sum(Round(avg_fragmentation_in_percent, 2
))
ELSE NULL
END,
extent_fragmentation = CASE
WHEN Max(index_type_desc) = ’HEAP’ THEN
Sum(
Round(avg_fragmentation_in_percent, 2))
ELSE NULL
END,
[rows] = Sum(record_count),
fw_records = Sum(forwarded_record_count),
partition_number
FROM   sys.Dm_db_index_physical_stats(Db_id(), NULL, NULL, NULL,
‘SAMPLED’)
WHERE  alloc_unit_type_desc = ’IN_ROW_DATA’
GROUP  BY [object_id],
index_id,
partition_number),
indexspaceused
AS (SELECT [object_id] = I.[object_id],
[index_ID] = I.index_id,
[Size (KB)] = Sum(S.used_page_count) * 8
FROM   sys.dm_db_partition_stats AS S
INNER JOIN sys.indexes AS I
ON S.[object_id] = I.[object_id]
AND S.index_id = I.index_id
INNER JOIN sys.objects AS O
ON O.[object_id] = I.[object_id]
INNER JOIN sys.schemas AS SC
ON O.schema_id = SC.schema_id
WHERE  O.[type] IN ( ’U', ’V' )
GROUP  BY I.[object_id],
I.index_id,
I.NAME,
S.used_page_count,
O.create_date,
O.modify_date,
SC.NAME)
SELECT [Database] = Db_name(Db_id()),
[Schema] = SI.[schema],
[Table] = SI.[table_name],
[Tbl object_id] = Object_id(SI.[schema] + ’.' + SI.[table_name]),
[Tbl Created] = CONVERT(CHAR(10), SI.[table_created], 101),
[Tbl Modified] = CONVERT(CHAR(10), SI.[table_modified], 101),
[Tbl QuotedIdentOn] = SI.[table_has_quotedident_on],
[IX] = Isnull(IX.NAME, ”),
[IX Type] = SI.[type],
[Key Columns] = Isnull(Replace(Replace(Replace((SELECT columnName =
c.NAME +
CASE WHEN
sic.is_descending_key = 0
THEN
‘ ASC’
ELSE
‘ DESC’
END
FROM   [sys].[index_columns] AS sic
INNER JOIN [sys].[columns] AS c
ON c.column_id = sic.column_id
AND c.[object_id] =
sic.[object_id]
WHERE  sic.[object_id] = ix.[object_id]
AND sic.index_id = ix.index_id
AND is_included_column = 0
ORDER  BY sic.index_column_id
FOR xml raw), ’”/><row columnName=”‘, ’, ’),
‘<row columnName=”‘, ”), ’”/>’,
”), ”),
[Included Columns] = Isnull(Replace(Replace(Replace(
(SELECT columnName = c.NAME
FROM   [sys].[index_columns] AS sic
INNER JOIN [sys].[columns] AS c
ON c.column_id =
sic.column_id
AND c.[object_id] =
sic.[object_id]
WHERE  sic.[object_id] = ix.[object_id]
AND sic.index_id = ix.index_id
AND is_included_column = 1
ORDER  BY sic.index_column_id
FOR xml raw), ’”/><row columnName=”‘, ’, ’)
,
‘<row columnName=”‘, ”)
,
‘”/>’, ”), ”),
[Filtered Columns] =
Isnull(
Replace(
Replace(Replace(
Replace(IX.filter_definition, ’]', ”), ’[‘, ”), ’(‘, ”), ’)', ”), ”),
[Reads] = Sum(DUS.user_seeks + DUS.user_scans
+ DUS.user_lookups),
[Seeks] = DUS.user_seeks,
[Scans] = DUS.user_scans,
[Lookups] = DUS.user_lookups,
[Writes] = DUS.user_updates,
[Reads Per Write] = CONVERT(DECIMAL(10, 2), Max(CASE
WHEN DUS.user_updates < 1 THEN
100.00
ELSE 1.00 * ( DUS.user_seeks +
DUS.user_scans
+
DUS.user_lookups ) /
DUS.user_updates
END)),
[Last Read] = CONVERT(CHAR(10), (SELECT Max(v)
FROM   (VALUES (DUS.last_user_seek),
(DUS.last_user_scan),
(DUS.last_user_lookup)) AS value
(v)), 101),
[Last Seek] = DUS.last_user_seek,
[Last Scan] = DUS.last_user_scan,
[Last Lookup] = DUS.last_user_lookup,
[Pages] = FR.pages,
[Page Density] = FR.[page_density],
[Page Fragmentation] = FR.page_fragmentation,
[Rows] = (SELECT Sum(P.[rows])
FROM   [sys].[partitions] AS P
WHERE  P.index_id = IX.index_id
AND IX.[object_id] = P.[object_id]),
[IX Size (KB)] = SU.[size (kb)],
[Last Stats Update] = CONVERT(CHAR(10), Stats_date(IX.[object_id], IX.index_id),
101),
[IX ID] = SI.index_id,
[IX IsDisabled] = SI.is_disabled,
[IX Fill Factor] = SI.fill_factor,
[IX IsPadded] = SI.is_padded,
[Forwarded Records] = FR.fw_records,
[IX Page Splits] = DOS.leaf_allocation_count
FROM   [sys].[indexes] AS IX
INNER JOIN [sys].[objects] AS O
ON IX.[object_id] = O.[object_id]
LEFT OUTER JOIN sys_indexes AS SI
ON O.[object_id] = SI.[object_id]
AND IX.index_id = SI.index_id
LEFT OUTER JOIN sys_index_operational_stats AS DOS
ON DOS.index_id = IX.index_id
AND DOS.[object_id] = IX.[object_id]
LEFT OUTER JOIN sys_index_usage_stats AS DUS
ON IX.NAME = DUS.index_name
AND DUS.[object_id] = O.[object_id]
LEFT OUTER JOIN sys_index_physical_stats AS FR
ON FR.[object_id] = IX.[object_id]
AND fr.index_id = ix.index_id
LEFT OUTER JOIN indexspaceused AS SU
ON FR.[object_id] = SU.[object_id]
AND SU.index_id = FR.index_id
LEFT OUTER JOIN sys_partitions AS PT
ON IX.data_space_id = PT.data_space_id
WHERE  O.is_ms_shipped = 0
AND O.[type] IN ( ’U', ’V' )
GROUP  BY SI.[schema],
SI.table_name,
SI.table_created,
SI.table_modified,
SI.table_has_quotedident_on,
IX.NAME,
SI.[type],
ix.[object_id],
ix.[index_id],
IX.filter_definition,
DUS.user_updates,
FR.pages,
FR.[page_density],
FR.page_fragmentation,
SU.[size (kb)],
SI.index_id,
SI.is_disabled,
SI.fill_factor,
SI.is_padded,
FR.fw_records,
DUS.last_user_seek,
DUS.last_user_scan,
DUS.last_user_lookup,
DOS.leaf_allocation_count,
DUS.user_seeks,
DUS.user_scans,
DUS.user_lookups
ORDER  BY SI.[schema],
SI.table_name,
FR.pages DESC
OPTION (maxdop 1);

2) Acentos em lugares inapropriados

Figura10Muitos modelos de dados contam com nomes de colunas e tabela que contém acentos. Não gosto muito desta prática pois é possível que durante a geração de um script possa haver algum problema na conversão de caracteres. Este SELECT que recebi por um e-mail em 2011 deixa isso bem claro. Destaque para o fato da instrução SELECT ler os dados de uma arquivo do Access ao invés de uma tabela do SQL Server.

SELECT `dados conint`.superintend�ncia                          AS ‘SUP’,

`dados conint`.malha                                      AS ‘MALHA’,

`dados conint`.ger�ncia_sd                               AS ‘GERÊNCIA’,

`dados conint`.p�lo                                      AS ‘PÓLO’,

Max(`dados conint`.munic�pio)                            AS ‘Max CIDADE’

,

`dados conint`.`n doc`                                    AS

‘N CONDIS’,

`dados conint`.`n int`                                    AS ‘N CONINT’,

`dados conint`.in�cio                                    AS ‘DT INÍCIO’

,

`dados conint`.t�rmino                                   AS

‘DT TÉRMINO’,

1                                                         AS ‘INT TOTAL’,

Max(Ccur(dura��o / 60))                                 AS

‘Max DURAÇÃO’,

Abs(dura��o < 420)                                      AS ‘INT < 7H’,

Ccur(( Abs(dura��o < 420) ) * dura��o / 60)           AS ‘TA < 7H’,

Abs(( dura��o >= 420 ) <> ( ( Datevalue(t�rmino) > Datevalue(in�cio)

AND Hour(in�cio) < 19

AND Hour(t�rmino) >= 7 )

OR dura��o >= 1440 ))    AS ‘INT > 7H’,

Ccur(( Abs(( dura��o >= 420 ) <> ( ( Datevalue(t�rmino) >

Datevalue(in�cio)

AND Hour(in�cio) < 19

AND Hour(t�rmino) >= 7 )

OR dura��o >= 1440 )) ) *

dura��o / 60

)                                                         AS ‘TA > 7H’,

Abs(( ( Datevalue(t�rmino) > Datevalue(in�cio)

AND Hour(in�cio) < 19

AND Hour(t�rmino) >= 7 )

OR dura��o >= 1440 ))                            AS ‘INT DORM’,

Ccur(( Abs(( ( Datevalue(t�rmino) > Datevalue(in�cio)

AND Hour(in�cio) < 19

AND Hour(t�rmino) >= 7 )

OR dura��o >= 1440 )) ) * dura��o / 60) AS ‘TA DORM’

FROM   `f:\cemig\tma – novo_gerint_2011.mdb`.`dados conint` `DADOS CONINT`

WHERE  ( `dados conint`.causa = ‘ACIDENTAL’ )

AND ( `dados conint`.`grupo causa` <> ’1/1′

AND `dados conint`.`grupo causa` <> ’4/7′ )

AND ( `dados conint`.`redes/linhas` = ‘REDES MT / BT’ )

GROUP  BY `dados conint`.superintend�ncia,

`dados conint`.malha,

`dados conint`.ger�ncia_sd,

`dados conint`.p�lo,

`dados conint`.`n doc`,

`dados conint`.`n int`,

`dados conint`.in�cio,

`dados conint`.t�rmino,

1,

Abs(dura��o < 420),

Ccur(( Abs(dura��o < 420) ) * dura��o / 60),

Abs(( dura��o >= 420 ) <> ( ( Datevalue(t�rmino) >

Datevalue(in�cio)

AND Hour(in�cio) < 19

AND Hour(t�rmino) >= 7 )

OR dura��o >= 1440 )),

Ccur(( Abs(( dura��o >= 420 ) <> ( ( Datevalue(t�rmino) > Datevalue

(in�cio)

AND Hour(in�cio) < 19

AND Hour(t�rmino) >= 7 )

OR dura��o >= 1440 )) ) *

dura��o / 60

),

Abs(( ( Datevalue(t�rmino) > Datevalue(in�cio)

AND Hour(in�cio) < 19

AND Hour(t�rmino) >= 7 )

OR dura��o >= 1440 )),

Ccur(( Abs(( ( Datevalue(t�rmino) > Datevalue(in�cio)

AND Hour(in�cio) < 19

AND Hour(t�rmino) >= 7 )

OR dura��o >= 1440 )) ) * dura��o / 60),

`dados conint`.dura��o

HAVING ( `dados conint`.dura��o >= 3 )

AND ( `dados conint`.`n doc` = 113353985 )

ORDER  BY `dados conint`.`n doc`,

`dados conint`.`n int`

1) Aquele que não pode ser digitado

Figura11Como o número 1 desta lista fiz questão de indicar aquele que é considerada a pior instrução SQL de todos os tempos. Ela foi postada em um fórum da Oracle  e contém um tamanho ridículo de 26 páginas, 4.826 palavras, 43.624 caracteres, 79 ocorrências da cláusula GROUP BY, 67 ocorrências da palavra FROM, 56 ocorrências do operador =, 54 ocorrências do operador AND dentre muitos outros problemas. Esta query foi gerada por uma ferramenta para uma consulta OLAP (imagina o desempenho dela!), mas mesmo assim não acredito que é algo que sequer deva ser mencionado em algum lugar. Por questões de espaço me nego a coloca-la aqui e recomendo a todos que não percam tempo tentando compreendê-la.

Publicado em Programação, SQL | Com a tag , , , , , , , , , , , | 2 comentários

Usabilidade da urna eletrônica

Figura1_CapaNo último domingo, 5 de outubro de 2014, eu e muitos outros brasileiros fomos às urnas eleger os nossos próximos governantes. Deixando de lado toda a história de festa da democracia, resolvi destacar neste post alguns detalhes que me chamaram à atenção sobre a usabilidade e a experiência de votar na urna eletrônica.

Quem me conhece ou acompanha o conteúdo que produzo na internet já há algum tempo deve se lembrar de que sempre fui um cara de sistemas e desenvolvimento back-end (e banco de dados também). Porém ultimamente venho estudando muito usabilidade e UX (user experience). Não sou um especialista como a colega Talita Pagani, que produz materiais muito bons, mas já consigo observar detalhes importantes.

Figura2_Harware

Neste post não vai falar sobre detalhes do hardware da urna ou dos problemas sobre biometria, fraude, confiabilidade e outros. Vou me ater a características da interface e também a experiência de uso do ponto de vista de sistema. Sei que o principal requisito quando se fala em urna eletrônica é a segurança e o tempo de votação (que deve ser rápido para evitar filas), mas acredito que um pouco mais de atenção à usabilidade pode ajudar muito o processo a ser mais rápido, agradável, menos confuso e melhor em geral. Este posto não é um estudo completo e, inclusive, neste link há algo que já foi escrito sobre o assunto.

De acordo com o resultado das apurações, a urna eletrônica foi utilizada por aproximadamente 150 milhões de brasileiros e isso quer dizer que o público alvo é grande: vai desde jovens até pessoas da terceira idade passando por portadores de necessidades especiais, grávidas, pessoas com dificuldade visual e outras características. Portanto, faz sentido que haja um cuidado especial na UI e UX para atendar a tamanha variação de público independente de qual sistema, tecnologia ou plataforma foi utilizada para a criação e desenvolvimento da urna eletrônica.

Antes de destacar alguns pontos que observei deixo claro que as imagens utilizadas foram obtidas da internet e que não cometi o crime de levar um smartphone ou câmera digital para dentro da cabina de votação.

Figura3_CurvandoO primeiro ponto que destaco diz respeito à ergonomia. Pude notar que em praticamente todas as sessões a posição em que a urna foi colocada dentro da cabina faz com que os eleitores se curvem para poder enxergar a tela e utilizar os botões. A propósito, devido à tecnologia da tela da urna ficou difícil enxergar o que estava na tela considerando o ângulo da cabeça e olhos em relação à tela. Sei que muitos eleitores são cadeirantes (muitos, mas não a maioria) e que a altura média do brasileiro não é muito grande, mas não consigo enxergar um motivo claro para não colocar a urna em uma posição onde a tela fique diretamente em frente aos olhos. É possível que seja por aspectos práticos, mas no meu ponto de vista é perfeitamente possível montar uma cabina de votação que mantenha a privacidade e coloque na urna um pouco mais acima para privilegiar a informação sendo mostrada visualmente ao invés de coloca-la em um local onde é preciso se curvar para utilizá-la.

Figura4_UIO segundo ponto que destaco é em relação à área onde é preciso digitar os números para cada candidato. Esta é o mais importante ponto de interação com o usuário e, infelizmente, me parece que ainda precisa de algumas melhores. Em particular os retângulos que delimitam o local para onde colocar o número ficaram com uma borda muito fina, o que dificulta a visualização e indicação de onde começa e termina cada dígito. De forma semelhante, o cursor (carret) que fica piscando para indicar o local extado do dígito ficou muito sutil e claro, torando a localização e identificação de qual é o dígito atual mais difícil por quem tem alguma dificuldade visual. Eu sugiro colocar bordas mais grossas e evidenciar o cursor, como na figura abaixo.

Figuraa5_MelhoriaAinda seguindo na interface, estranhei muito o posicionamento e organização dos elementos. Do modo como foi feita, na parte superior colocou-se uma mensagem indicando para quem será feito o voto (senador, governador, presidente, etc), logo abaixo na parte esquerda foram colocados os campos para a digitação e na parte direita aparecem as imagens dos candidatos. Na parte inferior ficaram as instruções dos botões.

Figura6_CandidatoO que sugiro aqui é uma melhor organização dos elementos. Por exemplo, é natural que o usuário mova os olhos de cima para baixo da interface e por isso acredito que colocar os campos para digitação centralizados e logo após a indicação do cargo faria com a que a movimentação do olhar seja mais fluida. Desta forma as imagens ficariam na parte de abaixo dando oportunidade para colocar a foto do candidato um pouco mais à esquerda seguida do seu vice (quando for o caso) à direita seguido logo abaixo das informações partidárias. Tal posicionamento faria com que o movimento dos olhos fosse de cima para baixo e da esquerda para direita, ao invés de para cima (nome do cargo), esquerda (local dos campos), direita (foto do candidato) e baixo (foto do vice).

Outro ponto que me chamou à atenção foi a falta de transições entre os elementos. Sei que a urna eletrônica é usada apenas uma ou duas vezes a cada dois anos e que ela não é um sistema operacional ou browser que recebe milhares de interações a cada dia. Mas isso não quer dizer que a UI não possa ser menos amigável. Por exemplo, quando a foto dos candidatos é mostrada ela é praticamente jogada na tela, quebrando a atenção do usuário que está na digitação. Isso fica pior ainda quando o eleitor vota na legenda, ou seja, após dois dígitos uma nova informação aparece na tela. Acredito que efeitos simples e discretos como fade in e fade out, movimentação dos elementos da esquerda para a direita (e vice-versa) tornariam o uso da interface mais fluida, informando melhor o usuário de qual etapa da votação ele está evitando confusões sem comprometer o tempo total de votação e a segurança.

Figura7_FontesAinda falando de layout, destaco a simplicidade das fontes escolhidas e outros elementos visuais como linhas e retângulos. Pelo que vi acredito que a interface tenha sido montada por engenheiros e não por designers e pessoal que trabalha com front-end, pois ela é muito simplista e funcional. Como sugestão gostaria de ver menos cantos retos (mais arredondamento), escolha melhor de caixa alta e caixa baixa, espaçamento e também outras maneiras adequadas de chamar a atenção do eleitor para determinadas partes da interface sem criar distrações desnecessárias.

Figura8_LoadingUm dos elementos que apareceu na interface e que, na minha opinião, poderia ter ficado de fora é a barra de percentual. Me parece que ao final da votação esta barra á mostrada para indicar a gravação das escolhas do eleitor. Atualmente há meio que um consenso que tais barras de percentagem não são a melhor maneira de se indicar que algum processamento está ocorrendo, especialmente em aplicações mobile e páginas Web. De fato, não faz muito sentido informar o eleitor que algum processo está 40% ou 90% completo, uma vez que ele não tem o que fazer com isso alguém de esperar. Aqui acredito que colocar algo mais amigável como um ícone simples para indicar processamento (como as famosas rodinhas) seria mais adequado. Ou mesmo fazer testes para detectar o pior caso de demora e tentar reduzir ao máximo o tempo de espera, pois com o hardware existente acredito que não deve ser tão demorado gravar uma porção de bytes e fazer as devidas verificações…

200Queria destacar também alguns pontos positivos, em particular o formato dos botões grandes e o uso do braile sobre as teclas. Também considero boas ideias os efeitos sonoros, mas fiquei com a impressão que apenas o trim-trim final ficou marcado. Talvez fosse o caso de pensar em algum tipo diferente de som ou efeito audível para o caso de votos em brancos ou nulos. Da maneira que a urna está me parece que o mesmo trim é repetido em vários momentos (confirmar, passar para o próximo candidato, etc) e que isso pode confundir um pouco.

Estes foram apenas alguns pontos que destaquei e que gostaria de fossem ao menos analisados pela equipe responsável pelo desenvolvimento da urna eletrônica. Como disse, não sou um especialista nesta área e considero a urna eletrônica boa (mas não excelente) o que justifica muito o orgulho e a evolução que ela representa para a votação, especialmente se considerarmos como outros países estão neste aspecto.

Aproveito a convido o leitor de post a indicar nos comentários outras observações, melhorias ou até sugestões relevantes à UI e UX da urna eletrônica, uma vez há espaço para melhorias de diversas partes relacionadas à como podemos interagir com este importante instrumento da democracia.

 




Publicado em Uncategorized | Com a tag , , , , , , , , , | 6 comentários

Estudando engenharia de software

FIgura1_SoftwareEngineering

Devido ao meu doutorado ultimamente tenho estudado muito sobre engenharia de software, em particular aspectos relacionados a testes de interface e à parte experimental. Eu sei, é muito complicado e pouco atrativo estudar estes tópicos gerais, principalmente pelo fato que não haverem muitos recursos didáticos. Além disso, estudar engenharia de software sozinho sem fazer parte de um projeto que mostra na prática não é nada recomendável.

Como resultado destes estudos acabei produzindo alguns pocket vídeos para a DevMedia sobre alguns assuntos específicos. Logo abaixo listo e comando rapidamente sobre os vídeos que produzi (é preciso ter a assinatura tipo MVP para acessar o conteúdo) Espero que este material possa ajudar quem está estudando este difícil tópico da área da computação.

Que quer ser notificado e saber um pouco mais sobre este tipo de conteúdo que produzo recomendo me seguir no Twitter (@pichiliani), pois sempre que algum material meu vai para o ar eu posto algo lá.

Figura2_SE

Registro de software: Este vídeo foi produzido junto com o meu irmão que é advogado. Apesar de não ser técnico, ele explica os princípios básicos para registro de software no Brasil.

Métricas de código Java com o PMD:Infelizmente muitos desenvolvedores não possuem o hábito de coletar métricas de software. Neste pocket video mostro como é fácil utilizar a ferramenta PMD para coletar métricas de software muito importantes.

Aplicando TDD o SQL Server:  A mentalidade de testar o SQL com testes unitários por meio de TDD ainda é muito pouco explorada. Por isso resolvi mostrar que dá para fazer isso de forma simples com uma biblioteca para fazer TDD com o código T-SQL utilizado no SQL Server.

Principais livros de Engenharia de Software: A área da engenharia de software está cheia de livros com muitas informações importantes. Neste vídeo mostro quais são os principais livros e comento um ponto sobre o conteúdo e a época em que eles foram lançados.

 Principais dificuldades de programadores: Esta pocket video foi gravada antes do post mais popular deste blog e fala um pouco sobre as pesquisas da área acadêmica que identificam, classificam e estudam as dificuldades envolvidas na tarefa de programar.

Figura2_SEVideos, documentários, entrevistas e filmes sobre engenharia de software: Assim como qualquer outra área da computação, a engenharia de software possui nomes e pessoas muito importantes e que são referências. Contudo, raramente vemos algo além de texto e, por isso, gravei este pocket video citando entrevistas, filmes e outros vídeos que, de alguma forma ou de outra, estão relacionados com  a ES.

Técnicas para entrevista/reunião de coleta de requisitos:Uma entrevista ou reunião é um evento muito importante para um projeto de software e, infelizmente, pouco gente dá a devida atenção. Como notei que existe muito pouco material falando sobre isso resolvi gravar esta pocket vídeo com várias recomendações que podem ajudar quem está na fase inicial de coleta e refinamento de requisitos.

Ferramentas para documentação: Quem trabalha com ES deve se munir de uma grande quantidade de ferramentas. Esta pocket vídeo mostra várias delas, sejam de código livre, versões gratuitas ou PaaS.

Modelando negócios com IDEF0:  O diagrama IDEF0 não é muito conhecido no Brasil, mas ele é um dos mais importantes recursos para desenhar, representar, compreender e explicar como funcionam processos de negócio.



Publicado em Programação, Uncategorized | Com a tag , , , , , , , , | Deixar um comentário