Doctrine - Left Join with LIMIT - symfony

I Have two tables : Post and Comment in a Many to One relation.
table post table comment
p_id | name | u_id c_id | p_id | comment | date
1 | post 1 | 1 1 | 1 | blah blah blah | 2017-01-01
2 | post 2 | 2 2 | 1 | blah blah blah | 2017-01-04
3 | post 3 | 1 3 | 2 | blah blah blah | 2017-01-07
... ...
I would like to retrieve all Posts by u_id with their 3 Last comments ordered by date.
I would do :
SELECT p, c
FROM p
LEFT JOIN p.comments c WITH c.date IN (SELECT c2.date) FROM App\Bundle\Entity\Comment as c2 WHERE c2.post = p.p_id ORDER BY date ASC LIMIT 3)
WHERE p.user = 1
But doctrine doesn't allow LIMIT, how i can do that ?

In my opinion the easiest way to to that is by using the function
setMaxResults($limit);
I give you an example :
$qb = $this->createQueryBuilder('a');
$qb
->innerJoin('a.advert', 'adv')
->addSelect('adv')
;
$qb->setMaxResults($limit);
return $qb
->getQuery()
->getResult()
I hope it will help you

As I known QueryBuilder::setMaxResult() work only on primary table not on subquery.
IMHO it's best to write it in classic SQL, where you have better control on subquery and what is joining. In this case you get plain array not array of objects with relation. But, this is better when you need only to show lists of posts and not interact with single entity. In this case getting entire object hydrating one by one populating from DQL, is quite slower then using plain array from SQL.
You could do this like that:
$conn = $this->getEntityManager()->getConnection();
$stmt = $conn->prepare('SELECT ...');
$stmt->execute($parameters);
$results = $stmt->fetchAll(\PDO::FETCH_ASSOC);

Related

SQLite Join - query most recent posts by each user

In a sqlite database i have a "Users" table as follows
Users:
| user_id | user
| 1 | John
| 2 | Peter
a "Posts" table as follows:
| post_id | user_id | text
| 10 | 1 | blah blah blah
| 11 | 1 | blah blah blah
| 12 | 2 | blah blah blah
I'm using this code to join two tables:
SELECT * FROM Users JOIN Posts USING ( user_id )
Now i have a list of all posts by different users as follows:
| user_id | user | post_id | text
| 1 | John | 10 | blah blah blah
| 1 | John | 11 | blah blah blah
| 2 | Peter | 12 | blah blah blah
Assuming that the post with bigger post_id is posted more recently.
Now i want a list of most recent posts by each users (row #2 and #3 in this case).
How can i do this?
I'm thinking of deleting all rows (for each users) , except the rows which has biggest post_id (most recent), is this a good solution? how should i query it?
You can use an inner query that gets you the latest post for each user, then that table will act as a filter when joined with the other two.
select *
from users u
join (
select user_id, max(post_id) post_id
from Posts
group by user_id
) r
on u.user_id = r.user_id
join posts p
on r.post_id = p.post_id and
r.user_id = p.user_id
In SQLite (but not in any other DB), when using GROUP BY, you can use max() or min() to select which row to return in a group:
SELECT user_id,
user,
max(post_id) AS post_id,
text
FROM Users
JOIN Posts USING ( user_id )
GROUP BY user_id;
you have to change the query a little bit. you have to write the query as follows:
SELECT * FROM Users Join Posts Using(user_id) order by post_id desc;

SQLite: delete all records from table specified in another table

Having tables like this
mytab deltab
--------- --------
id | name id | name
1 | Ann 2 | Bob
2 | Bob 3 | Cindy
3 | Cindy
4 | Dave
I'd like to perform query which deletes all records in mytab specified in deltab, so only Ann and Dave would be left in mytab.
While there is MySQL multiple-table delete syntax, looks like there is no such thing in SQLite delete syntax.
I am considering REPLACE syntax with select-stmt and mark the rows which will be deleted (like set these names to NULL) in DELETE query after. I wonder if there is more effective way?
Try this query
Delete from mytab where id in (select id from deltab);
Try this:
DELETE FROM mytab
WHERE EXISTS (SELECT *
FROM deltab
WHERE deltab.ID = mytab.ID)

select record that match one item in column

So I'm making a movie website and in my database table, I've got a column called Genre. In this I have listed the genres like this; Horror, Action.
Example Table
+----+---------+--------+
| id | Genre |
+----+---------+--------+
| 1 | Action |
| 2 | Horror, Action |
| 3 | Horror |
| 4 | Action |
| 5 | Romance, Drama |
| 6 | Horror, Drama |
+----+---------+--------+
So if I were to do a query to get films with a Horror genre, it would return the ID's 2,3 & 6. How would I go about structuring a query to do this?
Thanks.
The structure of your database is no good. Read up on database normalization to understand why this is. I would go with a structure like this:
Movie:
Id
Name
Movie_Genres
Movie_Id
Genre_Id
Genres:
Id
Name
You can then do a query like this:
SELECT m.name
FROM Movie as m
INNER JOIN Movie_Genres as mg
ON mg.Movie_id = m.id
WHERE mg.Genre_id = {Horror genre Id}
If you don't structure your DB like this you will run into a lot of problems down the road.
Continuing off of #Abe's excellent answer. If you want to build a comma delimited string based on #Abe's normalized 3 table structure it would look like this:
SELECT m.Name,
STUFF(( SELECT ', ' + g.Name
FROM Movie_Genres as mg
INNER JOIN Genres AS g on mg.Genre_id = g.Id and mg.Movie_id = m.id
FOR XML PATH('')
), 1, 2, '')
FROM Movie AS m
GROUP BY m.Name, m.Id;
Here's a Fiddle

How to get list of Mutual friends,Following and Followers

I read many questions here about mutual friends and following and followers but i can't solved my problem, I have two tables like these.
User_table ( UID, Name)
Relation_table (RID, UID,UIDF)
for example in user_table have 4 users
UID | Name
------------------
1 | Kim Tessman
2 | Nella Ohler
3 | Adria Larose
4 | Huey Errico
And Relation_table have these data
RID | UID | UIDF
------------------
1 | 1 | 2
2 | 2 | 1
3 | 1 | 4
4 | 4 | 3
5 | 4 | 1
my questions are :
how to get list of Mutual friends ?
how to get list of Following ?
how to get list of Followers ?
please who can solve this problem thank you :)
I am guessing that the UID column in the Relation_Table is the user and that the UIDF is the person they are following. Here is the SQL query that finds who a person is following. The who is being followed is the same query, you just focus on the other name column or switch the column order around.
Here is a list of who follows who. It is also the list of who is being followed.
SELECT FollowingUser.UID as FollowingUserID, FollowingUser.Name, BeingFollowed.UID AS BeingFollowedID, BeingFollowed.Name AS BeingFollowedName
FROM User_table AS FollowingUser INNER JOIN
Relation_Table AS r ON FollowingUser.UID = r.uid INNER JOIN
User_table AS BeingFollowed ON r.uidf = BeingFollowed.UID
And this query gives you the list of mutual friends - but it gives the inverse of the relationship. If this matters, add this to the end where FollowingUser.UID > BeingFollowed.UID
SELECT FollowingUser.UID AS FollowingUserID, FollowingUser.Name, BeingFollowed.UID AS BeingFollowedID, BeingFollowed.Name AS BeingFollowedName
FROM User_table AS FollowingUser INNER JOIN
Relation_Table AS r ON FollowingUser.UID = r.uid INNER JOIN
Relation_Table AS r1 ON r.uid = r1.uidf AND r.uidf = r1.uid INNER JOIN
User_table AS BeingFollowed ON r1.uid = BeingFollowed.UID

