Count query results on multi-join statements - sql

select cnt.loginid, grp.last_name as 'Group Name'
from contact cnt
right join grpmem list on cnt.contact_uuid = list.member
left join contact grp on grp.contact_uuid = list.group_id
join contact_acctyp cntacc on cnt.contact_uuid = cntacc.contact_uuid
where cntacc.c_acctyp_id in (select id from acctyp_v2 where sym like 'CDN%')
I have written a query for our system that pulls a list of all Canadian contacts and the group they are in.
Now, for people who are in multiple groups (their loginid appears multiple times) I need to determine the number of groups they are in (return a count). However, I am unsure of how to perform the count.
I'd like my output to be in the following format:
| USER ID | # of Groups |
I can't seem to figure out how to turn what I've written into that.

Assuming all you want to do is aggregate the information you are already getting back, and without looking in detail at your query, here is a guess:
select
cnt.loginid,
COUNT(*)
from contact cnt
right join grpmem list on cnt.contact_uuid = list.member
left join contact grp on grp.contact_uuid = list.group_id
join contact_acctyp cntacc on cnt.contact_uuid = cntacc.contact_uuid
where cntacc.c_acctyp_id in (select id from acctyp_v2 where sym like 'CDN%')
GROUP BY
cnt.loginid

Related

DVD rental SQL ###### QUERY

Trying to find how many active or inactive rentals there are for a specific store. 1 = active, 0 = inactive. the answer I get doubles the amount of customers and is not right. Can anyone help
SELECT COUNT(*)
FROM customer, store
WHERE active = 0
AND store_id = 2
ORDER BY customer_id;
The join is incorrect. There are no join criteria specified so the result is all possible combinations of customers and stores, filtered by where clause.
Correct way to join is:
...
from customer inner join store on store.store_id = customer.store_id
...
(assuming customer table has the store_id field).
And you probably want to group by store using the group by clause.

SQL Query for getting all elements matching another element where there are at least X elements

I want a query that will give me all the items with an ID matching another item, where the group of matching items is larger than X.
Say I have two tables, submissions and submission_items. Each submission has a submission_id and each submission_item has a foreign key that is the parent submission's submission_id. Submissions can have multiple submission_items.
I want to get all the submissions that have more than X submission_items in them.
I tried this:
select submissions.*, submission_item.*
from submission
join submission_item ON submissions.submission_id = submission_item.submission_id
group by submission.submission_id
having count(*) > 1
It errored out saying it wanted other fields in the GROUP BY function. How can I do this better?
When you specify a GROUP BY, fields in the SELECT statement must be either part of the group clause or part of an aggregate.
You need to find the IDs that you are interested in and then rejoin it back to the table.
SELECT * FROM
submission si INNER JOIN
(select submission_id
from submission
join submission_item ON submissions.submission_id = submission_item.submission_id
group by submission.submission_id
having count(*) > 1) c
ON si.submission_id = c.submission_id
or you can group all the fields individually
select submissions.submission_id, submissions.x, submissions.y
from submission
join submission_item ON submissions.submission_id = submission_item.submission_id
group by submission.submission_id, submissions.x, submissions.y
having count(*) > 1

SQL select with join are returning double results

I am trying to select some data from different tables using join.
First, here is my SQL (MS) query:
SELECT Polls.pollID,
Members.membername,
Polls.polltitle, (SELECT COUNT(*) FROM PollChoices WHERE pollID=Polls.pollID) AS 'choices',
(SELECT COUNT(*) FROM PollVotes WHERE PollVotes.pollChoiceID = PollChoices.pollChoicesID) AS 'votes'
FROM Polls
INNER JOIN Members
ON Polls.memberID = Members.memberID
INNER JOIN PollChoices
ON PollChoices.pollID = Polls.pollID;
And the tables involved in this query is here:
The query returns this result:
pollID | membername | polltitle | choices | votes
---------+------------+-----------+---------+-------
10000036 | TestName | Test Title| 2 | 0
10000036 | TestName | Test Title| 2 | 1
Any help will be greatly appreciated.
Your INNER JOIN with PollChoices is bringing in more than 1 row for a given poll as there are 2 choices for the poll 10000036 as indicated by choices column.
You can change the query to use GROUP BY and get the counts.
In case you don't have entries for each member in the PollVotes or Polls table, you need to use LEFT JOIN
SELECT Polls.pollID,
Members.membername,
Polls.polltitle,
COUNT(PollChoices.pollID) as 'choices',
COUNT(PollVotes.pollvoteId) as 'votes'
FROM Polls
INNER JOIN Members
ON Polls.memberID = Members.memberID
INNER JOIN PollChoices
ON PollChoices.pollID = Polls.pollID
INNER JOIN PollVotes
ON PollVotes.pollChoiceID = PollChoices.pollChoicesID
AND PollVotes.memberID = Members.memberID
GROUP BY Polls.pollID,
Members.membername,
Polls.polltitle
You are getting 1 row for each PollChoices record since there are multiple choices per Polls INNER JOIN Members. You may be expecting the SELECT COUNT(*) sub-queries to act as a GROUP BY clause, but they don't.
If that doesn't make sense, add a bare minimum of sample data and the expected result and we can help more.
This query result is telling you the number of votes per choice in each poll.
In your example, this voter named TestName answered the poll (with ID 10000036) and gave one choice 1 vote, and the second choice 0 votes. This is why you are getting two rows in your result.
I'm not sure if you are expecting just one row because you didn't specify what data, exactly, you are trying to select. However if you are trying to see the number of votes that TestName has submitted, for each choice where the vote was greater than 1, then you will have to modify your query like this:
select * from
(SELECT Polls.pollID,
Members.membername,
Polls.polltitle, (SELECT COUNT(*) FROM PollChoices WHERE pollID=Polls.pollID) AS 'choices',
(SELECT COUNT(*) FROM PollVotes WHERE PollVotes.pollChoiceID = PollChoices.pollChoicesID) AS 'votes'
FROM Polls
INNER JOIN Members
ON Polls.memberID = Members.memberID
INNER JOIN PollChoices
ON PollChoices.pollID = Polls.pollID) as mysubquery where votes <> 0;

