SQL Server 2012 – Cláusula CONCAT() e o que isso significa pra galáxia

golphin

Bom dia. Post rápido, de nível básico pra não deixar água parada 🙂

Vários Sistemas Gerenciadores de Bancos de Dados (ou DBMS, no original) utilizam, pra concatenar, ou seja, juntar textos, pipes (o símbolo  –> | <–)  sendo que o padrão ANSI-92 prevê dois pipes como concatenação (mas não é obrigado a ser seguido tanto que o SQL Server não o faz) ou a função CONCAT(), que é bastante popular no Oracle e no MySQL. SQL Server, por sua vez, sempre concatenou os textos com o símbolo + (plus, conhecimento popularmente como MAIS).

Felizmente, assim como de costume em cada release, novos comandos (ou velhos, dependendo do ponto de vista) são adicionados no SQL Server de modo a facilitar a portabilidade entre sistemas. Aliás, features assim eu já vejo de cara como tentativa da MS de justificar migrações de código, deixando-as menos penosas. Mas bem, adivinha quem entrou como alternativa pra concatenações no SQL Server?

THIS GUY  ————> link

Mas essa ainda não é a maior vantagem do CONCAT sobre o método tradicional. Observe que todos os envolvidos na concatenação eram tipos textuais. Mas e nos casos comuns, onde geralmente temos que concatenar texto com números?

Msg 245, Level 16, State 1, Line 11
Conversion failed when converting the varchar value ‘SQL SERVER ‘ to data type int.

A mensagem é clara demais…Ele tenta concatenar um número com texto e falha. Pra que o código acima rodasse tranquilamente, era necessário um CAST ou CONVERT na variável @release para um tipo textual, sendo possível assim realizar a concatenação. Exemplo:

Significa que quando recebermos a tarefa de migrar um script do MySQL ou Oracle pra SQL Server por exemplo, teremos menos uma dor de cabeça em relação a cláusula de concatenação, já que a função agora é a mesma. Parece uma mudança irrelevante e sem graça, e de fato, perto do que o 2012 traz, não é aquela senhora feature, mas achei um passo até notável. Alguns pontos:

1) Evite sempre que possível o uso de extensões proprietárias. Prefira sempre comandos ANSI e se não houver, algo de sintaxe similar ou que seja comum a outros bancos. Isso facilita o reuso, migrações de código entre plataformas. SEMPRE pense nisso quando for desenvolver algo em SQL.

2) Concatenação geralmente é um recurso bastante presente em códigos legados. Não precisar portar boa (senão toda) parte dele é economia de tempo. Isso tem impacto sobre sua vida, sua produtividade e na vida dos ursos no polo norte.

3) SQL Server cada vez mais se torna amigável adaptando em si alguns recursos que são usados de modo frequente. Vejo isso como uma oportunidade  de desmistificar pra profissionais que mexem com outros sistemas que MSSQL é algo de outro mundo (sendo que nem é e possui inclusive um dos dialetos (T-SQL) mais fáceis entre os disponíveis). O uso destes recursos pelo desenvolvedor possibilita a propagação dessa idéia. ;

4) Desenvolvedores (T)-SQL fazem com que o código mais universal  reduzam o impacto de meteoritos para longe da órbita do planeta Migrations;

Enfim, recomendo que você use CONCAT() sempre que possível partindo da versão SQL Server 2012 se não houver envolvimento com o legado, é claro.

[]’s

Reposta do Desafio #1 T-SQL, Ranking Functions (ROW_NUMBER, RANK E DENSE_RANK)

Resposta do desafio

Bom dia pessoal 🙂

Semana passada eu disse que postaria a resposta do desafio #1 T-SQL no começo da semana. Andei ocupado, só deu pra terminar agora.

Pra relembrar, o desafio foi este: http://radialog.wordpress.com/2013/07/12/checkpoint-pessoal-desafio-1-t-sql/.

Precisamos obter os segundos maiores salários de cada departamento. O desafio praticamente disse, com todas as letras (pelo menos a solução mais óbvia) foi que devemos particionar por departamentos e  rankear pelo segundo maior salário.

A resolução mais óbvia se encontra no que chamados de Ranking Functions, informação que você encontra aqui nesse link do MSDN. De cara, a questão sugere que façamos uma classificação…. A minha solução (a mais intuitiva possível) foi utilizando CTE (Common Table Expression).

A questão anterior trabalha com uma variável de tabela. Para fins de prática e para entendimento das funcionalidades, execute o script abaixo que é o mesmo código do desafio, porém, materializado para tabelas físicas, no tempdb.

Ok, temos a tabela populada. Note que em um SELECT simples, a tabela está ordernada por departamento, algo já pensando na inserção dos dados no início deste exercício. Como poderíamos tentar classificar as linhas? Vamos tentar um ROW_NUMBER() ?

Que trará o seguinte resultado:

Explicando o que a linha ROW_NUMBER() fez: Particionou por departamento e ordernou por salário. Falando de modo mais fácil,  ele vai numerar as linhas de acordo com o departamento, e não se baseando na tabela inteira (por isso a partição por departamento). Essa foi a forma MAIS PRÓXIMA que se dá pra chegar no desafio, claro, utilizando um r0w_number() sem maiores firulas. Porque mais próxima e não a correta? Bem, observe as linhas em negrito e lembre-se que se existirem dois maiores salários, os dois devem ser mostrados. Vejam que existe empate do que  questão pede, que é o “segundo maior salário” entre diversos empregados. 

O row_number() não leva em consideração empate, então, não é a melhor função pra resolver o problema. Observe:

E o que o select traz pra gente?

