Query to return distinct records ordered by newest association - activerecord

I have a simple relation
Playlist (id, title)
which has_many
Playlistships(playlist_id, post_id)
A Playlistship is an association between the Playlist and Post classes. When a user adds a 'post' to a 'playlist', a new 'playlistship' is created. When a 'post' is removed from a 'playlist', the corresponding 'playlistship' is destroyed.
I would like to create a query which would return 'Playlists' in order of playlistships.created_at
Basically, I want to have a view which shows playlists which have recently had posts added to them via the Playlistship class.

Try something like:
select * from Playlists P
join Playlistships S on P.id = S.playlist_id
order by S.created_at
To get the most recent Playlistship for each Playlist (that has any posts):
select * from Playlists P
join Playlistships S on P.id = S.playlist_id
where S.created_at = (select max(created_at) from Playlistships where playlist_id = P.id)
order by P.id

Related

Select from junction table

everybody!
What should I do if I need to make select from junction table?
For example, I develop project and I need to make chats between users. I have two entities: User and Chat, and many-to-many relation between them (accordingly, I have three tables: user, chat, chat_user). I try to get all chats, which user is member, and to get all users from these chats.
I made the following SQL query:
SELECT *
FROM chat c
INNER JOIN chat_user cu ON c.id = cu.chat_id
INNER JOIN user u ON u.id = cu.user_id
WHERE c.id IN (SELECT chat_id
FROM chat_user
WHERE user_id = <idUser>);
But I don't know how to translate in DQL subquery SELECT chat_id FROM chat_user WHERE user_id = <idUser>, because a haven't additional entity for table chat_user.
And I tried to add entity ChatUser and get data in ChatRepository smt. like this:
public function getChatsData($idUser)
{
$subQuery = $this->getEntityManager()
->getRepository(ChatUser::class)
->createQueryBuilder('chus')
->select('chus.chat')
->andWhere('chus.user = :idUser')
->setParameter('idUser', $idUser)
;
$qb = $this->createQueryBuilder('c');
return $qb
->innerJoin('c.chatUsers', 'cu')
->addSelect('cu')
->innerJoin('cu.user', 'u')
->addSelect('u')
->innerJoin('c.messages', 'm')
->addSelect('m')
->andWhere('u.id = :idUser')
->andWhere($qb->expr()->in(
'c.id',
$subQuery->getDQL()
))
->setParameter('idUser', $idUser)
->getQuery()
->getResult()
;
}
but it doesn't work. I get error [Semantical Error] line 0, col 12 near 'chat FROM App\Entity\ChatUser': Error: Invalid PathExpression. Must be a StateFieldPathExpression.
Have Doctrine standard tools for such tasks?

Spring Boot #Formula displaying same likeCount on every result

I'm having a problem with my #Formula field. I have three tables: user_acc, song and user_likes_song. I'm trying to display likeCount on songs based on how many times the songs ID appears in the many-to-many table.
My #Formula field in the Song entity looks like this:
#Formula("(SELECT COUNT(s.id) FROM user_acc u INNER JOIN user_likes_song us on u.id = us.user_id " +
"INNER JOIN song s on us.song_id = s.id WHERE us.song_id = s.id )")
private Long likeCount;
Currently when I like a song for the first time it shows "likeCount: 1" on every result. After I like another song it returns "likeCount: 2" on every result and so on.
Seems to me like it's counting all the likes in total, not for the specific song. How can I make it so it so it shows how many times a specific song ID appears in the many-to-many table?
You do the match with every song rows :
INNER JOIN song s on us.song_id = s.id WHERE us.song_id = s.id
What you want is restricting it to the current song entity instance.
In fact, if the #Formula annotation is specified on a field of the Song entity, you don't want to specify Song in the query, instead you want to directly refer the field of the current entity (here id), that is :
WHERE us.song_id = id
So this should do the job :
#Formula("(SELECT COUNT(*) FROM user_acc u INNER JOIN user_likes_song us on u.id = us.user_id " + "WHERE us.song_id = id )")
And I think that you could also simplify more in this way :
#Formula(" (SELECT COUNT(*) FROM user_likes_song us WHERE us.song_id = id) ")
because the join with user account doesn't matter.

Rails find all Customers that have exactly 1 Order

