How to COUNT with condition while distinct join MySQL? - mysql

I have two tables:
mysql> DESCRIBE swaps;
+-------------+----------+------+-----+---------+----------------+
| Field | Type | Null | Key | Default | Extra |
+-------------+----------+------+-----+---------+----------------+
| id | int(11) | NO | PRI | NULL | auto_increment |
| user1_id | int(11) | NO | | NULL | |
| user2_id | int(11) | NO | | NULL | |
| hasto | int(11) | NO | | NULL | |
| requested | datetime | NO | | NULL | |
| accepted | datetime | YES | | NULL | |
| swapped1 | datetime | YES | | NULL | |
| swapped2 | datetime | YES | | NULL | |
| rejected | datetime | YES | | NULL | |
| rejected_by | int(11) | YES | | NULL | |
+-------------+----------+------+-----+---------+----------------+
mysql> DESCRIBE messages;
+-----------+----------+------+-----+-------------------+----------------+
| Field | Type | Null | Key | Default | Extra |
+-----------+----------+------+-----+-------------------+----------------+
| msg_id | int(11) | NO | PRI | NULL | auto_increment |
| sender_id | int(11) | NO | | NULL | |
| msg | text | NO | | NULL | |
| msg_time | datetime | NO | | CURRENT_TIMESTAMP | |
| swap_id | int(11) | NO | | NULL | |
| seen | datetime | YES | | NULL | |
+-----------+----------+------+-----+-------------------+----------------+
and query that I adjusted from this question
SELECT s.*, m.*
FROM swaps as s
JOIN messages as m
ON (s.id= m.swap_id AND m.msg_time=
(SELECT MAX(msg_time) FROM messages WHERE messages.swap_id = s.id));
as a result I single row for every swap and information about last sent message within this swap. I want to add the count of messages that have not jet been seen (m.seen IS NULL).
I tried different approaches but always get error. What I want is to add count of messages in corresponding swap with seen IS NULL to my result set. Would appreciate any suggestions.

You can add the count as a subquery:
SELECT s.*, m.*,
(SELECT COUNT(*) FROM messages m2 WHERE m2.seen IS NULL) as seen_is_null
FROM swaps s JOIN
messages m
ON s.id= m.swap_id AND
m.msg_time =(SELECT MAX(m2.msg_time) FROM messages m2 WHERE m2.swap_id = s.id));
It seems curious to be counting over all messages, but that is what the question asks for. You can, of course, introduce a correlation clause to count for a given swap or something else.

Related

Check that all rows in table A have a specific value in table B, including a GROUP BY

I have two tables - students and evidence - and I'm trying to check for a corresponding value in one column in evidence, grouped by another column in evidence for every entry in students
These are what the tables look like:
> desc students;
+------------+---------------------+------+-----+---------+----------------+
| Field | Type | Null | Key | Default | Extra |
+------------+---------------------+------+-----+---------+----------------+
| id | bigint(20) unsigned | NO | PRI | NULL | auto_increment |
| firstname | varchar(191) | NO | | NULL | |
| surname | varchar(191) | NO | | NULL | |
| class_id | int(11) | NO | | NULL | |
| dob | datetime | NO | | NULL | |
| enrollment | datetime | NO | | NULL | |
| created_at | timestamp | YES | | NULL | |
| updated_at | timestamp | YES | | NULL | |
+------------+---------------------+------+-----+---------+----------------+
> desc evidence;
+---------------+------------------------------------------------+------+-----+---------+----------------+
| Field | Type | Null | Key | Default | Extra |
+---------------+------------------------------------------------+------+-----+---------+----------------+
| id | bigint(20) unsigned | NO | PRI | NULL | auto_increment |
| type | enum('written','image','audio','video','link') | YES | | NULL | |
| mime | varchar(191) | YES | | NULL | |
| path | varchar(191) | YES | | NULL | |
| user_id | int(11) | NO | | NULL | |
| date_recorded | datetime | YES | | NULL | |
| statement_id | int(11) | NO | | NULL | |
| progress | int(11) | NO | | NULL | |
| notes | mediumtext | YES | | NULL | |
| student | int(11) | NO | MUL | NULL | |
| created_at | timestamp | YES | | NULL | |
| updated_at | timestamp | YES | | NULL | |
+---------------+------------------------------------------------+------+-----+---------+----------------+
Entries in the evidence table are associated with a student (evidence.student_id) and an evidence statement (evidence.statement_id) and then given a progress value of 1 (in progress) or 2 (complete).
I want to be able to check that for each statement_id every student has at least one row with the progress entry set to 2. Ideally I'd like to GROUP BY statement_id and only return a value of 2 for progress if every student has at least one row in the evidence table for that statement_id where progress has been set to 2.
The goal is to list all of the statement_ids where everyone in a class has completed the task (and therefore had some evidence added with progress set to complete).
I've tried doing joins similar to this
SELECT * FROM students left join evidence ON students.id = evidence.student GROUP BY evidence.statement_id HAVING progress = 2;
but the problem there is that if one student is marked as completing a statement_id but another student doesn't have any entries for that statement_id then progress will return 2. I'd rather it just returned NULL for the student without any entries.
I'm pretty stumped on this one to any help is greatly appreciated.