Ele trouxe corretamente o que foi proposto sintaticamente falando: as segundas linhas (já que existe ordernação do maior salário ao menor) de cada deparmento. Mas os nossos amigos negritados não apareceram por completo devido ao empate, logo, row_number (em tradução simples, número da linha) não atende o desafio proposto.

NTILE() é uma cláusula que não serve também para resolver o desafio de forma simples. Não vou realizar o exemplo pro post não ficar muito grande.

Agora sim, temos RANK e DENSE_RANK que fazem exatamente o que o exerício pede, porém com uma sutil diferença… Vamos utilizar a mesma lógica de particionamento por departamento e ordenação por salário…

Olha o resultado:

É exatamente o que estamos precisando! Se formos realizar uma consulta filtrando as colunas “RANK” ou “RANK_DENSE” (usando uma CTE, foi a forma mais intuitiva que achei) ele trará o resultado correto da resposta. Porém, como disse antes, existe uma diferença sutil que faz a diferença entre “O certo” e o “O mais certo”. Não fez muito sentido o que eu disse? É justamente a palavra DENSE do rank. Se tivesse ali entre os dados DOIS “primeiros lugares” (sim amigos, um empate em um departamento dos primeiros maiores salários) o resultado certamente sairia diferente! Isso considerando dois, imagina três ou quatro…. Pra ilustrar melhor o que eu disse, vamos aos scripts:

Fazendo o teste…

Olha só o resultado!

Observe os empregados “S Martin” e “J Garcia”. Eles possuem os segundos maiores salários de seus departamentos, mas comparando os números de RANK() e DENSE_RANK() obtemos resultados diferentes entre si. A razão pra isso é muito simples: RANK() classificou em primeiro lugar dois itens. Como é natureza dessa função considerar os itens que estão agrupados em certa classificação, os teoricamente “segundo colocados” na verdade estão em terceiros, já que houve um empate entre os dois primeiros (então, como não foi dado nenhum outro critério de desempate, os dois ocupam o mesmo rank porém “tiram” a vaga dos próximos que virão).
DENSE_RANK() não faz isso. Ele considera uma classificação consecultiva, sem pular posições (por isso é “denso”).
Logo, a resposta mais correta seria o código abaixo utilizando dense_rank():

Resultado:

Pra ficar bonito o código sem esse rank_dense, você pode tirá-lo do SELECT pra ficar idêntico a resposta desejada. Deixei ele aqui só pra mostrar que era isso o que a questão queria: os segundos maiores salários de cada departamento, segundo que se houver empate entre os “segundo maior”, que todos os envolvidos sejam listados.

A resposta completa já estava no outro post neste link  aqui. Pra mim, a explicação daquele código já é suficiente para entender um pouco do problema, mas eu queria explorá-lo mais e pra isso, fiz o post.

Você conseguiu encontrar a solução? Utilizou outra forma? Fique a vontade para comentar. Agora vou dormir porque o dia é longo 🙂

Desafio #1 T-SQL

Divagação sobre momento + desafio TSQL

Bom dia!

Vi no blog do Vitor Fava (http://vfava.wordpress.com/) uma recomendação de sites com desafios de T-SQL.
Perfeita ocasião pra voltar a dar mais uma revisada na codagem!
Decidi então trazer alguns deles pra cá por dois motivos:

1) Incentivar você, leitor (se tiver afim é claro) de praticar (e reforçar o que você já sabe);
2) Transmitir uma forma de pensar diferente ou nova de lógica;

O crédito pelas questões é do site http://beyondrelational.com/, que é bem legal, e recomendo, pra quem curte desafios que visite sem medo de ser feliz (a não ser que você já seja um coder-monster, aí no caso você iria só pra brincar mesmo).

Toda semana, por períodos irregulares, irei trazer os desafios que eu acho interessante e postar aqui.
Quem quiser postar uma solução, pode fazê-lo no site ou aqui mesmo, pra gente compartilhar pontos de vista diferentes. Eu postarei a minha solução comentada sobre o exercício em outro  post pra não alongar esse aqui demais. Provavelmente esse final de semana! Já tá pronto aqui, só aguardando a oportunidade 🙂

Vamos ao enunciado:

Link da questão original: http://beyondrelational.com/modules/19/tsql-beginners/297/tsql-beginners-challenge-1-find-the-second-highest-salary-for-each-department.aspx?tab=info

Enunciado: O desafio é listar os funcionários com o segundo maior salário de cada departamento. Se dois funcionários tiverem o mesmo salário, você precisa listá-los também!

Regra: Sua solução deve funcionar no SQL Server 2005 ou superior, e a ordernação deve ser feita pelo salário.

Restrição: A solução deve ser apresentada em apenas uma consulta que comece com WITH ou SELECT. Ou seja, pode usar CTE? Pode tio(a). Pode usar tabela temporária, etc? Não tio(a), não pode.

Dados de amostra

Resultado esperado

Código da amostra:

Algumas dicas pra quem se sentir perdido (só leia isso se sentir bastante dificuldade):

1) Usar CTE (Common Table Expressions) é a forma mais fácil de fazer, na minha opinião;

2) Dê uma olhada nas Windows Functions, são o santo graal pra Ad-hoc Queries e vai resolver fácil essa questão .

3) Não veja a solução no site (existe a mesma disponível lá na aba solutions). Tente fazer pra aprender . Não é nada complexo e não exige muita leitura.

A solução será postada em outro post com explicações (farei isso final de semana ou na segunda). Se quiser ver a resposta (de novo, não recomendo) que ainda não tem as explicações mais atenciosas que farei na próxima postagem, clique aqui!

Até mais!