Count in Joins in SQL - php

i have two tables (post.id is primary key which become seconday key in like.post_id )
table "post"
id user_id image
1 10 abc.jpg
2 20 xyz.jpg
3 10 ajb.jpg
Table "Like"
id user_id post_id likes
1 10 1 1
2 20 2 1
3 10 1 1
4 10 1 1
3 10 3 1
now i want whenever i pass user_id then i wan to get all post of users with number of likes of posts
i tried with following code but not worked,
SELECT selfie_info.id,selfie_info.user_id,selfie_info.image, (SELECT COUNT(m.likes)FROM post_likes m WHERE m.user_id='10') as total_likes FROM selfie_info where user_id='10'
how can i do this ? i want result like following (if i pass user_id=10 )
user_id post_id likes
10 1 3
10 3 1

SELECT p.user_id, p.id AS post_id, COUNT(l.id) AS total_likes
FROM post p
LEFT JOIN likes l ON l.post_id =p.id
WHERE p.user_id=10 GROUP BY p.id;

You can use aggregation:
select pl.post_id, count(*) as numlikes,
si.*
from selfie_info si join
post_likes pl
on si.user_id = pl.user_id
where si.user_id = 10
group by si.user_id, si.post_id
You can use si.* in the select, even though this is a group by query because you are aggregating by the primary key/unique id in the table.

You have all necessary data in table "like"
You can use one select from "like" table.
select user_id, post_id, sum(likes) as likes from like where user_id = 10;
You need to count or sum likes?
Depends on you needs You can sum(likes) or count(*) likes

Related

How To Get All User(s) Who Have AND Have't Give Their Comment

I'm trying to generate a list of those who have commented and not per post like this:
id
post_id
user_id
text
created_at
1
1
1
test
timestamp
2
1
2
test
timestamp
3
1
3
-
-
4
1
4
-
-
5
1
5
-
-
6
1
6
-
-
7
2
1
-
-
8
2
2
-
-
9
2
3
-
-
10
2
4
-
-
11
2
5
test
timestamp
12
2
6
-
-
Here are my tables:
Posts Table:
id
title
created_at
1
Post 1
timestamp
2
Post 2
timestamp
Comments Table :
id
post_id
user_id
text
created_at
1
1
1
test
timestamp
2
1
2
test
timestamp
3
2
5
test
timestamp
Users Table :
id
name
created_at
1
Alfa
timestamp
2
Bravo
timestamp
3
Charlie
timestamp
4
Delta
timestamp
5
Echo
timestamp
6
Foxtrot
timestamp
Is this possible? I already tried this code below, but when I filter it with post_id, it only shows users who have commented.
QueryBuilder::for(Comments::class)
->rightJoin(
'users',
'comments.user_id',
'users.id',
)
->select(
'comments.id',
'users.id',
'comments.post_id',
'comments.text',
'comments.updated_at'
)
->allowedFilters([
'post_id',
])
With MySQL you can do this by trying code below:
SELECT case when comments.id is null then '-' else comments.id end,
case when posts.id is null then '-' else posts.id end,
case when users.id is null then '-' else users.id end,
case when comments.text is null then '-' else comments.text end,
case when comments.created_at is null then '-' else comments.created_at end
FROM posts
LEFT JOIN comments ON comments.post_id=posts.id
LEFT JOIN users ON users.id=comments.user_id
Use a cross join to generate the rows and then left join to bring in existing data:
select p.id as post_id, u.id as user_id, c.text, c.created_at
from posts p cross join
users u left join
comments c
on c.post_id = p.id and c.user_id = u.id
order by p.post_id, u.user_id;
This does not enumerate the rows. If you want that, you can use row_number() for the first column:
select row_number() over (order by p.id, u.id) as id,
p.id as post_id, u.id as user_id, c.text, c.created_at
from posts p cross join
users u left join
comments c
on c.post_id = p.id and c.user_id = u.id
order by p.post_id, u.user_id;

How to get avg rating per product using codeigniter query?

I have rating table which have rating for every product given by user, I am retrieving all rating records, but at the same time I want to get avg rating based on per product but I am unable to get ouptput
Query :
$this->db->select('ratings.*');
$this->db->select('select AVG(ratings) as avg_rating from ratings group by product_id');
$this->db->from('ratings');
$this->db->join('users','users.id=ratings.user_id');
$this->db->get()->result();
Rating table
id user_id product_id rating
1 4 1 4
2 5 2 4
3 6 1 2
4 7 4 4
Expected output:
id user_id product_id rating avg rating
1 4 1 4 3
2 5 2 4 4
3 6 1 2 3
4 7 4 4 4
get data from table ratings, using a left join with select for the average.
the join() function of Codeigniter allows you to to write a select part instead of the table name, but you need to place it into parenthesis:
$this->db->select('t1.*, t2.avg_rating, t3.*');
$this->db->from('ratings t1');
$this->db->join('
(select product_id, avg(rating) as avg_rating
from ratings
group by product_id) t2','t2.product_id=t1.product_id','left'
);
$this->db->join('users t3','t3.id=t1.user_id','left');
$this->group_by('t1.userid')
$this->db->get()->result();
generates:
SELECT t1.*, t2.avg_rating, t3.*
FROM ratings t1
left join
(select product_id, avg(rating) as avg_rating from ratings group by product_id) t2
on t2.product_id=t1.product_id
left join users t3
on t1.user_id = t3.id
group by t1.user_id
and outputs as you expect.
When Queries get complicated i like to use query instead of query builder. You could do this:
$this->db->query('select r.*,(select round(sum(r2.rating)/count(*),0) from ratings r2 where r2.product_id = r.product_id ) as 'avg rating' from ratings r')->result();

