I have two tables: invitees and invitee_information. invitees belong to events (third table event_id is a foreign key in invitees table).
I want to get the total count of invitees per event for which there is no information available yet (no record in the invitee_information table).
When I write the query using only one event_id in the where clause and anti-join pattern (IS NULL for invitee_id column to filter for rows having no information saved) the count is returned as expected for that given event_id:
SELECT COUNT(i.id)
FROM invitees i
LEFT JOIN invitee_information ii
ON ii.invitee_id = i.id
WHERE ii.invitee_id IS NULL AND i.`event_id` = 18571 AND i.`invitation_sent` = 1;
Now in order to optimize the query to get this count data at DB level for multiple event_ids instead of getting this in a loop for each event_id, I used IN and passed multiple event_ids and used GROUP_BY event_id.
Expected result should be (if there is no data saved in invitee_information table for event_id 18569 :
event_id | count(i.id)
18569 | 0
But query result is always empty. My updated query is:
SELECT i.`event_id`, COUNT(i.id)
FROM invitees i
LEFT JOIN invitee_information ii
ON i.id = ii.invitee_id
WHERE ii.invitee_id IS NULL AND i.`event_id` IN(18569,18571) AND i.`invitation_sent` = 1
GROUP BY i.`event_id`;
The only way I could imagine this happening is if you have internationalization settings such that , means a decimal point. You can easily test this using:
SELECT i.`event_id`, COUNT(i.id)
FROM invitees i LEFT JOIN
invitee_information ii
ON i.id = ii.invitee_id
WHERE ii.invitee_id IS NULL AND
(i.`event_id` = 18569 OR i.`event_id` = 18571) AND
i.`invitation_sent` = 1
GROUP BY i.`event_id`;
If this works, then at least we have identified the problem. I think a space then works:
SELECT i.`event_id`, COUNT(i.id)
FROM invitees i LEFT JOIN
invitee_information ii
ON i.id = ii.invitee_id
WHERE ii.invitee_id IS NULL AND
i.`event_id` IN (18569, 18571) AND
i.`invitation_sent` = 1
GROUP BY i.`event_id`;
I should note that you probably want COUNT(ii.invitee_id) in the SELECT rather than COUNT(i.id). The latter will never return 0.
EDIT:
Perhaps the two events are not in the invitees table. Assuming you have an events table, then something like this:
SELECT e.event_id, COUNT(i.id)
FROM events e LEFT JOIN
invitees i
ON i.event_id = e.event_id AND
i.invitation_sent = 1 LEFT JOIN
invitee_information ii
ON i.id = ii.invitee_id
WHERE ii.invitee_id IS NULL AND
e.event_id IN (18569, 18571) AND
GROUP BY e.event_id;
Related
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;
So I have two tables:
workersTbl (workerName and workerID)
workersLogTbl (workerID and workerLogTime)
e.g. workerTbl: workerName = John Smith | workerID = 5
When you pull the workerLogTbl (i.e.
SELECT * FROM workerLogTbl where workerID = 5
This should give you multiple times the worker logged in.
How do I pull all the workers names and also pull the latest log in time (supposing I join the two tables using workerID)?
Thanks.
you can try:
select l.*
from workerlogtbl l
inner join workerstbl w on l.workerid = w.workerid
where l.workerid = 5
order by l.workerlogtime desc
what i have done is join the two tables together with the common fields from both tables and then ordered by the workerlogtime in descending order. if you need more info on joins take a look here
What have you tried so far? Try adding a GROUP BY w.workerId clause and adding aggregate function in the SELECT list, MAX(t.workerLogTime). –
SELECT w.workerId
, MAX(t.workerLogTime) AS latest_time
FROM worker w
LEFT
JOIN workerLogTbl t
ON t.workerId = w.workerId
GROUP BY w.workerId
ORDER BY w.worderId
The first way is to use an inner join, which assumes every worker you are interested in has an entry in the workersLogTbl
SELECT w.workerName, max(wl.workerLogTime) as LastLogTime
FROM workersTbl w
INNER JOIN workersLogTbl tl on (w.workerId = wl.workerId)
WHERE (w.workerId = 5)
GROUP BY w.workerName
A second way uses an outer join. This is useful to capture those workers who do not have an entry in the workersLogTbl. Note that if there is no corresponding entry in workersLogTbl then LastLogTime will be null.
SELECT w.workerName, max(wl.workerLogTime) as LastLogTime
FROM workersTbl w
LEFT OUTER JOIN workersLogTbl tl on (w.workerId = wl.workerId)
WHERE (w.workerId = 5)
GROUP BY w.workerName
note that the outer word isn't generally necessary: left join is normally sufficient.
A third way is to use a subquery. This is useful if you are also wanting to get other aggregate results in the same query. Such as if we wanted the first and last log time for the worker, for example:
SELECT w.workerName,
(SELECT MAX(wl.workerLogTime) FROM workersLogTbl wl WHERE wl.workerId = w.workerId) as LastLogTime,
(SELECT MIN(wl.workerLogTime) FROM workersLogTbl wl WHERE wl.workerId = w.workerId) as FirstLogTime
FROM workersTbl w
WHERE (w.workerId = 5)
side note:
Don't name your tables with Tbl on the end. It's obvious that is a table and doesn't need the suffix and is simply noise. Also, use regular capitalization for the column names. ie: WorkerId instead of workerId
I'm trying to write an aggregate query in SQL which returns the count of all records joined to a given record in a table; If no records were joined to the given record, then the result for that record should be 0:
Data
My database looks like this (I'm not able to change the structure, unfortunately):
MESSAGE
----------------------------------------------
MESSAGEID SENDER SUBJECT
----------------------------------------------
1 Tim Rabbit of Caerbannog
2 Bridgekeeper Bridge of Death
MESSAGEPART
----------------------------------------------
MESSAGEID PARTNO CONTENT
----------------------------------------------
1 0 (BLOB)
1 1 (BLOB)
3 0 (BLOB)
(MESSAGEPART has a composite PRIMARY KEY("MESSAGEID", "PARTNO"))
Desired output
Given the data above I should get something like this:
MESSAGEID COUNT(*)
-----------------------------------------------
1 2
2 0
It seems obvious that I need to do a left join on the MESSAGE table, but how do I return a count of 0 for rows where the joined columns from MESSAGEPART are NULL? I've tried the following:
Logic
I've tried
SELECT m.MESSAGEID, COUNT(*) FROM MESSAGE m
LEFT JOIN MESSAGEPART mp ON mp.MESSAGEID = m.MESSAGEID
GROUP BY m.MESSAGEID;
However, this returns
MESSAGEID COUNT(*)
-----------------------------------------------
1 2
2 1
I've also tried
SELECT mp.MESSAGEID, COUNT(*) FROM MESSAGE m
LEFT JOIN MESSAGEPART mp ON mp.MESSAGEID = m.MESSAGEID
GROUP BY mp.MESSAGEID;
but this returns
MESSAGEID COUNT(*)
-----------------------------------------------
1 2
1
What am I doing wrong here?
How about something like this:
SELECT m.MESSAGEID, sum((case when mp.messageid is not null then 1 else 0 end)) FROM MESSAGE m
LEFT JOIN MESSAGEPART mp ON mp.MESSAGEID = m.MESSAGEID
GROUP BY m.MESSAGEID;
The COUNT() function will count every row, even if it has null. Using SUM() and CASE, you can count only non-null values.
EDIT: A simpler version taken from the top comment:
SELECT m.MESSAGEID, COUNT(mp.MESSAGEID) FROM MESSAGE m
LEFT JOIN MESSAGEPART mp ON mp.MESSAGEID = m.MESSAGEID
GROUP BY m.MESSAGEID;
You first want to count in your messaepart table before joining, i think. Try this:
SELECT m.MessageId
, COALESCE(c, 0) as myCount
FROM MESSAGE m
LEFT JOIN (SELECT MESSAGEID
, count(*) c
FROM MESSAGEPART
GROUP BY MESSAGEID) mp
ON mp.MESSAGEID = m.MESSAGEID
Don't forget to use DISTINCT in case you will LEFT JOIN more than one table:
SELECT m.MESSAGEID, COUNT(DISTINCT mp.MESSAGEID) FROM MESSAGE m
LEFT JOIN MESSAGEPART mp ON mp.MESSAGEID = m.MESSAGEID
GROUP BY m.MESSAGEID;
Return one number as a total of the matching elements between two tables, based on matching columns
In my case, I needed one total number returned for the number/count of matching items from a particular column and from two different tables.
For example, I have two separate tables that each have a PhoneNumber column. Between those two tables, I want to know how many from that column match.
Reference: https://www.guru99.com/joins.html
Using the same tables name above, it would look like this:
SELECT COUNT(DISTINCT m.MESSAGEID) AS COUNT FROM MESSAGE m, MESSAGEPART mp
where mp.MESSAGEID = m.MESSAGEID;
Firstly, my SQL knowledge is little rusty. I am trying to generate a report of reviews each patient has gone through for a time period. A review is done as part of a Doctors' round. The following are the corresponding tables with relevant columns:
Patients: (id, name)
Rounds: (id, patient_id, date)
Reviews: (id, round_id, review)
The report should look like the following:
Patient | Reviews
_________________________
Patient 1 | 2
_________________________
Patient 2 | 1
_________________________
Patient 3 | 0
_________________________
I tried the following SQL statement:
SELECT
p.name as patient,
COUNT(r.round_id) as reviews
FROM
patients as p
JOIN rounds as ro ON p.id = ro.patient_id
JOIN reviews as r ON ro.id = r.round_id
WHERE
r.review_date between '2012-02-01' AND '2012-02-29'
GROUP BY
p.name
But, the above query only returns rows where reviews count is > 1. I want it to return even if the count is 0.
The simplest way to Join the tables, and to include instances where there is no match in one of those tables, is to use a LEFT OUTER JOIN. This will match all records to the left, regardless of whether a match was found on the right side of the JOIN.
Since your r.review_date is in your WHERE clause, no matches can occur unless there is a review between those dates. So to include instances where there is no review, you must allow for that in your WHERE clause by adding "OR r.review_date IS NULL" as below. You may also want to consider filtering on the round.date field instead, so that you are only looking at instances where there were valid rounds performed within that time frame. ie. "WHERE ro.date between '2012-02-01' AND '2012-02-29'"
eg.
SELECT
p.name as patient,
COUNT(r.round_id) as reviews
FROM
patients as p
JOIN rounds as ro ON p.id = ro.patient_id
LEFT OUTER JOIN reviews as r ON ro.id = r.round_id
WHERE
r.review_date between '2012-02-01' AND '2012-02-29' OR r.review_date IS NULL
GROUP BY
p.name
Note: If you want to report records without any rounds, you will also have to make the first JOIN a LEFT OUTER JOIN as well.
FROM
patients as p
LEFT OUTER JOIN rounds as ro ON p.id = ro.patient_id
LEFT OUTER JOIN reviews as r ON ro.id = r.round_id
SELECT p.name AS patient
, COUNT(r.ID) AS reviews
FROM patients AS p
LEFT JOIN rounds AS ro ON p.id = ro.patient_id
LEFT JOIN reviews AS r ON ro.id = r.round_id
AND r.review_date BETWEEN '2012-02-01'
AND '2012-02-29'
GROUP BY p.name
Will get you a list of all patients, including those who have not had a round or a review in between your specific dates. Patients with no rounds or reviews will have a 0.
I have two tables like this
Member and their Purchases
I need the output like this
Member_ID | CountofProducts
(and the Product Value not should be 0 and Purchase Status = 1)
SELECT
MemberName,
(SELECT COUNT(*) AS Count
FROM dbo.Purchases
WHERE MemberName = dbo.Members.MemberName
AND Res_Status = 1) AS Count
FROM
dbo.Members
I can get their total CountofPurchased Products from the above query but I need to avoid their count=0 how to do it ?
You can try something like
SELECT m.MemberName,
COUNT(p.*) Cnt
FROM Members m INNER JOIN
Purchases p ON m.MemberName = p.MemberName
WHERE p.Res_Status = 1
GROUP BY m.MemberName
There is no need for the HAVING clause, as the INNER JOIN will exclude all entries in Members that do not have Purchases.
SELECT m.MemberName, COUNT(p.*) AS CountOfProducts
FROM Members m
INNER JOIN Purchases p ON m.MemberName = p.MemberName
WHERE p.Res_Status = 1
GROUP BY m.MemberName
HAVING COUNT(p.*) > 0
I think the above will somewhat do what you want. The key is that you probably do not even need your subquery, but rather you can handle the query (possibly with greater efficiency) just using a join.