Banco de Dados

5 out, 2009

Critérios de desempate utilizando o order by

Publicidade

Certos modelos permitem a geração de relatórios onde a ordenação dos dados é utilizada para destacar alguma linha do conjunto de linhas de uma tabela. Esse relatório geralmente é utilizado quando se deseja visualizar um ranking das linhas de uma tabela, organizado de acordo com os atributos da tabela. Esse ranking pode ser utilizado para auxiliar várias tarefas, como a premiação de um indivíduo de acordo com seu desempenho ou para obter a colocação de times em um campeonato.

Durante a definição de qual atributo deve ser aplicado para ordenar o conjunto de linhas, deve-se definir também qual é o critério de desempate, isto é, como a colocação vai ser atribuída às linhas que possuírem o mesmo valor para o atributo que define a ordem. Esta regra de desempate geralmente é baseada em outro atributo da mesma tabela.

A ordenação dos resultados de uma instrução SELECT é facilmente obtida através da cláusula ORDER BY. Porém, nos casos onde os valores das colunas especificadas na cláusula ORDER BY se repetem,  é necessário aplicar um critério de desempate. Devemos utilizar os atributos da tabela para construir a regra de desempate que permite a geração correta do relatório.

Neste artigo vamos apresentar alguns exemplos de modelos onde a aplicação da regra de desempate pode ser implementada através da cláusula ORDER BY de uma instrução SELECT.

Escolhendo o ganhador

No primeiro exemplo, vamos trabalhar com um modelo de jogos pela internet. Neste cenário, vários jogadores participam individualmente de um jogo on-line onde o objetivo é completar certas tarefas e acumular pontos durante uma partida. O jogo possui um ganhador mensal, apontado pelo jogador que acumulou mais pontos em alguma partida jogada no mês. Caso mais de um jogador possua a maior pontuação no final do mês, deve-se aplicar o seguinte critério de desempate: o primeiro jogador que atingiu a pontuação máxima é o ganhador do mês.

A tabela que armazena os dados das partidas do jogo chama-se TB_JOGOS e suas linhas podem ser visualizadas na Tabela 1, onde temos quatro jogadores do mês de janeiro de 2005. Neste exemplo, optou-se pela precisão de dias, mas nada impede que apliquemos o critério de desempate considerando as horas, minutos e segundos.

ID_JOGADOR NOME_JOGADOR PONTOS_JOGADOR DATA_PARTIDA
1 A 5 20/01/2005
2 B 3 22/01/2005
3 C 10 15/01/2005
4 D 10 16/01/2005

Tabela 1: Pontuação dos jogadores

Para obter o ganhador mensal, basta colocar a cláusula ORDER BY na instrução SELECT, especificando que desejamos primeiro ordenar as linhas da tabela pela coluna PONTOS_JOGADOR, de forma decrescente, e depois pela coluna DATA_PARTIDA, de forma crescente. A instrução SELECT que traz o ganhador do mês é apresentada na Listagem 1.

-- A INSTRUÇÃO ABAIXO OBTÉM O CAMPEÃO 
-- DO MÊS DE JANEIRO ATRAVÉS DO CRITÉRIO DE DESEMPATE
SELECT ID_JOGADOR
,NOME_JOGADOR
,PONTOS_JOGADOR
,CONVERT(VARCHAR(10),DATA_PARTIDA,103) AS DATA_PARTIDA
FROM TB_JOGOS
ORDER BY PONTOS_JOGADOR DESC, DATA_PARTIDA ASC

Listagem 1: Instrução que retorna a lista ordenada de participantes do mês

Notem, na instrução da Listagem 1, que obtemos todos os participantes do mês ordenados pela sua pontuação e, no caso de repetição da pontuação, aplicamos o critério de desempate através da ordenação decrescente da coluna DATA_PARTIDA, que é do tipo DATETIME do SQL Server. Os valores da coluna DATA_PARTIDA foram convertidos para o formato de data brasileiro.

A Tabela 2 apresenta o resultado da instrução contida na Listagem 1. O campeão do mês de janeiro de 2005 é o jogador C, pois ele fez os mesmos 10 pontos que o jogador D, mas um dia antes.

ID_JOGADOR NOME_JOGADOR    PONTOS_JOGADOR    DATA_PARTIDA
3 C 10 15/01/2005
4 D 10 16/01/2005
1 A 5 20/01/2005
2 B 3 22/01/2005

Tabela 2: Jogadores ordenados

Campeão da Taça