Order by join column but use distinct on another

I'm building a system in which there are the following tables:
Song
Broadcast
Station
Follow
User
A user follows stations, which have songs on them through broadcasts.
I'm building a "feed" of songs for a user based on the stations they follow.
Here's the query:
SELECT DISTINCT ON ("broadcasts"."created_at", "songs"."id") songs.*
FROM "songs"
INNER JOIN "broadcasts" ON "songs"."shared_id" = "broadcasts"."song_id"
INNER JOIN "stations" ON "broadcasts"."station_id" = "stations"."id"
INNER JOIN "follows" ON "stations"."id" = "follows"."station_id"
WHERE "follows"."user_id" = 2
ORDER BY broadcasts.created_at desc
LIMIT 18
Note: shared_id is the same as id.
As you can see I'm getting duplicate results, which I don't want. I found out from a previous question that this was due to selecting distinct on broadcasts.created_at.
My question is: How do I modify this query so it will return only unique songs based on their id but still order by broadcasts.created_at?
Try this solution:
SELECT a.maxcreated, b.*
FROM
(
SELECT bb.song_id, MAX(bb.created_at) AS maxcreated
FROM follows aa
INNER JOIN broadcasts bb ON aa.station_id = bb.station_id
WHERE aa.user_id = 2
GROUP BY bb.song_id
) a
INNER JOIN songs b ON a.song_id = b.id
ORDER BY a.maxcreated DESC
LIMIT 18
The FROM subselect retrieves distinct song_ids that are broadcasted by all stations the user follows; it also gets the latest broadcast date associated with each song. We have to encase this in a subquery because we have to GROUP BY on the columns we're selecting from, and we only want the unique song_id and the maxdate regardless of the station.
We then join that result in the outer query to the songs table to get the song information associated with each unique song_id
You can use Common Table Expressions (CTE) if you want a cleaner query (nested queries make things harder to read)
I would look like this:
WITH a as (
SELECT bb.song_id, MAX(bb.created_at) AS maxcreated
FROM follows aa
INNER JOIN broadcasts bb ON aa.station_id = bb.station_id
INNER JOIN songs cc ON bb.song_id = cc.shared_id
WHERE aa.user_id = 2
GROUP BY bb.song_id
)
SELECT
a.maxcreated,
b.*
FROM a INNER JOIN
songs b ON a.song_id = b.id
ORDER BY
a.maxcreated DESC
LIMIT 18
Using a CTE offers the advantages of improved readability and ease in maintenance of complex queries. The query can be divided into separate, simple, logical building blocks. These simple blocks can then be used to build more complex, interim CTEs until the final result set is generated.
Try by adding GROUP BY Songs.id
I had a very similar query I was doing between listens, tracks and albums and it took me a long while to figure it out (hours).
If you use a GROUP_BY songs.id, you can get it to work by ordering by MAX(broadcasts.created_at) DESC.
Here's what the full SQL looks like:
SELECT songs.* FROM "songs"
INNER JOIN "broadcasts" ON "songs"."shared_id" = "broadcasts"."song_id"
INNER JOIN "stations" ON "broadcasts"."station_id" = "stations"."id"
INNER JOIN "follows" ON "stations"."id" = "follows"."station_id"
WHERE "follows"."user_id" = 2
GROUP BY songs.id
ORDER BY MAX(broadcasts.created_at) desc
LIMIT 18;

SQL query help - Join on second table, possible?

Two questions.
One: How would I be able to join two tables together, which are not the original FROM 'x' table?
Example: Grabbing all the reports and that users level name.
Report
id | user_id
1 1
User
id | level_id
1 1
Level
id | level_name
1 Admin
SELECT report.id,
report.user_id,
level.name
FROM report
INNER JOIN user
ON report.user_id=user.id
INNER JOIN level
ON user.level_id=level.id
Doesn't seem to work.
Two: How to join tables not from a 1-1 but a 1-many?
Say I wanted to:
SELECT * FROM user JOIN report ON user.id=report.user_id WHERE user.id='4'
But only join with the most recent report ordered by date desc?
I know it seems like I am asking you to do my work for me, but I just need to know what to use to accomplish this, not necessarily you coding it. Thanks.
UPDATE:
First question: Fixed, thanks
Second question:
I'll show you my query to make better sense, I didn't explain it well. I meant a many-many relationship where I grab a list of users and their latest report!
SELECT
user.id
FROM
user
<< INNER JOINS ON OTHER TABLES >>
INNER JOIN
(
SELECT
report.id
FROM
report
WHERE user.id=report.user_id
ORDER BY
date
DESC
LIMIT 1
)
For your second question:
MySQL-style:
SELECT *
FROM user
INNER JOIN report re
ON re.id = (SELECT id
FROM report
WHERE user_id=user.id
ORDER BY report.date DESC
LIMIT 1)
WHERE user.id='4'
One: Looks good to me, but shouldn't that be level.level_name in the SELECT clause?
Two: If you just want the most recent report it would be something like...
SELECT TOP 1 * FROM user JOIN report ON user.id=report.user_id WHERE user.id='4' order by report.date desc