Problem with simple SQL join - mysql

This relates to a webpage that should show all upcoming events, and mark any that are in the current user's diary.
diary table
diary_id
member_id
event_id
event table
event_id
region_id
...
region table
region_id
...
member table
member_id
...
QUERY:
SELECT event.*, region.name, diary.diary_id
FROM event, region
LEFT JOIN diary on diary.member_id = 10 AND diary.event_id = event.event_id
WHERE region.region_id = event.region_id AND `date` >= NOW()
This is returning unknown column event.event_id and I can't figure out why. I'm no SQL whiz but expected this would just work and give me a NULL in the diary_id column for all events that are not in the user's diary

You are mixing join syntax. Try this instead.
SELECT event.*,
region.name,
diary.diary_id
FROM event
INNER JOIN region
ON region.region_id = event.region_id
LEFT JOIN diary
ON diary.member_id = 10
AND diary.event_id = event.event_id
WHERE `date` >= NOW()
Update
Your problem with not finding event_id is because of this FROM event, region. It can't find event_id in the on clause. Change your query as suggested above but it would also be possible to fix it by switching places of the tables to FROM region, event. Don't do that. Use the new join syntax introduced to the SQL language some 20 years ago.

Don't put diary.member_id = 10 in the where clause if you want to do the left join. In PL/SQL this will turn your left join into a join without asking you
The below should do better:
SELECT event.*, region.name, diary.diary_id
FROM event
JOIN region on region.region_id = event.region_id
LEFT JOIN ( select diary_id, event_id
from diary
where diary.member_id = 10 ) diary
ON diary.event_id = event.event_id
WHERE `date` >= NOW()

First of all I wouldn't put diary.member_id = 10 in the Join:
SELECT event.*, region.name, diary.diary_id
FROM `event`, region
LEFT JOIN diary ON diary.event_id = event.event_id
WHERE region.region_id = event.region_id AND `date` >= NOW() AND diary.member_id = 10
Are you sure that event.event_id is not event.id?

Related

Mysql Select query with double JOIN statement

Hy guys. I need to select the customer_id who is on 'base' table, where the created date is the current date and where this customer dont have a budget_item executed on the current date. My query brings up an incorrect result. It show the customer_id of all budget_id that are not on that date..
What is wrong in my stetement?
PS.: I cannot consolidate the tables.
SELECT base.customer_id
FROM base
LEFT JOIN budget ON base.customer_id = budget.customer_id
LEFT JOIN budget_item ON budget.budget_id = budget_item.budget_id
WHERE
CAST(base.created as Date) = CURDATE()
AND budget_item.execution_date <> CURDATE();
Left join budget/budget_item specifically looking for items with the current date and then exclude them in WHERE by checking that some non-nullable column of budget_item is NULL (indicating no record was joined):
SELECT base.customer_id
FROM base
LEFT JOIN budget ON base.customer_id = budget.customer_id
LEFT JOIN budget_item ON budget.budget_id = budget_item.budget_id AND budget_item.execution_date = CURDATE()
WHERE
CAST(base.created as Date) = CURDATE()
AND budget_item.budget_id IS NULL;
Some prefer using NOT EXISTS for this, but the result and efficiency should be the same:
SELECT base.customer_id
FROM base
WHERE
CAST(base.created as Date) = CURDATE()
AND NOT EXISTS (
SELECT 1
FROM budget
JOIN budget_item ON budget.budget_id = budget_item.budget_id AND budget_item.execution_date = CURDATE()
WHERE budget.customer_id = base.customer_id
);
fiddle
Use aggregation:
SELECT base.customer_id
FROM base LEFT JOIN
budget bu
ON base.customer_id = bu.customer_id LEFT JOIN
budget_item bi
ON bu.budget_id = bi.budget_id
WHERE CAST(base.created as Date) = CURDATE()
GROUP BY base.customer_id
HAVING SUM(bi.execution_date = CURDATE()) = 0;

MySQL GROUP BY...HAVING different values same field