SQL select posts that have similar tags

In my MariaDB I have a table called
"Posts" with
id, title, username_id, text, image_url, url
one called "tags" with
id, tag
and one called post_tags with
id, post_id, tag_id.
The thing I want to accomplish is to get 3 posts from the "Posts" table that have the most tags in common with the post that's currently shown on the page.
I'm stuck here not knowing where to start.
EDIT
Posts
id | username_id | title | text | image_url | url
1 1 example example_text localhost/image.jpg localhost/first-post
2 1 example1 example_text localhost/image1.jpg localhost/second-post
3 1 example2 example_text localhost/image2.jpg localhost/third-post
4 1 example4 example_text localhost/image4.jpg localhost/fourth-post
... ... ... ... ... ...
... ... ... ... ... ...
Tags
id | tag
1 herbs
2 flower
3 rose
Post_tags
id | post_id | tag_id
1 1 1
2 1 2
3 1 3
4 2 1
5 3 1
6 3 2
7 4 1
8 4 2
9 4 3
I'd like to return an array with posts.title and posts.image_url selecting the posts that have the most post_tags.tag_id in common with the current one.
As you can see, if we take post n. 1 as the selected post, post n. 4 has the most tags in common with it, post n.3 is in the second position and post n.2 is the third.
example4 | localhost/image4.jpg
example3 | localhost/image3.jpg
example2 | localhost/image2.jpg
I hope I made it clearer. Thank you.
SELECT p.id, p.title, p.image_url, COUNT(*) as how_many_shared_tags
FROM posts p
JOIN post_tags pt ON pt.post_id = p.id
AND pt.tag_id IN(SELECT tag_id FROM post_tags WHERE post_id = 1)
WHERE p.id != 1
GROUP BY p.id, p.title, p.image_url
order by COUNT(*) DESC
LIMIT 3
As requested, explanation for query:
In order to find top 3 posts that share most tags with our "parent" post, we first need to get list of tags that "parent" has => SELECT tag_id FROM post_tags WHERE post_id = 1
Then find posts that have at least one of those tags by searching in table that holds both ID for posts and tags by adding condition tag_id IN(LIST_OF_tag_id_FROM_SUB_SELECT_SHOWN_ABOVE).
Now we know what posts share at least one tag with "parent", so we can count how many tags they have actually in common and sort by it => order by COUNT(*) DESC
Because "parent" post also "shares" those tags and we don't want him in our result, we give additional condition excluding ID of our "parent" => WHERE p.id != 1
Finally we limit result set to 3 rows, because we want only top 3. LIMIT 3
Selecting count is not required, it is only to point out what it counts COUNT(*) as how_many_common_tags
Check this if it gives what you want to get. Then you may need to optimize query.
SELECT
t1.*
FROM posts t1,
(
SELECT
post_id
FROM post_tags t2
WHERE
tag_id IN (SELECT tag_id FROM post_tags WHERE post_id = $CURRENT_POST_ID)
AND NOT post_id = $CURRENT_POST_ID
GROUP BY post_id
ORDER BY COUNT(tag_id) DESC
LIMIT 3
) t2
WHERE
t1.id = t2.post_id
This will get you just the post_id values:
SELECT x.post_id
FROM
(
SELECT b.post_id
FROM Post_tags a
JOIN Post_tags b USING(tag_id)
WHERE a.post_id = 1234
AND b.post_id != a.post_id
) x
GROUP BY x.post_id
ORDER BY COUNT(*) DESC
LIMIT 3;
Tips on designing a better mapping table (Post_tags). That will give you the optimal indexes for that table.
To get more info on the 3 posts:
SELECT p.*
FROM
(
SELECT x.post_id
FROM
(
SELECT b.post_id
FROM Post_tag a
JOIN Post_tag b USING(tag_id)
WHERE a.post_id = 1234
AND b.post_id != a.post_id
) AS x
GROUP BY x.post_id
ORDER BY COUNT(*) DESC
LIMIT 3
) AS y
JOIN Posts AS p ON p.id = y.post_id;

mysql multiple COUNT() from multiple tables with LEFT JOIN

