SQL query that produces the following output? - mysql

I am trying to write an SQL statement producing the below output.
I have the two following tables:
UserMovie
userID | movieID
-----------------
135 | k0jps
135 | p1zka
125 | v0t67
115 | opp2s
111 | xnwri
115 | kspdl
Follows
followerid | followingid
------------------------
122 | 135
192 | 111
125 | 240
120 | 125
45 | 111
I want to fetch the number of followers of each user who's userid is in the UserMovie Table, giving the following result:
Result
userid | followerCount
----------------------
135 | 1
125 | 1
115 | 0
111 | 2
The following statement gives me partially what i want:
SELECT followingid, count(*) as followerCount
FROM Follows
WHERE followingid in (SELECT DISTINCT userID FROM UserMovie)
GROUP BY followingid
The issue with the above query is that users with 0 followers do not appear in the results giving the following output:
userid | followerCount
----------------------
135 | 1
125 | 1
111 | 2
Any idea on how to do it?

Try this to include users with no follows:
SELECT UserId, Count(followerid) AS followerCount
FROM (SELECT DISTINCT userId FROM UserMovie ) m
LEFT JOIN Follows f
ON f.followingid = m.userID
GROUP BY UserId
Now it generates :
UserId followerCount
111 2
115 0
125 1
135 1

The following worked for me.
However I am getting NULLs instead of 0 for users with no followers
SELECT DISTINCT u.userid, t.followerCount
FROM UserMovie u
LEFT JOIN (
SELECT followingid, count(*) AS followerCount
FROM Follows
WHERE followingid in (SELECT DISTINCT userID FROM UserMovie)
GROUP BY followingid ) as t
on t.followingid = u.userid

How about a solution using CASE?
SELECT userId,
CASE
WHEN IFNULL(followerid, 0) = 0 THEN 0
ELSE count(*)
END
FROM UserMovie
LEFT JOIN Follows on followingid=userID
GROUP BY userId;
Seems to work fine in SQLite3, just replace IFNULL with ISNULL (if SQLServer) or any other equivalent. It's pretty similar to what you've done.