MySQL Unknown column in where clause?

I have two databases.
One is called INFO with three tables (Stories, Comments, Replies)
Stories has the following fields
+--------------+----------------+------+-----+---------------------+-----------------------------+
| Field | Type | Null | Key | Default | Extra |
+--------------+----------------+------+-----+---------------------+-----------------------------+
| storyID | int(11) | NO | PRI | NULL | |
| originalURL | varchar(500) | YES | | NULL | |
| originalDate | timestamp | NO | | CURRENT_TIMESTAMP | on update CURRENT_TIMESTAMP |
| numDiggs | int(11) | YES | | NULL | |
| numComments | int(11) | YES | | NULL | |
| diggURL | varchar(500) | YES | | NULL | |
| rating | varchar(50) | YES | | NULL | |
| title | varchar(200) | YES | | NULL | |
| summary | varchar(10000) | YES | | NULL | |
| uploaderID | varchar(50) | YES | | NULL | |
| imageURL | varchar(500) | YES | | NULL | |
| category1 | varchar(50) | YES | | NULL | |
| category2 | varchar(50) | YES | | NULL | |
| uploadDate | timestamp | NO | | 0000-00-00 00:00:00 | |
| num | int(11) | YES | | NULL | |
+--------------+----------------+------+-----+---------------------+-----------------------------+
Another database is called Data with one table (User). Fields shown below:
+-------------------+-------------+------+-----+---------+-------+
| Field | Type | Null | Key | Default | Extra |
+-------------------+-------------+------+-----+---------+-------+
| userID | varchar(50) | NO | PRI | NULL | |
| numStories | int(11) | YES | | NULL | |
| numComments | int(11) | YES | | NULL | |
| numReplies | int(11) | YES | | NULL | |
| numStoryDiggs | int(11) | YES | | NULL | |
| numCommentReplies | int(11) | YES | | NULL | |
| numReplyDiggs | int(11) | YES | | NULL | |
| numStoryComments | int(11) | YES | | NULL | |
| numStoryReplies | int(11) | YES | | NULL | |
+-------------------+-------------+------+-----+---------+-------+
User.userID is full of thousands of unique names. All other fields are currently NULL. The names in User.userID correspond to the names in Stories.uploaderID.
I need to, for each userID in User, count the number of stories uploaded from (i.e. num) Stories for the corresponding name and insert this value into User.numStories.
The query which I have come up with (which produces an error) is:
INSERT INTO DATA.User(numStories)
SELECT count(num)
FROM INFO.Stories
WHERE INFO.Stories.uploaderID=DATA.User.userID;
The error I get when running this query is
Unknown column 'DATA.User.userID' in 'where clause'
Sorry if this is badly explained. I will try and re-explain if need be.
You aren't creating new entries in the User table, you're updating existing ones. Hence, insert isn't the right syntax here, but rather update:
UPDATE DATA.User u
JOIN (SELECT uploaderID, SUM(num) AS sumNum
FROM INFO.Stories
GROUP BY uploadedID) i ON i.uploaderID = u.userID
SET numStories = sumNum
EDIT:
Some clarification, as requested in the comments.
The inner query sums the num in Stories per uploaderId. The updates statement updates the numStories in User the the calculated sum of the inner query of the matching id.

How do I correctly wrap sum(quantity) = max(sum(quantities)) in mySQL?

I'm wanting to take the sum of each unique attribute, then take the maximum of those values. Any advice on how to do this using mySQL? I keep getting an "invalid use of group function".
+-------------+-------------+------+-----+---------+-------+
| Field | Type | Null | Key | Default | Extra |
+-------------+-------------+------+-----+---------+-------+
| matchName | varchar(26) | YES | MUL | NULL | |
| winner | varchar(26) | YES | | NULL | |
| loser | varchar(26) | YES | | NULL | |
| winningGame | varchar(26) | YES | | NULL | |
| dateofWin | date | YES | | NULL | |
+-------------+-------------+------+-----+---------+-------+
and
+------------+-------------+------+-----+---------+-------+
| Field | Type | Null | Key | Default | Extra |
+------------+-------------+------+-----+---------+-------+
| matchName | varchar(26) | NO | PRI | | |
| date | date | YES | | NULL | |
| grandPrize | int(11) | YES | | NULL | |
| location | varchar(26) | YES | | NULL | |
| state | varchar(26) | YES | | NULL | |
| attendance | int(11) | YES | | NULL | |
+------------+-------------+------+-----+---------+-------+
The closest I've gotten so far was by using:
select Results.winner, Tourney.grandPrize
from Tourney
inner join Results
on Results.matchName=Tourney.matchName;
Your question is very unclear,
I've upvoted #A.D. comment, you should provide more details, table structure, data sample, expected result, current query that brings error etc..
But quick answer for your question is:
SELECT t.*, MAX(t.sum_qty) as max_qty
FROM (
SELECT *, SUM(qty) as sum_qty
FROM test
GROUP BY order_id // you can set any group field you need here
) AS t