I want to show the conclusion of all users.
I have 3 tables.
table post
post_id(index) user_id
1 1
2 3
3 3
4 4
table photo
photo_id(index) user_id
1 2
2 4
3 1
4 1
table video
photo_id(index) user_id
1 4
2 4
3 3
4 3
and in table user
user_id(index) user_name
1 mark
2 tommy
3 john
4 james
in fact, it has more than 4 rows for every tables.
I want the result like this.
id name post photo videos
1 mark 1 2 0
2 tommy 0 1 0
3 john 2 0 2
4 james 1 1 2
5 .. .. .. ..
Code below is SQL that can work correctly but very slow, I will be true appreciated if you help me how it using LEFT JOIN for it. Thanks.
SQL
"select user.*,
(select count(*) from post where post.userid = user.userid) postCount,
(select count(*) from photo where photo.userid = user.userid) photoCount,
(select count(*) from video where video .userid = user.userid) videoCount
from user order by user.id"
(or ORDER BY postCount, photoCount or videoCount ASC or DESC as i want )
I done researched before but no any helped me.
SELECT u.user_id,
u.user_name,
COUNT(DISTINCT p.post_id) AS `postCount`,
COUNT(DISTINCT ph.photo_id) AS `photoCount`,
COUNT(DISTINCT v.video_id) AS `videoCount`
FROM user u
LEFT JOIN post p
ON p.user_id = u.user_id
LEFT JOIN photo ph
ON ph.user_id = u.user_id
LEFT JOIN video v
ON v.user_id = u.user_id
GROUP BY u.user_id
ORDER BY postCount;
Live DEMO
Your method of doing this is quite reasonable. Here is your query:
select user.*,
(select count(*) from post where post.userid = user.userid) as postCount,
(select count(*) from photo where photo.userid = user.userid) as photoCount,
(select count(*) from video where video.userid = user.userid) as videoCount
from user
order by user.id;
For this query, you want the following indexes:
post(userid)
photo(userid)
video(userid)
user(id)
You probably already have the last one, because user.id is probably the primary key of the table.
Note that a left join approach is a bad idea in this case. The three tables -- posts, photos, and videos -- are independent of each other. If a user has five of each, then joining them together would produce 125 intermediate rows. If a user has fifty of each, it would be 125,000 -- a lot of extra processing.
Your answer is probably slow as it is using a correlated sub-query i.e. the sub query is running once for each user_id (unless the optimizer is doing something smart - which shouldn't be counted on).
You could use a left outer join and count or use something temporary like:
SELECT u.user_id,
u.user_name,
ph.user_count AS 'photoCount',
p.user_count AS 'postCount',
v.user_count AS 'videoCount'
FROM user u
INNER JOIN ( SELECT user_id,
COUNT(*) AS user_count
FROM photo
GROUP BY user_id
) ph
ON ph.user_id=u.user_id
INNER JOIN ( SELECT user_id,
COUNT(*) AS user_count
FROM post
GROUP BY user_id
) p
ON p.user_id=u.user_id
INNER JOIN ( SELECT user_id,
COUNT(*) AS user_count
FROM video
GROUP BY user_id
) v
ON v.user_id=u.user_id
There are pros and cons for both (depending on indexes). Always have a look at the query plan (using EXPLAIN for MySQL).

adding and multiplying COUNT() of several tables

Is it possible to add and multiply the count of different tables where the id is the same?
Imagine:
Table_1 Table_2 Table_3
id id id
1 1 1
1 2 2
2 2 3
3 2 3
3 2 3
3 3 3
So that the end result would be this table with 2 columns:
id (COUNT(Table_1.id) + 2*COUNT(Table_2.id) + 3*COUNT(Table_3.id))
1 7
2 12
3 17
I don't know if I understood you correctly but give this a try,
SELECT a.ID,
a.aa + (2 * b.bb) + (3 * c.cc)
FROM
(
SELECT ID, COUNT(*) aa
FROM table1
GROUP BY ID
) a LEFT JOIN
(
SELECT ID, COUNT(*) bb
FROM table2
GROUP BY ID
) b ON a.ID = b.ID
LEFT JOIN
(
SELECT ID, COUNT(*) cc
FROM table3
GROUP BY ID
) c ON a.ID = c.ID
SQLFiddle Demo
SELECT id, counts_1.number + 2 * counts_2.number + 3 * counts_3.number
FROM
(SELECT id, COUNT(*) AS number FROM Table_1 GROUP BY id) AS counts_1
JOIN
(SELECT id, COUNT(*) AS number FROM Table_2 GROUP BY id) AS counts_2 USING (id)
JOIN
(SELECT id, COUNT(*) AS number FROM Table_3 GROUP BY id) AS counts_3 USING (id)
Note that this solution requires that every id exists at least once in each of the tables, otherwise it will be left out of the result. Changing this would require a FULL OUTER JOIN that MySQL is incapable of. There are ways around that limitation, though.

Categories