Here's one approach: get a distinct list of userID from UserMovie in an inline view (use either a GROUP BY or a DISTINCT keyword), and perform an "outer join" operation of that to the Followers table to find followers. Collapse the rows from that with a GROUP BY, and use an aggregate function to get a count of unique/distinct non-null values of userId from the Followers table.
For example:
SELECT u.userID
, COUNT(DISTINCT f.userID) AS cnt_followers
FROM ( SELECT m.userID
FROM UserMovie m
GROUP BY m.userID
) u
LEFT
JOIN Follows f
ON f.followingid = u.userID
GROUP BY u.userID
EDIT
There's an invalid column reference in the SELECT list, f.userID is not valid. That should be f.followerID.
When we fix that, the query returns:
userID cnt_followers
111 2
115 0
125 1
135 1
SQL Fiddle HERE http://sqlfiddle.com/#!9/de3e7/2
As long as we are counting "distinct" followerid (question doesn't give any guarantee that (followerID,followingID) is UNIQUE in Followers table), we could eliminate the inline view
SELECT u.userID
, COUNT(DISTINCT f.userID) AS cnt_followers
FROM UserMovie u
LEFT
JOIN Follows f
ON f.followingid = u.userID
GROUP BY u.userID

Related

mysql How to select an ID when it's values are equal to X

I have tried quite a lot of solutions and I decided to post it here to try and find a solution. Any little help is welcome (so I can learn too).
I have a table formed by ArticleID, UserID, and Votes (1/-1).
I want to select the ArticleID that contains a certain UserID and which SUM of Votes is equal to 1.
So far I arrived to:
SELECT catch.ID, votes.postid, catch.text, votes.userid, votes.value, catch.name FROM catch INNER JOIN votes ON catch.ID=votes.postid AND votes.userid=:iduser AND votes.value='1' ORDER BY ID DESC LIMIT 100
but this gives me an erroneous result, as it doesn't consider articles that have votes 1 and -1 (which SUM should be 0).
Thanks!
UPDATE
ID + Value + userid
1 | 1 | 54
1 | -1 | 54
3 | 1 | 54
7 | 1 | 56
7 | -1 | 56
Given the above table, and selecting just the user '54' the wanted result should be ID 3.
Is this what you want?
SELECT c.ID, v.postid, c.text, v.userid, v.value, c.name
FROM catch c INNER JOIN
votes v
ON c.ID = v.postid AND v.userid = :iduser
GROUP BY c.ID
HAVING SUM(v.value) = 1;
This is what you describe but it is a bit different from your query.
Try like this, but for :iduser set what particular id of user
SELECT catch.ID,
votes.postid,
catch.text,
votes.userid,
SUM(votes.value),
catch.name
FROM catch
LEFT JOIN votes ON catch.ID=votes.postid
WHERE votes.userid = :iduser and votes.value='1'
ORDER BY ID DESC
LIMIT 100

Find duplicates from same table and constraint them from another table in sql

Oh, my title is not the best one and as English is not my main language maybe someone can fix that instead of downvoting if they've understood the issue here.
Basically i have two tables - tourneyplayers and results. Tourneyplayers is like a side table which gathers together tournament information across multiple tables - results, tournaments, players etc. I want to check duplicates from the results table over column day1_best, from single tournament and return all the tourneyplayers who have duplicates.
Tourneyplayers contain rows:
Tourneyplayers
tp_id | resultid | tourneyid
1 | 2 | 91
2 | 21 | 91
3 | 29 | 91
4 | 1 | 91
5 | 3 | 92
Results contains rows:
Results:
r_id | day1_best
1 | 3
2 | 1
3 | 4
.. | ..
21 | 1
.. | ..
29 | 2
Now tourney with id = 91 has in total 4 results, with id's 1,2,21 and 29. I want to return values which have duplicates, so currently the result would be
Result
tp_id | resultid | day1_best
1 | 2 | 1
2 | 21 | 1
I tried writing something like this:
SELECT *
FROM tourneyplayers
WHERE resultid
IN (
SELECT r1.r_id
FROM results AS r1
INNER JOIN results AS r2 ON ( r1.day1_best = r2.day1_best )
AND (
r1.r_id <> r2.r_id
)
)
AND tourneyid =91
But in addition to values which had the same day1_best it chose two more which did not have the same. How could i improve my SQL or rewrite it?
First you JOIN both tables, so you know how the data looks like.
SELECT *
FROM tourney_players t
JOIN results r
ON t.`resultid` = r.`r_id`;
Then using the same query you GROUP to see what tourneyid, day1_best combination has multiple rows
SELECT `tourneyid`, `day1_best`, count(*) as total
FROM tourney_players t
JOIN results r
ON t.`resultid` = r.`r_id`
GROUP BY `tourneyid`, `day1_best`;
Finally you use the base JOIN and perform a LEFT JOIN to see what rows has a match and show only those rows.
SELECT t.`tp_id`, r.`r_id`, r.`day1_best`
FROM tourney_players t
JOIN results r
ON t.`resultid` = r.`r_id`
LEFT JOIN (SELECT `tourneyid`, `day1_best`, count(*) as total
FROM tourney_players t
JOIN results r
ON t.`resultid` = r.`r_id`
GROUP BY `tourneyid`, `day1_best`
HAVING count(*) > 1) as filter
ON t.`tourneyid` = filter.`tourneyid`
AND r.`day1_best` = filter.`day1_best`
WHERE filter.`tourneyid` IS NOT NULL;
SQL DEMO
OUTPUT
Please try this :
Select tp.tp_id , tp.resultid ,r.day1_best from (Select * from Tourneyplayers
where tourneyid = 91)as tp inner join (select * from Result day1_best in(select
day1_best from result group by day1_best having count(*)>1 ) )as r on tp.resultid
= r.r_id ;

difficulties getting a 3 table join to return expected results

I'm having some difficulty getting to the bottom of this sql query.
Tables:
--Tickets-- --Finance-- --Access--
id_tickets id_finance id_access
name_tickets id_event id_event
cat_tickets id_tickets id_tickets
sold_finance scan_access
Finance and Access both contain a row for multiple of each ticket type as listed in tickets.
and I'm trying to get:
cat_tickets | total_sold | total_scan
-------------------------------------
single | 3043 | 2571
season | 481 | 292
comp | 114 | 75
-------------------------------------
total | 3638 | 2938
The closest I've been to the result I've used:
SELECT tickets.cat_tickets, COALESCE(SUM(finance.sold_finance), 0) AS total_sold, COALESCE(SUM(access.scan_access), 0) AS total_scan
FROM finance INNER JOIN tickets ON finance.id_tickets = tickets.id_tickets
INNER JOIN access ON access.id_tickets = tickets.id_tickets
WHERE access.id_event = 235 AND finance.id_event = access.id_event
GROUP BY tickets.cat_tickets
ORDER BY tickets.cat_tickets DESC
but that just returns:
cat_tickets | total_sold | total_scan
-------------------------------------
single | 4945 | 4437
season | 954 | 599
comp | 342 | 375
-------------------------------------
total | 6241 | 5411
Any ideas where I could be going wrong?
Thanks!
The problem is the relation between access and finance tables, you have to join them. Even if you LEFT JOIN the table the predicate finance.id_event = access.id_event will make it INNER JOIN. As a work around, use UNION like this:
SELECT
tickets.cat_tickets,
SUM(CASE WHEN a.Type = 'f' THEN num ELSE 0 END) AS total_sold,
SUM(CASE WHEN a.Type = 'a' THEN num ELSE 0 END) AS total_scan
FROM tickets
LEFT JOIN
(
SELECT 'f' Type, id_tickets, sold_finance num
FROM finance f
WHERE id_event = 1
UNION ALL
SELECT 'a', id_tickets, scan_access
FROM access
WHERE id_event = 1
) a ON a.id_tickets = tickets.id_tickets
GROUP BY tickets.cat_tickets;
SQL Fiddle Demo
Although I am fully clear on what you want, just try this query if the result of this is what you are expecting.
SELECT tickets.cat_tickets, COALESCE(SUM(finance.sold_finance), 0) AS total_sold, COALESCE(SUM(access.scan_access), 0) AS total_scan
FROM finance LEFT JOIN tickets ON finance.id_tickets = tickets.id_tickets
LEFT JOIN access ON access.id_tickets = tickets.id_tickets
WHERE access.id_event = 235
GROUP BY tickets.cat_tickets
ORDER BY tickets.cat_tickets DESC
Disclaimer: This query is not tested due to incomplete data on the question.
SELECT z.Cat_tickets,
COALESCE(x.total_sold,0) total_sold,
COALESCE(y.total_scan,0) total_scan
FROM tickets z
LEFT JOIN
(
SELECT a.id_tickets,
a.cat_tickets,
SUM(b.sold_finance) total_sold
FROM tickets a
INNER JOIN finance b
ON a.id_tickets = b.id_tickets
WHERE id_event = 235
GROUP BY a.id_tickets, a.cat_tickets
) x ON z.id_tickets = x.id_tickets
LEFT JOIN
(
SELECT aa.id_tickets,
aa.cat_tickets,
SUM(bb.scan_access) total_scan
FROM tickets aa
INNER JOIN Access bb
ON aa.id_tickets = bb.id_tickets
WHERE id_event = 235
GROUP BY aa.id_tickets, aa.cat_tickets
) y ON z.id_tickets = y.id_tickets

mysql query with count

I'm running into my own limits of MySQL query skills, so I hope some SQL guru can help out on this one. I have 2 tables:
Table "comments"
comment_ID | comment_post_ID
120 | 620
121 | 620
122 | 620
Table "comments_like"
like_ID | comment_ID
1 | 120
2 | 120
I will result the numbers of comment that have a like group by comment_post_ID:
COUNT(comments in comments like) | comment_post_ID
1 | 620
This query give the post_id and the no of like for its comment in comment_like table
SELECT comment_post_ID, count(like_ID)
from (select distinct comment_post_ID,like_ID
from comments_like cl , comments c
where cl.comment_ID= c.comment_ID ) as iq
group by comment_post_ID
I guess a subquery is the easiest here:
SELECT (SELECT COUNT(like_ID) FROM `comments_like` cl WHERE cl.comment_ID=c.comment_ID) AS comments_in_comments_like, c.comment_post_ID FROM `comments` c
This will produce count of 2 though for likes since comment id 120 has 2 likes in your example
select cmtLike.like_ID, commnts.comment_post_ID
from comments_like cmtLike left join comments commnts on cmtLike.comment_ID = commnts.comment_ID
If you need to count each comment listed in comments_like just once, then:
select comment_post_ID, count(distinct cl.comment_ID)
from comments c, comments_like cl
where c.comment_id = cl.comment_id
group by comment_post_ID

help with mysql joins/grouping

I'll try to make this simple.
This query lists my sites users, their total orders, cards, and addresses on file.
For some reason if the user has 2 addresses but only 1 order, it will show the customer has 2 orders.
For example: user4 actually only has 1 order, but shows 2 and im assuming its because he has 2 addresses and it has something to do with the joins or grouping.
SELECT
users.user_email,
users.user_firstname,
users.user_lastname,
users.user_joindate,
users.user_logindate,
Count(users_addresses.usera_id) AS count_addr,
Count(users_cards.userc_id) AS count_cards,
Count(orders.order_id) AS count_orders,
Sum(orders.order_total) AS sum_ordertotal
FROM
users
LEFT JOIN users_addresses ON users_addresses.usera_userid = users.user_id
LEFT JOIN users_cards ON users_cards.userc_userid = users.user_id
LEFT JOIN orders ON orders.order_userid = users.user_id
GROUP BY
users.user_id
ORDER BY user_id DESC
LIMIT 5
Example ouput:
userid | orders | addresses | cards
------ ----------------------------
user4 | 2 | 2 | 0
user3 | 0 | 0 | 0
user2 | 1 | 1 | 0
user1 | 0 | 1 | 0
One possibility is to use COUNT(DISTINCT orders.order_id). However, this doesn't help with the Sum().
Another possibility (albiet less efficient) is to use subqueries for the counts.
SELECT
users.user_email,
users.user_firstname,
users.user_lastname,
users.user_joindate,
users.user_logindate,
(SELECT count(*) FROM users_addresses WHERE users_addresses.usera_id = users.user_id) AS count_addr,
(SELECT count(*) FROM user_cards WHERE users_cards.userc_id = users.user_id) AS count_cards,
Count(orders.order_id) AS count_orders,
Sum(orders.order_total) AS sum_ordertotal
FROM users
LEFT JOIN orders ON orders.order_userid = users.user_id
GROUP BY users.user_id
ORDER BY user_id DESC
LIMIT 5
As suggested before:
count(distinct(orders.order_id)) as count_orders
And you could use:
(select sum(order_total) from orders where user_id = users.user_id) as sum_ordertotal