I want to use a query similar to the following to retrieve all rows in events that have at least one corresponding event_attendances row for 'male' and 'female'. The below query returns no rows (where there certainly are some events that have event_attendances from both genders).
Is there a way to do this without a subquery (due to the way the SQL is being generated in my application, a subquery would be considerably more difficult for me to implement)?
SELECT * FROM events e
LEFT JOIN event_attendances ea ON (e.id = ea.event_id)
GROUP BY e.id
HAVING ea.gender = 'female' AND ea.gender = 'male'
Use
HAVING sum(ea.gender = 'female') > 0
AND sum(ea.gender = 'male') > 0
or
HAVING count(distinct ea.gender) = 2
BTW you should use a subquery to get all data when you group.
SELECT *
FROM events
where id in
(
SELECT events.id
FROM events
LEFT JOIN event_attendances ON (events.id = event_attendances.event_id)
GROUP BY events.id
HAVING count(distinct event_attendances.gender) = 2
)
HAVING generally used with aggregate functions.
You should do self-jointo get the desired results, since ea.gender = 'female' AND ea.gender = 'male' is contradictory,which always returns empty set.
You can try this
SELECT T1.*
FROM events T1
INNER JOIN
(SELECT events.id
FROM events
LEFT JOIN event_attendances ON (events.id = event_attendances.event_id)
GROUP BY events.id
HAVING COUNT(DISTINCT event_attendances.gender) = 2) T2 ON T1.events.id=T1.events.id
Hope this helps.

Mysql selecting date range from unixtime timestamp gives too many records

I have a mysql query.
The idea is to select the records between a date range. The dates are stored as unix timestamps. With the query below, I end up with far more records than I should (out of the date range).
I have picked my brain and I cant see where the query is going wrong. The other fields look correct, its just that I am out of the desired date range.
SELECT
mdl_user_info_data.data,
mdl_user.firstname,
mdl_user.lastname,
mdl_grade_grades.itemid,
mdl_grade_items.itemname,
mdl_quiz.fcpd,
mdl_user_info_data.id,
mdl_grade_grades.timecreated AS DATE
FROM mdl_grade_grades
INNER JOIN mdl_user ON mdl_grade_grades.userid = mdl_user.id
INNER JOIN mdl_grade_items ON mdl_grade_grades.itemid = mdl_grade_items.id
INNER JOIN mdl_quiz ON mdl_grade_items.courseid = mdl_quiz.course
INNER JOIN mdl_user_info_data ON mdl_user.id = mdl_user_info_data.userid
INNER JOIN mdl_course ON mdl_grade_items.courseid = mdl_course.id
WHERE mdl_grade_grades.timecreated BETWEEN (FROM_UNIXTIME(1371704400) AND FROM_UNIXTIME(1371790800))
AND mdl_user_info_data.fieldid = 1
AND mdl_grade_items.itemname IS NOT NULL
AND mdl_course.category = 27
OR mdl_grade_items.itemname LIKE '%asa%'
GROUP BY mdl_user.firstname, mdl_user.lastname, mdl_grade_grades.timecreated
The OR term negates some of the limits you built into the WHERE clause as it collects records that are not subject to your date selection.
Do you mean this?
WHERE mdl_grade_grades.timecreated BETWEEN (FROM_UNIXTIME(1371704400) AND
FROM_UNIXTIME(1371790800))
AND mdl_user_info_data.fieldid = 1
AND mdl_grade_items.itemname IS NOT NULL
AND
(
mdl_course.category = 27
OR
mdl_grade_items.itemname LIKE '%asa%'
)
You need to group the conditions in the WHERE clause.
When a row satisfies the condition OR mdl_grade_items.itemname LIKE '%asa%' it will be selected.
Use ( and ) to group the conditions. For example:
WHERE mdl_grade_grades.timecreated BETWEEN (FROM_UNIXTIME(1371704400) AND FROM_UNIXTIME(1371790800))
AND mdl_user_info_data.fieldid = 1
AND mdl_grade_items.itemname IS NOT NULL
AND (mdl_course.category = 27 OR mdl_grade_items.itemname LIKE '%asa%')

