SQL nested count query - mysql

I have these tables:
Discussion
----------
DiscussionID (Primary Key)
Name
....
UserDiscussion
--------------
UserID (Primary Key)
DiscussionID (Primary Key)
Participated (Boolean)
Bookmarked (Boolean)
and I need to query 2 things:
All discussions where a certain user has participated
For every participated discussion I need the total count of users who bookmarked the discussion
Here is my current query:
SELECT d.DiscussionID, d.Name
FROM Discussion d LEFT JOIN
UserDiscussion ud ON ud.DiscussionID = d.DiscussionID
WHERE ud.UserID = 1 AND ud.Participated = true;
Sample data:
Discussion:
DiscussionID | Name |
---------------------
1 First
UserDiscussion:
UserID | DiscussionID | Participated | Bookmarked |
===================================================
1 1 1 1
4 1 1 0
Output should look like this:
Discussions: [
{
DiscussionID: 1,
Name: "First",
BookmarkCount: 1
}
]

SELECT
d.DiscussionID
,d.Name
,Participated = SUM(CASE WHEN CAST(ISNULL(b.Participated,0) = 1 THEN 1 ELSE 0 END)
,Bookmarked = SUM(CASE WHEN CAST(ISNULL(b.Bookmarked,0) = 1 THEN 1 ELSE 0 END)
FROM
Discussion d
INNER JOIN UserDiscussion ud
ON d.DiscussionID = ud.DiscussionID
AND ud.Participated = true;
LEFT JOIN UserDiscussion b
ON d.DiscussionId = b.DiscussionId
GROUP BY
d.DiscussionID
,d.Name

You could use a temp table to get a count of the bookmarks related to each discussion ID
select DiscussionID, count(Bookmarked) as 'Bookmarked'
into #Bookmarks
from UserDiscussion
group by DiscussionID
then, join it to the final table on DiscussionID and you can use the count as a column.
SELECT d.DiscussionID, d.Name, b.Bookmarked
FROM Discussion d LEFT JOIN
UserDiscussion ud ON ud.DiscussionID = d.DiscussionID
left join #Bookmarked b on ud.DiscussionID = b.DiscussionID
WHERE ud.UserID = 1 AND ud.Participated = true;

You can use a in clause with a subqyery ,, and count
SELECT a.DiscussionID, a.Name, b.count(*)
FROM discussion a
INNER JOIN UserDiscussion b
WHERE a.DiscussionID in (
SELECT d.DiscussionID
FROM Discussion d
INNER JOIN UserDiscussion ud ON ud.DiscussionID = d.DiscussionID
WHERE ud.UserID = 1 AND ud.Participated = true
)
AND b.Bookmarked = true;
group by a.DiscussionID, a.Name

One way is to write a view for number of times bookmarked
CREATE OR REPLACE VIEW v_times_bookmarked (
discussion_id,
times_bookmarked ) AS
SELECT discussion_id, count(*)
FROM userdiscussion
WHERE Bookmarked = true
GROUP BY discussion_id
Then in your main query, left join to the view.

sound like you have the 1st query the 2d should be like
SELECT DiscussionID, count(UserID)
FROM UserDiscussion
WHERE DiscussionID in (SELECT DiscussionID from UserDiscussion WHERE ud.UserID = 1 AND ud.Participated = true)
AND Bookmarked = true
GROUP BY DiscussionID;
then eventually you need to join them
With the posted correction:
SELECT d.DiscussionID, d.Name, BookmarkCount
FROM Discussion d
JOIN UserDiscussion ud ON ud.DiscussionID = d.DiscussionID
JOIN (
SELECT DiscussionID as DID, count(UserID) as BookmarkCount
FROM UserDiscussion
WHERE DiscussionID in (SELECT DiscussionID from UserDiscussion WHERE
ud.UserID = 1 AND ud.Participated = true)
AND Bookmarked = true
GROUP BY DiscussionID
) ON d.DiscussionID=DID;

Related

MySql query on 3 different tables

I have 3 tables.
Owners:
ownerID name
1 josh
Pets:
petID name
1 M
2 x
3 f
4 h
PetsOwners:
petID ownerID
1 1
3 1
4 1
I have a query that returns the ownerID from a person. "SELECT ownerID FROM Owners WHERE name = 'josh';" This will return ownerID = 1. I need a query that returns all pets that josh owns. In this case will be "m", "f" and "h" according to the petsOwners table.
If you have ownerId use
SELECT p.name
FROM Pets p
JOIN PetsOwners po
ON p.petID = po.petID
WHERE po.ownerID = 1
If you only have the owner name, need join all 3 tables
SELECT p.name
FROM Pets p
JOIN PetsOwners po
ON p.petID = po.petID
JOIN Owners o
ON po.ownerID = o.ownerID
WHERE o.name = 'josh'
If you just want their names:
SELECT Pets.name
FROM Pets, PetsOwners, Owners
WHERE Pets.petID = PetsOwners.petID
AND Owners.ownerID = PetsOwners.ownerID;
try this:
select a.ownerID,a.`name`as OwnerName, b.petID,b.`name` as PetName from
(select ownerID `name` from Owners) as a
right join
(select a.petID,a.`name`,OwnerID from
(select petID,`name` from Pets) as a
left JOIN
(select petID,OwnerID from PetsOwners) as b
on a.petID = b.petID) as b
on a.ownerID = b.OwnerID
I see your question and this is easy you see the query I wrote blow:
SELECT links.`link`,
links.`link_id`
FROM links
WHERE links.`link_id` NOT IN
(SELECT Y.`link_id`
FROM users X
INNER JOIN user_visited Y ON X.`user_id` = Y.`user_id`
WHERE X.`user_id` = 22 );

SQL query returns column with data from another column

I have a table Notices connected to tables Likes and Comments. When I return the notices for a user I also create columns: number_of_likes, number_of_comments and liked_by_me. The query is working correctly when the user making the query hasn't liked a notice (liked_by_me = 0) . But if they have (liked_by_me = 1) the value I get for number_of_likes is wrong and is the same as number_of_comments.
Example:
1)
- liked by me = false
- likes = 1
- comments = 5
Returned values:
- liked_by_me = 0
- number_of_likes = 1
- number_of_comments = 5
2)
- liked by me = true
- likes = 2
- comments = 5
Returned values:
- liked_by_me = 1
- number_of_likes = 5
- number_of_comments = 5
Here is the query I am using:
SELECT notices.*
, count(comment.id) as number_of_comments
, count(like1.user_id) as number_of_likes
, like2.user_id IS NOT NULL AS liked_by_me
, boards.name as board_name
FROM notices
LEFT JOIN comments as comment
ON (comment.notice_id = notices.id)
LEFT JOIN likes as like1
ON (like1.notice_id = notices.id)
LEFT JOIN likes as like2
ON (like2.notice_id = notices.id
AND like2.user_id = $1)
LEFT JOIN boards
ON (boards.id = notices.board_id)
LEFT OUTER JOIN board_users
ON (board_users.board_id = notices.board_id)
WHERE board_users.user_id = $1
GROUP BY notices.id
, boards.name
, like2.user_id
, userId
Any help would be appreciated. I have been on this for hours and I don't think I will be able to find the problem.
Thanks!
Solution:
Here is the working query
SELECT notices.*,
(SELECT COUNT(user_id) from likes WHERE likes.notice_id = notices.id) AS number_of_likes,
(SELECT user_id IS NOT NULL from likes WHERE likes.notice_id = notices.id AND likes.user_id = $1) AS liked_by_me,
count(comments.id) as number_of_comments, boards.name as board_name
FROM notices LEFT JOIN comments ON (comments.notice_id = notices.id)
LEFT JOIN boards ON (boards.id = notices.board_id)
LEFT OUTER JOIN board_users ON (board_users.board_id = notices.board_id)
WHERE board_users.user_id = $1 GROUP BY notices.id, boards.name", user);
You will have to use subeselects.
Excellent article on this problem: The GROUPing pitfall
TL;DR: Basically, you have to realize, that all your comments and likes are being multiplicated by one another. Try to display the result of the query without the group clause to see, that duplicate likes/comments are being counted.
EDIT: I didn't test this, but it's how the query might look:
(that is if user can only like one notice once, otherwise you would have to group current user likes too)
SELECT
notices.*,
comments.number_of_comments,
likes.number_of_likes
current_user_likes.user_id IS NOT NULL AS liked_by_me
boards.name AS board_name
FROM notices
LEFT JOIN (
SELECT
COUNT(*) AS number_of_comments,
notice_id
FROM comments
GROUP BY notice_id
) AS comments ON comments.notice_id = notices.id
LEFT JOIN (
SELECT
COUNT(*) AS number_of_likes,
notice_id
FROM likes
GROUP BY notice_id
) AS likes ON likes.notice_id = notices.id
LEFT JOIN likes AS current_user_likes
ON current_user_likes.notice_id = notices.id
AND current_user_likes.user_id = $1
LEFT JOIN boards ON boards.id = notices.board_id
INNER JOIN board_users
ON board_users.board_id = notices.board_id
AND board_users.user_id = $1;