I have a data model where a Customer has many Orders. I now need to extract all of the customers that have only placed 1 order and I am scratching my head trying to figure out what to do.
Use this
Customer.joins(:orders).group(:id).having("count(orders.id) = 1")
this will create a query like:
SELECT "customers".* FROM "customers" INNER JOIN "orders" ON
"orders"."customer_id" = "customers"."id" GROUP BY id HAVING
count(orders.id) = 1
You would get all of the customers that have placed exactly 1 order.
To avoid ambiguous reference to the ID field, this would be used:
Customer.joins(:orders).group("customers.id").having("count(orders.id) = 1")
which would generate the following SQL:
SELECT "customers".* FROM "customers" INNER JOIN "orders" ON "orders"."customer_id" =
"customers"."id" GROUP BY customers.id HAVING count(orders.id) = 1
Consider Customer and Order are ActiveRecord classes and consider you have a code line
belongs_to :customer in your Order class definition. And also consider that Order table have a foreign_key or index column named customer_id do as below to get this Customer objects.
Customer.joins(:orders).where("count(orders.id) = 1")

Linq left outer group by, then left outer the group

I've this query that i'm trying to put as linq:
select *
from stuff
inner join stuffowner so on so.stuffID = stuff.stuffID
left outer join (select min(loanId) as loanId, stuffownerId from loan
where userid = 1 and status <> 2 group by stuffownerId) t on t.stuffownerid = so.stuffownerid
left outer join loan on t.LoanId = loan.LoanId
when this is done, I would like to do a linq Group by to have Stuff has key, then stuffowners + Loan as value.
I can't seem to get to a nice query without sub query (hence the double left outer).
So basically what my query does, is for each stuff I've in my database, bring the owners, and then i want to bring the first loan a user has made on that stuff.
I've tried various linq:
from stuff in Stuffs
join so in StuffOwners on stuff.StuffId equals so.StuffId
join tLoan in Loans on so.StuffOwnerId equals tLoan.StuffOwnerId into tmpJoin
from tTmpJoin in tmpJoin.DefaultIfEmpty()
group tTmpJoin by new {stuff} into grouped
select new {grouped, fluk = (int?)grouped.Max(w=> w.Status )}
This is not good because if I don't get stuff owner and on top of that it seems to generate a lot of queries (LinqPad)
from stuff in Stuffs
join so in StuffOwners on stuff.StuffId equals so.StuffId
join tmpLoan in
(from tLoan in Loans group tLoan by tLoan.StuffOwnerId into g
select new {StuffOwnerId = g.Key, loanid = (from t2 in g select t2.LoanId).Max()})
on so.StuffOwnerId equals tmpLoan.StuffOwnerId
into tmptmp from tMaxLoan in tmptmp.DefaultIfEmpty()
select new {stuff, so, tmptmp}
Seems to generate a lot of subqueries as well.
I've tried the let keyworkd with:
from tstuffOwner in StuffOwners
let tloan = Loans.Where(p2 => tstuffOwner.StuffOwnerId == p2.StuffOwnerId).FirstOrDefault()
select new { qsdq = tstuffOwner, qsdsq= (int?) tloan.Status, kwk= (int?) tloan.UserId, kiwk= tloan.ReturnDate }
but the more info i get from tLoan, the longer the query gets with more subqueries
What would be the best way to achieve this?
Thanks

LINQ: Inner join to the First row in a sub query?

I have two classes, basically one holds Members and the other Sessions.
They are joined together with a common field called "name". There is one member but can be many Sessions.
So if I do a standard join I get back 1 member and many sessions. I just want to get back the first row of sessions.
The session has field called SessioEndTime. So I need to order by DESC on this to pick out the first record.
This is my linq; I have returns too many. I think I need a subquery but I am a little confused.
var sessions = from m in this.members
join s in this.sessions
on m.Name equals s.Name
select new { MemberName = m.Name, SessionTime = s.SessioEndTime};
Edit
To make it clear, imagine I have five members, each member has NUMEROUS sessions. I just wish to receive my five members but with only one session each, that session is the LATEST session which can be got from the SessioEndTime.
Try this:
var sessions =
from m in this.members
join s in
(
from se in this.sessions
group se by se.Name into g
select new {Name = g.Key, SessioEndTime = g.Max(a=>a.SessioEndTime)}
)
on m.Name equals s.Name
select new { MemberName = m.Name, SessionTime = s.SessioEndTime}

Resources