Complicated query with SQLite

I have a complicated query that I just cant create. I'm thinking of doing it with JOIN's but don't know where to start. I'll just try to explain my problem as clearly as possible.
I am creating software that deals with compensations. Every compensation can have several links (like in a chain). Each link is basically a customer. So, I'll just tell you the table structures:
Customer
CustomerID | Name
Compensation
CompensationID | CustomerID | Date
Link
LinkID | CompensationID | CustomerID | Sequential
Now, the Sequential field increases with every link added. Let me demonstrate by filling the tables with some data:
CustomerID | Name
-----------+-----
0 | Foo
1 | Bar
2 | Baz
CompensationID | CustomerID | Date
---------------+------------+------
0 | 0 | 2-2-2010
1 | 1 | 2-3-2010
LinkID | CompensationID | CustomerID | Sequential
-------+----------------+------------+-----------
0 0 0 0
1 0 2 1
2 0 1 2
So no matter what the LinkID/CompensationID/CustomerID the Sequential always goes from 0 to N in the Links table (depending on how many links a compensation has).
Now here is the problem: I want to list all Compensation's whose Link's meet the following requirements:
Search in Links
CustomerID = A
Sequential = 0 (first link) AND Sequential = LAST (in this case 2)
List compensations
Here is what I got so far:
SELECT * FROM Compensation JOIN Link ON Compensation.ID = Link.CompensationID
WHERE Link.CustomerID=A AND Link.Sequential = 0 AND Link.Sequential=LAST
This is more or less pseudo SQL since I know Link.Sequential cant be 0 and another value at the same time, but I don't know how to do this.
Any help would be appreciated.
Thank you.
P.S.
Sorry for the big wall of text.
If subqueries in where statements work how I remember:
select *
from
Compensation
left join
Link
on Compensation.CompensationID = Link.CompensationID
where
Link.CustomerID = :A
AND (
Link.Sequential = 0
OR
Link.Sequential = (
select MAX(Sequential) from Link where Link.CustomerID = :A
)
)
Try
SELECT c.*
FROM Compensation c
JOIN (select CompensationID, MAX(Sequential) AS LastSeq FROM Link GROUP BY CompensationID) AS LastOnes ON c.ID = LastOnes.CompensationID
JOIN (select CompensationID FROM Link WHERE CustomerID=A AND Sequential=0) AS FirststOnes ON c.ID = FirststOnes.CompensationID
JOIN Link AS l on l.CompensationID=c.CompensationID AND l.CustomerID=A AND l.Sequential=LastOnes.LastSeq

Resources