У меня есть база данных SQL Server с таблицей с именем Users.
В этой таблице есть 2 столбца: XP (int32) и Name (nvarchar).
Мне нужен запрос SQL или Linq (это лучше, потому что я использую Entity Framework на C#), который получает Name и ранжирует всех на основе XP и отображает ранг этого имени + ранг предыдущего 1 человека + ранг следующие 1 чел.
Что-то вроде этого :





Конечно. Попробуй это
SELECT
XP,
name,
RANK() OVER (ORDER BY XP DESC) AS Ranking
FROM UserExperience;
Бро, можешь сказать мне, как мне поступить? WHERE Ranking BETWEEN 3 AND 7 теперь работает... фактически сервер sql не видит "Рейтинг"
Вам нужно обернуть его в подзапрос
@siggemannen Я протестировал множество режимов, думаю, что использовать этот метод «ранжирования» неправильно.
@siggemannen Я даже не могу получить ранг этого "пользователя" + ее следующих людей
@YasimYasin Думаю, что-то вроде этого задумано? WITH ranked AS (SELECT xp, name, RANK() OVER (ORDER BY XP DESC) AS ranking FROM users), ranking_compare AS (SELECT ranking FROM ranked WHERE name = 'User6') SELECT r.ranking, r.name FROM ranked r INNER JOIN ranking_compare rc ON r.ranking between rc.ranking - 1 and rc.ranking + 1 ORDER BY r.ranking, не так ли?
@JonasMetzler Я получил ответ, но и вам спасибо. Я думаю, что этот код для меня тяжелый... потому что у меня нет возможности анализировать запросы на сервере MS Sql. И я предпочитаю использовать LINQ сейчас. Вы имеете в виду, что это лучше использовать здесь «процедурный сервер Sql»?
@YasimYasin Да, на мой взгляд, здесь гораздо лучше не использовать Linq, см. мой комментарий для Charlieface под принятым вами ответом.
С точки зрения EF кажется, что это может быть просто:
var d = db.Users
.OrderByDescending(u => u.Xp)
.AsEnumerable()
.Select((u, i) => new { Rank=i+1, User = u })
.ToDictionary(at => at.Rank);
Вы выписываете всех своих пользователей из БД в порядке XP и используете перегрузку Select((user,index)=>...) для присвоения ранга (индекс + 1) и для удобства помещаем их в словарь, индексированный по рангу.
Теперь у вас есть словарь, который вы можете хранить и неоднократно просматривать, где для любого конкретного пользователя вы можете получить окружающих пользователей по рангу пользователя, которого вы уже знаете:
var user = d.Values.FirstOrDefault(u => u.User.Name == "user6");
var prev = d[user.Rank - 1];
var next = d[user.Rank + 1];
Вам нужен код, чтобы предотвратить исключение KeyNotFoundException, если вы найдете пользователя с верхним или нижним рейтингом, потому что, если вы найдете пользователя с рангом 1 по имени, вы не сможете получить пользователя с рангом 0 (их не существует). В исходном сообщении вы не сказали, что делать в этой ситуации, вы можете использовать containsKey, чтобы проверить, содержит ли словарь определенный ранг, или вы можете использовать GetValueOrDefault или TryGetValue, чтобы попытаться получить пользователя - эти методы не произойдет сбой, если пользователь не найден, но вам нужно будет решить, что делать, если он не найден
@Jonas ToDictionary не выдаст KeyNotFound. Единственное место, которое выдает ошибку, — это если найден пользователь, например, с рангом = 1, и запускается код, который пытается получить ранг = 0. Я специально предупреждаю об этом в последней строке сообщения и советую использовать TryGetValue. В ФП не указано, что делать в случае попытки получить пользователя, у которого нет пользователя с рейтингом выше или ниже него. Реализация некоторых вещей должна быть оставлена на усмотрение ОП; мы не можем написать за них всю программу
@Charlieface, чтобы инициировать загрузку всех пользователей, потому что, хотя теоретически это может быть преобразовано, например, в ROW_NUMBER()OVER(), EF не способен переводить перегрузку Select((e,i)...), которая рассчитывает рейтинг.
@Charlieface Они усиленно хотят создать что-то вроде RANK или ROW_NUMBER на C# с помощью Linq. Это просто ужасно, потому что это излишне сложно, неэффективно и не для того, для чего предназначен Linq. Такую функциональность гораздо лучше реализовать на чистом SQL, где RANK и ROW_NUMBER — это встроенные функции, которые работают довольно быстро и очень просты в использовании. Да, ОП также попросил решение Linq, но честный ответ здесь будет: пожалуйста, не делайте этого, используйте чистый SQL. Проблема, с которой вам предстоит справиться, возможно KeyNotFoundException тоже это показывает.
@JonasMetzler, какая строка вызывает исключение KeyNotFound?
@flackoverstow После последних изменений в вашем коде (которые вы сделали после моего комментария) код работает. Спасибо за улучшение.
Добро пожаловать.. Я до сих пор не могу найти ничего, что могло бы вызвать KNF, кроме, возможно, извлечения пользователя user3, поскольку попытка получить пользователя более высокого уровня не удалась, но спасибо за подталкивание проверить это.
Спасибо. Пожалуйста, помогите мне. Добавьте в него
where... напримерwhere name = 'user2'просто покажите одну запись.