I have a problem with a query which dosen't output what I expects and I cant get it to work.
The query is
SELECT COUNT(event_type_id), userprofiles.id as userprofile_id, users.first_name, users.last_name
FROM users, userprofiles
LEFT JOIN event_matches ON event_matches.userprofile_id = userprofiles.id
AND event_type_id = 1
LEFT JOIN activities ON activities.id = event_matches.activity_id
AND YEAR(activities.start) = 2012
WHERE userprofiles.home_id = 2
AND users.id = userprofiles.user_id
GROUP BY userprofiles.id
ORDER BY COUNT(event_type_id) DESC
But when I run this query it gets all the activities and not only the ones from 2012, it seems like the LEFT JOIN does something. If i dont use LEFT JOIN and instead use JOIN, the query works, but then I only get the user who has a event_type_id = 1 in 2012. But the reason why I used the LEFT JOIN is because I want to have alle the users, and not only the ones who has a event_type_id.
Hopes someone can see my mistake and help me.
So what -- in English are you trying to get... Ex: For all user profiles where home_id = 2, how many events of type 1 were within 2012. By doing left joins, you are saying I want all profiles of Home_ID = 2 regardless of attending an event of type 1. You would want to change them from LEFT JOINs to just JOIN (INNER JOIN) if that is the case
This is for all user profiles regardless of an actual event that was of type 1 within 2012, this only cares if it finds an event of type 1
SELECT
COUNT(event_type_id),
userprofiles.id as userprofile_id,
users.first_name,
users.last_name
FROM
userprofiles
JOIN users
ON userprofiles.user_id = users.id
LEFT JOIN event_matches
ON userprofiles.id = event_matches.userprofile_id
AND event_matches.event_type_id = 1
LEFT JOIN activities
ON event_matches.activity_id = activities.id
AND YEAR(activities.start) = 2012
WHERE
userprofiles.home_id = 2
GROUP BY
userprofiles.id
ORDER BY
COUNT(event_type_id) DESC
by changing to the granular activity table, you would get your count of event types = 1 AND the year was of 2012
SELECT
COUNT(activities.id),
... rest of query...
ORDER BY
COUNT(activities.id) DESC
If you only want those profiles that actually HAD events of type 1 in the year 2012, change to JOIN
SELECT
COUNT(event_type_id),
userprofiles.id as userprofile_id,
users.first_name,
users.last_name
FROM
userprofiles
JOIN users
ON userprofiles.user_id = users.id
JOIN event_matches
ON userprofiles.id = event_matches.userprofile_id
AND event_matches.event_type_id = 1
JOIN activities
ON event_matches.activity_id = activities.id
AND YEAR(activities.start) = 2012
WHERE
userprofiles.home_id = 2
GROUP BY
userprofiles.id
ORDER BY
COUNT(event_type_id) DESC
Related
(I know this could have been solved by redesigning the database, but I have already spent a lot of time researching my problem and I am hoping to learn a solution which can also be useful for later..)
I have a registration system. Users who are participants of a certain activity will be placed in the registration-table, which will be linked to the activity table, which will be linked to the weeks table, and I need to anonymize (UPDATE) the name in the users-table when the last activity the user participated in is more than four weeks old.
My table is this:
USERS
id, name
REGISTRATION
uid, aid (uid = USERS.id, aid = ACTIVITY.id)
ACTIVITY
wid (wid = WEEKS.id)
WEEKS
year, weeknumber
My quest is to find all USERS.id who has'nt been registered for an activity which for 4 weeks, and I which to anonymize (UPDATE) the USERS.name field in that case. So I need to select all users and through the registration-table find the activity with the latest WEEKS.year + WEEKS.weeknumber and then determine if that entry is more than 4 weeks old.
I believe I have been able to solve this as a SELECT, with the following statement:
SELECT u.id, r.act_id, a.wid, w.weeknumber, w.year
FROM users AS u
INNER JOIN registration AS r ON r.uid = u.id
INNER JOIN activity AS a ON a.id = r.aid
INNER JOIN weeks AS w ON w.id = (
SELECT id
FROM weeks AS w2
WHERE w2.id = a.wid
ORDER BY year DESC, weeknumber DESC
LIMIT 1
)
WHERE w.year=YEAR(NOW())
AND w.weeknumber=WEEK(NOW())-5
GROUP BY u.id
ORDER BY u.id DESC
However I am having problem converting this to a functional UPDATE-statement. What I have so far doesn't seem to get the latest entry from the weeks-table:
UPDATE users u
JOIN registration AS r ON r.uid = u.id
JOIN activity AS a ON a.id = r.aid
JOIN weeks AS w ON w.id = (
SELECT id
FROM weeks AS w2
WHERE w2.id = a.wid
ORDER BY year DESC, weeknumber DESC
LIMIT 1
)
SET u.name = 'Anonymized'
WHERE w.year=YEAR(NOW())
AND w.weeknumber=WEEK(NOW())-5
I would be very grateful for any pointers in the right direction as my head is spinning at this point.
I've already read every post with the similarly title but didn't find right answer.
What I really need to do is to count some data from MySQL table and then do group_concat because I got more than one row.
My table looks like this
and here is how I tried to run the query
SELECT
count(cal.day) * 8,
w.name
FROM claim as c
RIGHT JOIN calendar as cal ON c.id = cal.claim_id
RIGHT JOIN worker as w ON c.worker_id = w.id
GROUP BY c.id
ORDER BY w.name asc
But I get for some workers more than one row and I can't group_concat them because of count(). I need this for mysql procedure I've making so please help me if you can.
I hope I've gived you enough informations
Edit for Dylan:
See the difference in output
GROUP BY w.id
GROUP BY c.id
MySQL does'not allow two aggregate functions used together, like GROUP_CONCAT(COUNT(...)).
Therefore, we can use a sub-query to work around as below.
SELECT
GROUP_CONCAT(t.cnt_cal_day) as cnt_days,
t.name
FROM
(
SELECT
count(cal.day) * 8 as cnt_cal_day,
w.name
FROM claim as c
RIGHT JOIN calendar as cal ON c.id = cal.claim_id
RIGHT JOIN worker as w ON c.worker_id = w.id
GROUP BY c.id
ORDER BY w.name asc
) t
While the question is still not clear for me, I try to guess what you need.
This query:
SELECT
w.name,
COUNT(cal.day) * 8 AS nb_hours
FROM worker w
LEFT JOIN claim c ON w.id = c.worker_id
INNER JOIN calendar cal ON c.id = cal.claim_id
GROUP BY w.id
ORDER BY w.name ASC
returns the names of all workers and, for each one, the number of hours of vacation approved for them.
If you use LEFT JOIN calendar instead you will get the number of hours of vacation claimed by each worker (approved and not approved). In order to separate them you should make the query like this:
SELECT
w.name,
c.approved, # <---- I assumed the name of this field
COUNT(cal.day) * 8 AS nb_hours
FROM worker w
LEFT JOIN claim c ON w.id = c.worker_id
LEFT JOIN calendar cal ON c.id = cal.claim_id
GROUP BY w.id, c.approved
ORDER BY w.name ASC
This query should return 1 or 2 rows for each worker, depending on the types of vacation claims they have (none, approved only, not approved only, both). For workers that don't have any vacation claim, the query returns NULL in column approved and 0 in column nb_hours.
NOTE: the p_date is supposed to be in dd-mm-yy format but excel decided to be a jerk as displayed it as dd-mm.
Anyways, for my query, I wanted to retrieve the username and a counter for the number of times the user has published a message in a particular month.
This is the code I used:
SELECT users.username, count(*)
FROM publishdate JOIN users
ON publishdate.uid = users.uid
WHERE MONTH(STR_TO_DATE(p_date, '%Y-%m-%d')) = 11
GROUP BY users.username;
When I filter for November, I see that ruby has published twice in that month, but how do I include all users who have published 0 messages into the result?
Thank you all in advance.
You have to use users table on the left side of your query and do LEFT JOIN to publishdate table:
SELECT users.username, count(publishdate.uid)
FROM users
LEFT JOIN publishdate ON publishdate.uid = users.uid AND
MONTH(STR_TO_DATE(p_date, '%Y-%m-%d')) = 11
GROUP BY users.username;
Note: Predicate MONTH(STR_TO_DATE(p_date, '%Y-%m-%d')) = 11 has to appear in the ON clause of the query, otherwise LEFT JOIN will be equivalent to an INNER JOIN operation.
I'm using this query to get all the accounts with at least 3 projects.
SELECT
accounts.id,
accounts.name,
COUNT(accounts_project_1project_idb) as count
FROM accounts_project_1_c
LEFT JOIN accounts ON accounts_project_1accounts_ida = accounts.id
LEFT JOIN project ON accounts_project_1project_idb = project.id
LEFT JOIN project_cstm ON id_c = project.id
GROUP BY accounts_project_1accounts_ida
HAVING COUNT(accounts_project_1project_idb) >= 3
ORDER BY count DESC
Where accounts is the account table, project the project one, and project_cstm a table containing more information about projects
This request returns me 153 results.
But now, I would like to get the contact linked to an account. If it exists several contacts, I don't really care, I just want one.
SELECT
accounts.id,
accounts.name,
contacts.first_name,
contacts.last_name,
contacts.phone_mobile,
contacts.phone_work,
COUNT(accounts_project_1project_idb) as count
FROM accounts_project_1_c
LEFT JOIN accounts ON accounts_project_1accounts_ida = accounts.id
LEFT JOIN project ON accounts_project_1project_idb = project.id
LEFT JOIN project_cstm ON id_c = project.id
LEFT JOIN accounts_contacts ON accounts.id = accounts_contacts.account_id
LEFT JOIN contacts ON accounts_contacts.contact_id = contacts.id
GROUP BY accounts_project_1accounts_ida
HAVING COUNT(accounts_project_1project_idb) >= 3
ORDER BY count DESC
This request returns 173 results.
I don't really get it, using left join it should not add any row right?
Can you tell me what I'm doing wrong?
Thanks a lot.
I think you want the HAVING to be:
HAVING COUNT(DISTINCT accounts_project_1project_idb) >= 3
You probably want that in the SELECT as well.
Hi I have a query that is giving me a few problems and it was suggested I ask a separate question about the end result rather than the problem.
So I have three tables and some user input.
the tables are:
users,
usersLanguages and
usersSkills
each table has a related ID the users table has ID and on the other two they have userID to match skills and languages to users
the user input is dynamic but for example it can be 1 for usersLanguages and 2 for usersSkills
The user input is taken from a form and what i need to do is match get the results of users
depending on the language IDs or skill ids passed through. for example i can pass two user ids and three language ID's.
SELECT DISTINCT users.ID, users.name
FROM users
INNER JOIN usersSkills
ON users.ID = usersSkills.userID
INNER JOIN usersLanguages ON users.ID = usersLanguages.userID
WHERE activated = "1"
AND type = "GRADUATE" AND usersSkills.skillID IN(2)
AND usersLanguages.languageID IN(2)
GROUP BY usersSkills.userID HAVING COUNT(*) = 1,
usersLanguages.userID HAVING COUNT(*) = 1
You do not mix group by and having clauses.
having is not a part of group by, in fact you can have having without a group by, in which case it will work as a more capable (and slower) where clause.
SELECT u.ID, u.name
FROM users u
INNER JOIN usersSkills us ON u.ID = us.userID
INNER JOIN al ON u.ID = ul.userID
WHERE u.activated = '1'
AND u.type LIKE 'GRADUATE' AND us.skillID IN('2')
AND ul.languageID IN('2')
GROUP BY u.ID
HAVING COUNT(*) = 1
because of the join criteria, ul.userID = us.userID = u.ID so it makes no sense to group by both. Just group by u.id, because that's the ID you select after all.
u.name is functionally dependent on ID, so that does not need to be listed (*).
When doing a test like u.type = 'GRADUATE', I prefer to do u.type LIKE 'GRADUATE' because LIKE is case insensitive and = may not be depending on the collation, but that's just me.
Distinct is not needed; group by already makes the results distinct.
Having works on the resultset up to and including group by, so in this case you need only one having clause. If you want to have more, you need to treat them the same as a where clause:
having count(*) = 1 AND SUM(money) > 10000
(*) this is only true in MySQL, in other SQL's you need to list all non-aggregated selected columns in your group by clause. Whether they are functionally dependent on the group by column or not. This slows things down and makes for very long query-syntax, but it does prevent a few beginners errors.
Personally I like the MySQL way of doing things.
something like
SELECT * from users left join usersLanguage on users.id=usersLanguage.userID
left join usersSkills on usersSkills.userID=users.id
where usersLanguage.id in (1, 2, 3) and usersSkills.id in (1, 2, 3)
GROUP BY users.id
will probably work
try this
SELECT users.ID,user.name
FROM users
INNER JOIN usersSkills ON users.ID = usersSkills.userId
INNER JOIN AND usersLanguages ON users.ID = usersLanguages.userID
WHERE activate = '1'
AND type = 'GRADUATE'
AND usersSkill.skillID IN (2)
AND usersLanguages.languageID IN (2)
GROUP BY users.ID