Como dito na introdução do artigo, o critério de desempate geralmente é baseado em algum atributo da tabela. Em algumas situações, somente os atributos não são suficientes para o critério de desempate e precisamos montar alguma expressão que represente o critério de desempate na nossa instrução SELECT. O próximo exemplo que veremos é um caso típico deste tipo de critério de desempate.

Em um campeonato de futebol, diversos times disputam partidas entre si, de alguma maneira lógica, com o objetivo de conquistar a taça do campeonato, dada ao time que mais conseguir pontos em sua campanha. Além da quantidade de pontos que cada time obtém, a tabela de colocação no campeonato armazena os gols a favor e os gols contra, como pode ser visto nos dados que a Tabela 3 apresenta.

TIME    PONTOS    GOLS_AFAVOR    GOLS_CONTRA
CASADOS S.A. 16 4 3
S. D. PÉ E BOLA 20 6 3
SOLTEIROS LTDA.  21 7 2
TIME DA VÁRZEA 18 3 6
UNIDOS F.C. 20 6 4

Tabela 3: Times do campeonato

O critério de desempate deste campeonato diz que, quando dois ou mais times possuírem a mesma pontuação, deve-se aplicar o saldo de gols como critério de desempate. Este saldo de gols é obtido subtraindo a quantidade de gols contra da quantidade de gols a favor do time em questão. Aquele que possuir o maior saldo de gols deve ser posicionado acima dos outros times com a mesma pontuação.

Para obter a tabela deste campeonato com as colocações de cada time devemos listar as linhas ordenando-as pela quantidade decrescente de pontos e pelo saldo. Em uma instrução SELECT é simples montar a expressão que gera o saldo de gols como uma coluna calculada. Podemos utilizar o nome da coluna calculada na cláusula ORDER BY, facilitando a leitura da instrução SQL. A Listagem 2 mostra a instrução SELECT que ordena os dados corretamente.

-- A INSTRUÇÃO ABAIXO ORDENA OS TIMES DO CAMPEONATO 
-- APLICANDO O SALDO DE GOLS COMO CRITÉRIO DE DESEMPATE
SELECT TIME
,PONTOS
,GOLS_AFAVOR
,GOLS_CONTRA
,GOLS_AFAVOR - GOLS_CONTRA AS SALDO
FROM TB_CAMPEONATO
ORDER BY PONTOS DESC,SALDO DESC

Listagem 2: Instrução que retorna os times do campeonato ordenados

De acordo com os dados da Tabela 3, o time ‘SOLTEIROS LTDA’ leva a taça do campeonato para a casa, pois possui a maior quantidade de pontos. Para escolher o vice-campeão devemos aplicar o critério de desempate entre os times ‘S. D. PÉ E BOLA’ e ‘UNIDOS F.C.’, onde o primeiro leva a melhor devido ao seu saldo de gols, como os dados da Tabela 4 mostram:

TIME    PONTOS    GOLS_AFAVOR    GOLS_CONTRA    SALDO
SOLTEIROS LTDA. 21 7 2 5
S. D. PÉ E BOLA 20 6 3 3
UNIDOS F.C. 20 6 4 2
TIME DA VÁRZEA 18 3 6 -3
CASADOS S.A. 16 4 3 1

Tabela 4: Tabela de colocação final do campeonato

Ordenando o Telemarketing

No último exemplo que veremos, o critério de desempate depende não somente dos atributos, mas da correlação entre seus valores. Valores estes que, dependendo da situação, precisam ser modificados para facilitar a ordenação.

Em um modelo de telemarketing passivo, diversos operadores (ou operadoras) possuem um telefone conectado a uma central de atendimento telefônico. Para cada operador é designado o número da sua posição de atendimento (P.A.) e, conforme as ligações vão chegando, a central deve seguir uma ordem de atendimento que indica quais PAs vão receber as ligações. Esta ordem é baseada em três critérios:

a) A prioridade das P.A. As prioridades são: Alta (A), Normal (N) e Baixa (B) e as chamadas devem ser encaminhadas nesta ordem de prioridades.

b) O Status da P.A. Status válidos são: Livre (L), Ocupado (O) e Inoperante (I). As P.A.’s que tenham os status Ocupado e Inoperante não recebem ligações, mas as que têm o status Livre recebem ligações.

c) Tempo desde a última ligação. O tempo desde que a P.A. atendeu a sua última ligação. Este tempo é armazenado em segundos e a P.A. que estiver mais tempo sem atender uma ligação tem prioridade sobre as outras.