MySQL query sub query or grouping

Each modx_site_content record may have several records in modx_site_tmplvar_contentvalues.
I need to retrieve both modx_site_tmplvar_contentvalues.value where the tvv.tmplvarid = 3 AND the tvv.tmplvarid = 1. where tvv.tmplvarid is a future date, I need to return the tvv.value of tvv.tmplvarid 3 which is a comma separated list of tags.
This query does not return the values I need & I'm not sure how to get just what I want.
SELECT sc.id, sc.pagetitle, tvv.value, tvv.tmplvarid, tvv.id, tvv.value
FROM modx_site_content sc
left join modx_site_tmplvar_contentvalues tvv on tvv.contentid = sc.id
where published = '1'
and (tvv.tmplvarid = '3' and tvv.value >= curdate())
order by sc.id;
basically in the end I need to return only the list of tags (tvv.tmplvarid = 3) where the other associated record (tvv.tmplvarid = 1) is a date in the future.
Any thoughts, can this be done with grouping instead? I don't actually need anything from the modx_site_content table.
So you need to return the tags both rows in the modx_site_tmplvar_contentvalues table that has tmplvarid of 1 and 3 both related to the same modx_site_content, but only when the tmplvarid 3 row has a datetime field in the future?
I would do two separate joins to the modx_site_tmplvar_contentvalues tabe:
SELECT tOne.value, tThree.value
FROM modx_site_tmplvar_contentvalues tOne
INNER JOIN modx_site_content c ON tOne.contentid = c.id
INNER JOIN modx_site_tmplvar_contentvalues tThree ON tThree.contentid = c.id
WHERE c.published = 1
AND tOne.tmplvarid = 1
AND tThree.tmplvarid = 3 AND tThree.date > NOW()
SQL Fiddle: http://sqlfiddle.com/#!2/a4031/2
I figured it out. Amazing what you can do if you just read the docs ;)
select tv.contentid, tv.value as eventdate, sc.id, sc.pagetitle, tvv.value from
(
select * from modx_site_tmplvar_contentvalues cv
where cv.tmplvarid = 3
and cv.value >= curdate()
) as tv
left join modx_site_content sc on sc.id = tv.contentid
left join modx_site_tmplvar_contentvalues tvv on tvv.contentid = sc.id
where (tvv.tmplvarid = 1)
order by value asc

Modifying a query that had a left join and an inner join

The query below sums up points from the MySQL table "comment" when the following conditions are met:
The loginid when l.username = '$profile' is found.
All the submissionids are found that have the loginid from #1 above.
All the commentids with the submissionids from #2 above are found, and the corresponding points are summed.
Now, how could I make a different query that returns an array of all of the comments in #3 above rather than summing the points?
Here are the MySQL tables involved:
login:
logind username created activated
submission:
submissionid loginid
comment:
commentid submissionid points comment
Query:
SELECT
l.loginid,
l.username,
l.created,
l.activated,
COALESCE(scs.total, 0) AS commentsreceivedvalue
FROM login l
LEFT JOIN (
SELECT S2.loginid, SUM(C2.points) AS total
FROM submission S2
INNER JOIN comment C2
ON S2.submissionid = C2.submissionid
GROUP BY S2.loginid
) scs ON scs.loginid = l.loginid
WHERE l.activated = 1
AND l.username = '$profile'
GROUP BY l.loginid
ORDER BY commentsreceivedvalue DESC
Isn't it as simple as:
SELECT l.loginid, l.username, l.created, l.activated, scs.commentid, scs.comment
FROM login AS l
LEFT JOIN (SELECT S2.loginid, c2.commentid, c2.comment
FROM submission AS S2
JOIN comment AS C2 ON S2.submissionid = C2.submissionid
) AS scs ON scs.loginid = l.loginid
WHERE l.activated = 1
AND l.username = '$profile'
ORDER BY scs.commentid DESC;
The outer GROUP BY in the original was not doing anything useful. The ORDER BY in the original was replaced here by the ordering in reverse order of comment ID, which is an approximation to reverse chronological order (most recent first, in other words).