Duplicate results with mysql query - mysql

I've 3 tables to query. I make a select on the first one, depending on the two others. I must have only distinct id from the 1st table, but my query is returning some duplicates... http://sqlfiddle.com/#!2/3e3d6/1
My query:
SELECT p.*
FROM posts p, blogs_subscribed s
WHERE (p.user_id = s.user_id OR p.user_id = 1)
AND p.id NOT IN (
SELECT post_id
FROM posts_unsubscribed u
WHERE u.post_id = p.id
AND u.user_id = p.user_id);
SELECT p.*
FROM posts p, blogs_subscribed s
WHERE (p.user_id = s.user_id OR p.user_id = 1)
AND NOT EXISTS(
SELECT null
FROM posts_unsubscribed u
WHERE u.post_id = p.id
AND u.user_id = p.user_id);
Any idea please?

not entirely sure I understand what you are looking for, but I think this is what you want...
SELECT p.*
FROM posts p, blogs_subscribed s
WHERE (p.user_id = s.user_id OR p.user_id = 1)
AND p.id NOT IN (
SELECT post_id
FROM posts_unsubscribed u
WHERE u.post_id = p.id
AND u.user_id = p.user_id)
GROUP BY p.id;
SELECT p.*
FROM posts p, blogs_subscribed s
WHERE (p.user_id = s.user_id OR p.user_id = 1)
AND NOT EXISTS(
SELECT null
FROM posts_unsubscribed u
WHERE u.post_id = p.id
AND u.user_id = p.user_id)
GROUP BY p.id;

Related

Mysql query returning too many results

I'm working on a project where I need to check if the user liked the post and then use COUNT() on it, if it gives 0 they haven't if it says 1 they have liked it
I tried using this query
SELECT P.id AS id
, U.username AS username
, P.body AS body
, P.timestamp AS timestamp
, COUNT(L.user_id) AS likes
, COUNT(LD.post_id) AS liked
FROM posts AS P
LEFT JOIN users AS U ON U.id = P.user_id
LEFT JOIN followers AS F ON F.user_id = 'user1'
LEFT JOIN likes AS L ON L.post_id = P.id
LEFT JOIN likes AS LD ON LD.post_id = P.id
AND LD.user_id = 'user1'
WHERE F.following_id = P.user_id
OR P.user_id = 'user1'
GROUP BY P.id
My entrys in my likes table are
UserId|PostId|timestamp
user1 |post1 |time
user2 |post1 |time
My problem is it keeps giving a 2 for the count of LD which shouldn't be possible
*Note: In my code I use :user through PDO I don't actually type the id like that
Edit:
$sql = "SELECT P.id AS id, P.user_id AS userid, U.username AS username, U.name AS name, U.verified AS verified, P.body AS body, P.data AS data, P.timestamp AS timestamp, P.type AS type, P.users AS users, COUNT(L.user_id) AS likes, COUNT(DISTINCT LD.post_id) AS liked FROM posts AS P LEFT JOIN users AS U ON U.id = P.user_id LEFT JOIN followers AS F ON F.user_id = :userid LEFT JOIN likes AS L ON L.post_id = P.id LEFT JOIN likes AS LD ON LD.post_id = P.id AND LD.user_id = :userid WHERE F.following_id = P.user_id OR P.user_id = :userid GROUP BY P.id";
$results = DB::query($sql, array(':userid' => $user_id));
I then loop through the results and format them into json
Can you try adding a DISTINCT keyword on the COUNT function for liked column?
COUNT(DISTINCT LD.post_id) AS liked
Most likely the joins are causing the likes table to be duplicated. Thus, we'll only count the unique posts (by post_id) using DISTINCT.

Order all union results by timestamp