A tabela que armazena o estado atual do telemarketing é mostrada na Tabela 5, onde temos uma configuração válida dos operadores.

ID_PA TEMPO PRIORIDADE STATUS
1 10 B L
2 20 B L
3 5 N L
4 0 N I
5 1 A L
6 0 A O

Tabela 5: Estado do telemarketing

O que o sistema deve gerar é, dado uma determinada configuração dos operadores e as regras de atendimento, a ordem das P.A.s que vão atender às próximas ligações.

Para determinar qual é a próxima P.A. que vai atender a ligação, temos que seguir a mesma idéia apresentada nos dois exemplos anteriores: aplicar a regra de ordenação e o critério de desempate na instrução SELECT.

O primeiro critério da ordem de atendimento diz respeito às prioridades, que podem assumir os valores Alta, Normal ou Baixa, representados na coluna PRIORIDADE através dos caracteres ‘A’,’N’, e ‘B’, respectivamente. Com estes caracteres representando as prioridades não conseguiremos utilizar a coluna PRIORIDADE para ordenar os dados. Para resolver este problema vamos transformar os caracteres que representam a prioridade em números.  Esta transformação será feita atribuindo o valor 1 para prioridade Alta, 2 para Normal e 3 para Baixa. Vamos criar uma view que gera uma coluna calculada com os novos valores da coluna PRIORIDADE utilizando a estrutura CASE.

O próximo critério da ordem de atendimento diz respeito ao Status da P.A. Este critério diz que apenas o status ‘L’, de Livre, pode atender ligações. O que temos a fazer é filtrar os registros da tabela para que somente aqueles com o valor ‘L’ na coluna STATUS sejam mostrados no nosso relatório.

O último critério diz respeito ao tempo que passou desde a última ligação atendida por uma determinada PA. Como o valor do tempo decorrido está sem segundos basta ordenar o valor desta coluna de forma decrescente.

A Listagem 3 apresenta a criação da view que gera uma coluna calculada a partir dos valores da coluna PRIORIDADE e a instrução SELECT que retorna qual é a ordem das P.A’s a ser seguida no atendimento das ligações.

-- A VIEW ABAIXO GERA OUTROS VALORES
-- PARA REPRESENTAR A PRIORIDADE
CREATE VIEW VW_TELEMARKETING AS
SELECT ID_PA
,TEMPO,PRIORIDADE,
( CASE WHEN PRIORIDADE = 'A' THEN 1
WHEN PRIORIDADE = 'N' THEN 2
ELSE 3 END) AS P_NUM
,STATUS
FROM TB_TELEMARKETING
GO

-- A INSTRUÇÃO ABAIXO RETORNA A ORDEM
-- DE ATENDIMENTO DAS LIGAÇÕES
SELECT ID_PA,TEMPO,PRIORIDADE,P_NUM,STATUS
FROM VW_TELEMARKETING
WHERE STATUS = 'L'
ORDER BY P_NUM ASC, TEMPO DESC

Listagem 3: View com a coluna calculada e a instrução que gera a ordem das P.A.s

Analisando o estado do telemarketing apresentado na Tabela 5, e aplicando as regras de prioridade, podemos perceber que as P.A.s 5 e 6 possuem prioridade Alta e, portanto, deveriam atender a próxima ligação. A P.A. 6 está ocupada, o que faz com que a P.A 5 seja a primeira da fila a atender a próxima ligação. As P.A.s 3 e 4 possuem a prioridade Normal e a P.A. 4 está inoperante, fazendo com que a P.A. 3 seja a próxima da fila . As duas P.A.s de prioridade baixa estão livres para atender, mas a P.A. 2 está há mais tempo na espera por uma ligação e por isso entra na fila antes da P.A. 1. A ordem final de atendimento, gerada pela instrução SELECT da Listagem 3, é apresentada na Tabela  6.

ID_PA TEMPO PRIORIDADE P_NUM  STATUS
1 A 1 L
3 5 N 2 L
2 20 B 3 L
1 10 B 3

Tabela 6: Ordem de atendimento do telemarketing

Notem que se uma P.A. mudar o seu status, ou se uma prioridade for modificada na tabela, a instrução só precisa ser executada novamente para obtermos a nova ordem de atendimento do telemarketing.

Conclusão

Neste artigo discutimos como implementar regras de desempate em instruções SELECT através da cláusula ORDER BY. Três exemplos diferentes foram apresentados com o objetivo de explicar ao leitor quando devemos modificar a instrução SELECT para atender aos requisitos da regra de desempate que foi especificada.