This doesn't address your query, but rather the overall problem in case that's useful.
I confronted the same for an international contest where the teams table could get quite big. I never was able to get an SQL ranking query to perform well enough for a good user experience (goal was 80ms, and the query was somewhat more complex than yours), so finally decided to use a redis server just for returning ranks.
It provides a ranking function exactly suited to this problem. It's fast: a few millis for a table of 10 million contestants.
I still regarded scores stored in the SQL DB as the source of truth. Redis isn't ACID. It saves to disk only snapshots of its data image in RAM. If the server goes down, it reverts to the last snapshot. So redis and the source of truth could diverge a bit.
This wasn't any problem in my case because instantly returned ranks were acknowledged to be unofficial pending final review by judges. Missing data due to reboots from snapshot were "self healing". That is, if I queried for the rank of a team and it wasn't in the redis store, I added it then re-queried. I also ran a daily sync job to restore perfect agreement. I could run this sync at any time to initialize a new redis from scratch.
This scheme proved extremely fast and robust for 7 years. The implementation it replaced used a custom BerkeleyDB-based service. That one worked well for the preceding 7 years.
One other point is that a redis service can be very handy for other purposes like caches.