MySql count result from another table not return correct number

In my query i want to count how much user have photos and how much have pets. When i count how much pets he have that work good. When i try to count how much photos have than not work. He return me wrong result.
Full query is :
SELECT
acc.account_id,
CONCAT(acc.account_firstname,' ', acc.account_lastname) AS full_name,
acc.account_username AS username,
acc.account_website_url AS website,
g.gender_name AS gender,
acc.account_birthday AS birthday,
acc_t.account_type_name AS account_group,
(SELECT COUNT(*) FROM pb_account_photos WHERE owner_id = acc.account_id) AS photoCount,
(SELECT COUNT(*) FROM pb_account_photo_albums WHERE album_account_id = acc.account_id) AS photoAlbumCount,
CONCAT(country.country_name,'/',city.city_name) AS location,
COUNT(p.pet_id) AS petCount,
(SELECT CONCAT(s.value, '/uploads/user_data/',acc.account_id,'/photos/',photo.photo_guid, '/',.photo.photo_name) FROM pb_settings AS s WHERE s.key = 'siteurl') AS profile_picture,
(SELECT CONCAT(s.value, '/uploads/user_data/',acc.account_id,'/photos/',cover.photo_guid, '/',.cover.photo_name) FROM pb_settings AS s WHERE s.key = 'siteurl') AS cover_picture
FROM pb_accounts AS acc
LEFT JOIN pb_account_genders AS g ON g.gender_id = acc.account_gender_id
LEFT JOIN pb_animal_pets AS p ON p.pet_owner_id = acc.account_id
LEFT JOIN pb_account_types AS acc_t ON acc_t.account_type_id = acc.account_type_id
LEFT JOIN pb_locations AS loc ON loc.location_id = acc.account_location_id
LEFT JOIN pb_country AS country ON country.country_id = loc.location_id
LEFT JOIN pb_city AS city ON city.city_id = loc.city_id
LEFT JOIN pb_account_photos AS photo ON photo.photo_id = acc.profile_picture_id
LEFT JOIN pb_account_photos AS cover ON cover.photo_id = acc.profile_cover_picture_id
WHERE acc.account_id = 1 GROUP BY acc.account_id
Output
account_id full_name username website gender birthday account_group photoCount location petCount profile_picture cover_picture
---------- ---------------- -------- -------------- ------ ---------- ------------- ---------- -------------- -------- --------------------------------------------------------------------------------------------------------------------------- -----------------------------------------------------------------------------------------------------------------------------
1 Jon Doe jondoe013 www.google.com Male 2016-01-07 Administrator 2 Serbia/Belgrade 2 http://work.example.com/uploads/user_data/1/photos/9A5EF85E-F691-F42E-C20C-BCDC765BFA1B/6c28b264470e7a7f2829ea5b7290cbba.jpg http://work.example.com/uploads/user_data/1/photos/14B6D588-68E6-6783-324B-673CBCCD4FBC/6db0935929d08f7c51ded014bd9e73df.jpg
Problem:
Query return photoCount just 2 but i have 123 pictures. I also try to Group by for photo_id but not working.
Also in this case i use subquery and also try to remove subquery for photoCount and put COUNT(photo.*) AS photoCount and result is the same

