#sql #postgresql
#sql #postgresql
Вопрос:
У меня есть таблица сообщений, post_likes, и мне нужен запрос, который выдаст мне как итоговые значения лайков для сообщений, так и также лайки данного конкретного пользователя для этих сообщений. Это означает, что мне нужен хороший способ предоставления MY_USER_ID в качестве входных данных.
Вот запрос, который работает
create view post_view as
select post.id,
coalesce(sum(post_like.score),0) as score,
(
select score
from post_like
where post.id = post_like.post_id
and post_like.fedi_user_id = MY_USER_ID
) as my_vote,
from post
left join post_like on post.id = post_like.post_id
group by post.id;
НО мой ORM (rust diesel) не позволяет мне устанавливать или запрашивать это необходимое MY_USER_ID
поле, поскольку это подзапрос.
Мне бы очень хотелось иметь возможность делать что-то вроде:
select *
from post_view
where my_user_id = 'X';
Комментарии:
1. Вы не можете передать параметр в представление. Вам нужно было бы использовать функцию возврата set, которая имеет параметр и возвращает результат запроса
Ответ №1:
Выставить my_user_id в предложении выбора представления
-- get all of the user's score on all post, regardless of the user liking the post or not
create view post_view as
select
u.id as my_user_id,
p.id as post_id,
sum(pl.score) over (partition by p.id) as score,
coalesce(pl.score, 0) as my_vote -- each u.id's vote
from user u
cross join post p
left join post_like pl on u.id = pl.fedi_user_id and p.id = pl.post_id;
select * from post_view where my_user_id = 'X';
Обновить
Это может получить оценки post, даже если пользователь не указан
create view post_view as
with all_post as
(
select
p.id as post_id,
sum(pl.score) as score
from post p
left join post_like pl on p.id = pl.post_id
group by p.id
)
select
u.id as my_user_id,
ap.post_id,
ap.score,
coalesce(pl.score, 0) as my_vote
from user u
cross join all_post ap
left join post_like pl on u.id = pl.fedi_user_id and ap.post_id = pl.post_id
union all
select
'' as my_user_id,
ap.post_id,
ap.score,
0 as my_vote
from all_post ap
;
select * from post_view where my_user_id = 'X';
Если пользователь не передан, выберите запрос, обозначаемый my_user_id из ''
select * from post_view where my_user_id = '';
Комментарии:
1. Это действительно отличный ответ и ТАКОЙ близкий…. но это не обрабатывает случай, когда мне также нужно получить сумму, когда я не указываю пользователя. Я думаю, что то, о чем я прошу, невозможно в sql, и это лучшее, что я могу получить, и отвечает на мой вопрос: это удаляет пользовательские настройки из подзапроса. Приветствия!
2. @thouliha внес изменения, поэтому, когда пользователь не передан, он все равно будет показывать все оценки post
3. Спасибо, я не думал включать эту информацию в объединение! Начиная с вашего ответа, я просто сгруппировал по во всех других полях и установил для user_id и my_vote значения null для моего ORM, но это тоже работает!
Ответ №2:
вы можете переместить это условие в on
предложение
create view post_view as
select post.id,
coalesce(sum(post_like.score),0) as score
from post
left join post_like
on post.id = post_like.post_id and post_like.fedi_user_id = MY_USER_ID
group by post.id;
Ответ №3:
Вы можете переместить логику в select
, используя условную агрегацию:
select p.id,
coalesce(sum(pl.score), 0) as score,
sum( (pl.fedi_user_id = MY_USER_ID)::int ) as my_vote
from post p left join
post_like pl
on p.id = pl.post_id
group by p.id;
Комментарии:
1. Это оставляет меня в том же положении, что и раньше, неспособным установить MY_USER_ID вне представления.
2. @thouliha . . . Я боялся, что это может быть тем, что вы хотели сделать. Вы не можете определить представление с помощью переменной. Что вам нужно, так это определяемая пользователем табличная функция: postgresql.org/docs/11/sql-createfunction.html .