MySQL - how can I do a join and order by the sum of votes?

I have schema like this (just experimenting, so if you have improvement suggestions I am all ears):
mysql> describe contest_entries;
+---------------+----------------+------+-----+---------+----------------+
| Field | Type | Null | Key | Default | Extra |
+---------------+----------------+------+-----+---------+----------------+
| entry_id | int(10) | NO | PRI | NULL | auto_increment |
| member_id | int(10) | YES | | NULL | |
| person_name | varchar(10000) | NO | | NULL | |
| date | date | NO | | NULL | |
| platform | varchar(30) | YES | | NULL | |
| business_name | varchar(100) | YES | | NULL | |
| url | varchar(200) | YES | | NULL | |
| business_desc | varchar(3000) | YES | | NULL | |
| guid | varchar(50) | YES | UNI | NULL | |
+---------------+----------------+------+-----+---------+----------------+
9 rows in set (0.00 sec)
mysql> describe contest_votes;
+------------------+---------+------+-----+---------+----------------+
| Field | Type | Null | Key | Default | Extra |
+------------------+---------+------+-----+---------+----------------+
| vote_id | int(10) | NO | PRI | NULL | auto_increment |
| user_id | int(10) | NO | | NULL | |
| contest_entry_id | int(10) | NO | MUL | NULL | |
| vote | int(7) | NO | | NULL | |
+------------------+---------+------+-----+---------+----------------+
And I am trying to pull the data as a leaderboard, ordering the results by the most votes. How would I do that? I am able to do the left-join part, but the sum and the ordering part of the query is confusing me.
Thank you!
SELECT entry_id
FROM contest_entries
LEFT OUTER JOIN contest_votes ON entry_id = contest_entry_id
GROUP BY entry_id
ORDER BY SUM(vote) DESC
select e.entry_id, sum(v.vote) as votes
from contest_entries e
left join contest_votes v on e.entry_id = v.contest_entry_id
group by e.member_id
order by votes desc

How to fix this sql with LEFT JOIN?

I have the follow two mysql tables:
mysql> desc subscriptions;
+------------+--------------+------+-----+---------+----------------+
| Field | Type | Null | Key | Default | Extra |
+------------+--------------+------+-----+---------+----------------+
| id | int(6) | NO | PRI | NULL | auto_increment |
| id_user | int(6) | NO | MUL | NULL | |
| id_package | int(4) | NO | | NULL | |
| expiry | date | YES | | NULL | |
| ip | varchar(100) | NO | | NULL | |
| amount | double | NO | | NULL | |
| processed | tinyint(4) | NO | | NULL | |
+------------+--------------+------+-----+---------+----------------+
7 rows in set (0.00 sec)
mysql> desc packages;
+----------+--------------+------+-----+---------+----------------+
| Field | Type | Null | Key | Default | Extra |
+----------+--------------+------+-----+---------+----------------+
| id | int(6) | NO | PRI | NULL | auto_increment |
| id_panel | tinyint(4) | NO | | NULL | |
| name | varchar(100) | NO | | NULL | |
| price | double | NO | | NULL | |
| period | varchar(50) | NO | | NULL | |
| days | int(11) | NO | | NULL | |
| visible | tinyint(4) | NO | | NULL | |
| payment | tinyint(4) | NO | | 1 | |
+----------+--------------+------+-----+---------+----------------+
8 rows in set (0.00 sec)
I'm using the follow query to retrive all the packages and the relative USER expirations date (of the specific package).
SELECT packages.id, packages.name, packages.price, packages.period, packages.days, MAX(subscriptions.expiry) as expiry
FROM packages LEFT JOIN subscriptions
ON subscriptions.id_package = packages.id AND subscriptions.processed = 1 AND subscriptions.id_user = 1
WHERE packages.visible = 1
GROUP BY subscriptions.id_user
ORDER BY packages.id_panel
My problem is that if there is not the subscription of a specific package the query does not show the package.
I MUST show all the page and....IF the user has a subscription check the MAX expiry date (becase i track all the subscrions the user made).
How can i show all the packages and the relative user expiration dates? (and if there is no subscription of a package show NULL)
You're grouping by subscriptions.id_user, yet you select all your non_aggregates from the packages table. Are you sure that you're grouping by the correct field?
It looks like it should be packages.id...
Easy... just move the packages.visible = 1 condition into the ON clause!
SELECT packages.id, packages.name, packages.price, packages.period, packages.days, MAX(subscriptions.expiry) as expiry
FROM packages
LEFT JOIN subscriptions ON subscriptions.id_package = packages.id
AND subscriptions.processed = 1
AND subscriptions.id_user = 1
AND packages.visible = 1
GROUP BY 1,2,3,4,5
ORDER BY packages.id_panel
This approach is the only way to get conditions into a LEFT JOIN, and a great trick to remember.
Note: Your group by list was incorrect too