Eu tenho uma tabela de usuários:
|Username|UserType|Points|
|John |A |250 |
|Mary |A |150 |
|Anna |B |600 |
e Níveis
|UserType|MinPoints|Level |
|A |100 |Bronze |
|A |200 |Silver |
|A |300 |Gold |
|B |500 |Bronze |
E estou procurando uma consulta para obter o nível de cada usuário. Algo na linha de:
SELECT *
FROM Users U
INNER JOIN (
SELECT TOP 1 Level, U.UserName
FROM Levels L
WHERE L.MinPoints < U.Points
ORDER BY MinPoints DESC
) UL ON U.Username = UL.Username
De modo que os resultados seriam:
|Username|UserType|Points|Level |
|John |A |250 |Silver |
|Mary |A |150 |Bronze |
|Anna |B |600 |Bronze |
Alguém tem alguma idéia ou sugestão sobre como eu poderia fazer isso sem recorrer a cursores?
Sua consulta existente está próxima de algo que você poderia usar, mas você pode obter o resultado facilmente fazendo algumas alterações. Ao alterar sua consulta para usar o
APPLY
operador e implementar oCROSS APPLY
. Isso retornará a linha que atende aos seus requisitos. Aqui está uma versão que você pode usar:Aqui está um SQL Fiddle com uma demonstração . Isso produz um resultado:
A solução a seguir usa uma expressão de tabela comum que verifica a
Levels
tabela uma vez. Nesta varredura, o nível de pontos "próximo" é encontrado usando aLEAD()
função window, então você temMinPoints
(da linha) eMaxPoints
(o próximoMinPoints
para o atualUserType
).Depois disso, você pode simplesmente juntar a expressão de tabela comum,
lvls
, onUserType
e o intervaloMinPoints
/MaxPoints
, assim:A vantagem de usar a função de janela é que você elimina todos os tipos de soluções recursivas e melhora drasticamente o desempenho. Para melhor desempenho, você usaria o seguinte índice na
Levels
tabela:Por que não fazer isso usando apenas as operações rudimentares, INNER JOIN, GROUP BY e MAX:
Eu acho que você pode usar um
INNER JOIN
-como um problema de desempenho que você também pode usarLEFT JOIN
- comROW_NUMBER()
uma função como esta:Demonstração do SQL Fiddle