How to perform left outer join with distinct clause

I have the following data:
Users:
Id UserId Name
----------------
1 1 Him
2 10 Her
3 2 Other
Groups:
Id GroupId UserId
-------------------
1 1 1
2 2 2
3 3 10
In SQL I can do something like this to determine if a user is in any group
select *
from Users as u
left outer join (select distinct(UserId) from Groups) as g on u.UserId = g.UserId
The result should be something like this.
Id UserId Name UserId
------------------------
1 1 Him 1
2 10 Her 10
3 2 Other Null
But how can I do this in LINQ?
I think that this one is helpful for u,
var data = (from u in Users
join g in Groups.Where(a => a.UserId == (from gp in Groups.Select(r=>r.UserId).Distinct() ) )
on u.UserId equals g.UserId into outer
from x in outer.DefaultIfEmpty()
select u);
In function syntax, it would look like
var result = Users.Join(Groups,
U => U.UserId, G => G.GroupId,
(U,R) => R.Select(
G => new { Id = U.Id, UserId = U.UserId, Name = U.Name, UserId = G.UserId }
).DefaultIfEmpty(new { Id = U.Id, UserId = U.UserId, Name = U.Name, UserId = null })
);

(My)SQL JOIN - get teams with exactly specified members

Assume tables
team: id, title
team_user: id_team, id_user
I'd like to select teams with just and only specified members. In this example I want team(s) where the only users are those with id 1 and 5, noone else. I came up with this SQL, but it seems to be a little overkill for such simple task.
SELECT team.*, COUNT(`team_user`.id_user) AS cnt
FROM `team`
JOIN `team_user` user0 ON `user0`.id_team = `team`.id AND `user0`.id_user = 1
JOIN `team_user` user1 ON `user1`.id_team = `team`.id AND `user1`.id_user = 5
JOIN `team_user` ON `team_user`.id_team = `team`.id
GROUP BY `team`.id
HAVING cnt = 2
EDIT: Thank you all for your help. If you want to actually try your ideas, you can use example database structure and data found here: http://down.lipe.cz/team_members.sql
How about
SELECT *
FROM team t
JOIN team_user tu ON (tu.id_team = t.id)
GROUP BY t.id
HAVING (SUM(tu.id_user IN (1,5)) = 2) AND (SUM(tu.id_user NOT IN (1,5)) = 0)
I'm assuming a unique index on team_user(id_team, id_user).
You can use
SELECT
DISTINCT id,
COUNT(tu.id_user) as cnt
FROM
team t
JOIN team_user tu ON ( tu.id_team = t.id )
GROUP BY
t.id
HAVING
count(tu.user_id) = count( CASE WHEN tu.user_id = 1 or tu.user_id = 5 THEN 1 ELSE 0 END )
AND cnt = 2
Not sure why you'd need the cnt = 2 condition, the query would get only those teams where all of users having the ID of either 1 or 5
Try This
SELECT team.*, COUNT(`team_user`.id_user) AS cnt FROM `team`
JOIN `team_user` ON `team_user`.id_team = `team`.id
where `team_user`.id_user IN (1,5)
GROUP BY `team`.id
HAVING cnt = 2