#php #mysql #sql #laravel #window-functions
#php #mysql #sql #laravel #окно-функции
Вопрос:
Версия
Laravel: 7.28.3
mysql: версия 14.14 Дистрибутив 5.7.29, для osx10.15 (x86_64) с использованием оболочки EditLine
Таблицы
- содержимое (идентификатор)
- content_views (id, content_id)
Что я пытаюсь сделать
Я хотел бы получить рейтинг контента по количеству просмотров контента.
Код
App/Models/Content.php
/**
* Attribute of get rank by views
*
* @return Int
*/
public function getViewsRankingAttribute()
{
DB::statement(DB::raw('set @c=0'));
$result = collect(
DB::select('select rank from
(
select _ranking.*, @c:=@c 1 as rank from
(
select content_views.content_id, count(content_views.id) as views
from content_views
group by content_views.content_id
order by views desc
) as _ranking
) as ranking
where content_id = :contentId', [
'contentId' => $this->id
]))
->first();
return $result ? $result->rank : '-';
}
На самом деле это работает в mysql 5.8, но не в 8.0.
Ошибка
SQLSTATE[42000]: синтаксическая ошибка или нарушение доступа: 1064 У вас ошибка в синтаксисе SQL; проверьте руководство, соответствующее вашей версии сервера MySQL, чтобы найти правильный синтаксис для использования рядом с ‘from ( выберите _ranking., @c:=@c 1 как ранг из ‘ в строке 1 (SQL: выберите ранг из( выберите _ranking., @c:=@c 1 как ранг из ( выберите content_views.content_id, count(content_views.id ) как просмотры из группы content_views по content_views.content_id порядок просмотров desc ) как _ranking ) как ранжирование, где content_id = :ContentID)
Комментарии:
1. Появление CTE в 8 избавило от необходимости в таких переменных. Но, поскольку вы анализируете результат в PHP, почему бы не обработать ранжирование там?
2. @Strawberry: OP хочет получить рейтинг только определенного контента. Чтобы сделать это в PHP, им нужно будет получить весь набор данных (сгруппированный по содержимому), затем выполнить итерацию в приложении, что выглядит неоптимально.
Ответ №1:
В MySQL 8.0 представлены оконные функции, которые значительно упрощают выполнение задач ранжирования. С другой стороны, пользовательские переменные официально планируются к устареванию в будущих версиях.
Время принять будущее! Вы можете переписать свой запрос следующим образом:
select *
from (
select content_id, count(*) as views, rank() over(order by count(*) desc) rn
from content_views
group by content_id
) t
where content_id = :content_id
Ответ №2:
Это действительно сработало!
public function getViewsRankingAttribute()
{
$rank = '-';
if ($this->views->isNotEmpty()) {
$views = $this->views->count();
$rank = Content::withCount('views')
->having('views_count', '>', $views)
->get()
->count();
$rank ;
}
return $rank;
}