The query is
SELECT L.timestamp AS timestamp . . .
FROM like AS L
INNER JOIN users AS U
ON U.id = L.user_id
INNER JOIN posts AS P
ON P.user_id = :userid
WHERE L.post_id = P.id
UNION
SELECT P.timestamp . . .
FROM post AS P
INNER JOIN users AS U
ON U.id = P.user_id
WHERE FIND_IN_SET(:userid , P.users)
UNION
SELECT C.timestamp AS timestamp . . .
FROM comment AS C
INNER JOIN posts AS P
ON P.user_id = :userid
INNER JOIN users AS U
ON U.id = C.user_id
WHERE C.post_id = P.id
ORDER BY timestamp DESC
I tried adding the ORDER BY timestamp DESC at the end but it still isn't showing the latest one first. It's showing one from; 10 days ago, 9 days ago, 1 month ago, and 4 days ago, in that order.
You need to use the query as sub-query to do an ORDER BY on the whole result:
SELECT * FROM (
SELECT L.timestamp AS timestamp FROM like AS L
INNER JOIN users AS U ON U.id = L.user_id
INNER JOIN posts AS P ON P.user_id = :userid
WHERE L.post_id = P.id
UNION
SELECT P.timestamp . . . FROM post AS P
INNER JOIN users AS U ON U.id = P.user_id
WHERE FIND_IN_SET(:userid , P.users)
UNION
SELECT C.timestamp AS timestamp . . .
FROM comment AS C
INNER JOIN posts AS P ON P.user_id = :userid
INNER JOIN users AS U ON U.id = C.user_id
WHERE C.post_id = P.id
)x ORDER BY x.timestamp DESC
You do not need to put the entire query in a from clause. You can just put parentheses around each subquery. This is clearly stated in the documentation:
To use an ORDER BY or LIMIT clause to sort or limit the entire UNION
result, parenthesize the individual SELECT statements and place the
ORDER BY or LIMIT after the last one.
So:
(SELECT L.timestamp AS timestamp . . .
FROM like L JOIN
users U
ON U.id = L.user_id JOIN
posts P
ON P.user_id = :userid
WHERE L.post_id = P.id
) UNION
(SELECT P.timestamp . . .
FROM post P JOIN
users U
ON U.id = P.user_id
WHERE FIND_IN_SET(:userid , P.users)
) UNION
(SELECT C.timestamp AS timestamp . . .
FROM comment C JOIN
posts P
ON P.user_id = :userid JOIN
users U
ON U.id = C.user_id
WHERE C.post_id = P.id
)
ORDER BY timestamp DESC;
Note: If your subqueries are not returning duplicate rows, then change the UNION to UNION ALL. There is no need to incur the overhead of removing duplicates.

How can I calculate the number of all rows when there are both LIMIT and JOIN clauses?

Here is my query:
select u.id, u.name,
(select count(*) from users where name = u.name) as total
from users u
where u.name = 'anything'
order by id
limit 1
As you know, my query returns 1 user which has anything name. And total contains the number of all users which have anything name. Ok all fine.
Now I want to do the same thing when there are three JOINs in the query. Please assume this:
select u.id, u.name, sum(r.reputation) rep
from users u
join reputation r on u.id = r.user_id
join posts_tags pt on r.post_id = pt.post_id
join tags t on pt.tag_id = t.id
where u.name = 'anything' and t.name = 'mytag'
group by u.id, u.name
order by rep desc, u.id
limit 1
Now I want to know, how can I implement total part in this ^ query?
Try this query ,hope this give you the right result.
select * , count(id) as total from (select u.id, u.name, sum(r.reputation) rep
from users u
join reputation r on u.id = r.user_id
join posts_tags pt on r.post_id = pt.post_id
join tags t on pt.tag_id = t.id
where u.name = 'anything' and t.name = 'mytag'
group by u.id, u.name
order by rep desc, u.id
limit 1 ) as result

MySql Join tables based on field value

Is it possible to do something like this:
SELECT
p.*, u.*
FROM
posts AS p
IF(p.status = 1)
LEFT JOIN users AS u
ON u.id = p.user_id
ELSE
LEFT JOIN pusers AS u
ON u.id = p.user_id
WHERE p.id = 10 ;
Based on post status being true/false join users/pusers table
No, but you can do this:
SELECT p.*,
(case when p.status = 1 then u.col1 else pu.col1 end) as col1
FROM posts p LEFT JOIN
users u
ON u.id = p.user_id and p.status = 1 LEFT JOIN
pusers AS pu
ON pu.id = p.user_id and p.status <> 1
WHERE p.id = 10 ;
In other words, you can join both tables and use the values from the table, based on the condition.

How to excluding some records before left join executes

I'm looking for a way to excluding some records before i the left join executes.
My sql statement looks as follows:
SELECT * FROM users
LEFT JOIN (SELECT * FROM premissions WHERE post_id = 1) AS p
ON p.user_id = users.id
WHERE p.id IS NULL
How can I exclude the records with the id 1 and two in the user table?
You could add criteria to your WHERE clause:
SELECT *
FROM users
LEFT JOIN ( SELECT *
FROM premissions
WHERE post_id = 1
) AS p
ON p.user_id = users.id
WHERE p.id IS NULL
AND users.id NOT IN (1,2)
Use a subselect
SELECT * FROM
(select * from users where id not in(1,2))
u
LEFT JOIN (SELECT * FROM premissions WHERE post_id = 1) AS p
ON p.user_id = u.id
WHERE p.id IS NULL
SELECT * FROM
(select from users where id not in (1,2)) u
LEFT JOIN (SELECT * FROM premissions WHERE post_id = 1) AS p
ON p.user_id = u.id
WHERE p.id IS NULL
you can try to filter in where clause or do it as M Khalid Junaid has mentioned.
SELECT * FROM users
LEFT JOIN (SELECT * FROM premissions WHERE post_id = 1) AS p
ON p.user_id = users.id
WHERE p.id IS NULL
and users.id not in (1, 2)