I'm using MySQL and trying to sum the number of hours that user has participated in events, but only for a specific organization.
EDIT: I have another table thrown into the mix (misEvents). I think the GROUP BY statement is causing some problems, but I'm not quite sure what to do about it.
table events: contains the "event" information. orgID is the ID of the organization hosting it
-each listing is a separate event
ID | orgID | hours
1 | 1 | 1
2 | 1 | 2
3 | 2 | 1
table eventUserInfo: shows which users attended the events
-refID references the ID column from the events table
ID | refID | userID
1 | 1 | 1
2 | 1 | 3
3 | 2 | 2
4 | 2 | 1
5 | 3 | 2
table miscEvents
-these are entered in manually if an event by an organization wasn't posted on the site, but
the users still wants to keep track of the hours
ID | userID | orgID | Hours
1 | 1 | 1 | 2
So, when I view the member activity for organization 1, the following table should display
userid | total hours
1 | 5 //participated in ID #1 and 2 and a misc event, total of 4 hours
2 | 2 //participated in ID #2 only for org 1
3 | 1 //participated only in ID #1
assume the given input is $orgID which is set to 1 in this case
SELECT eventUserInfo.userID, sum(events.hours) as hours,sum(miscEvents.hours) AS mhours FROM events
JOIN eventUserInfo ON events.ID = eventUserInfo.refID
JOIN miscEvents ON events.orgID = miscEvents.orgID
WHERE events.orgID = '$orgID'
GROUP BY eventUserInfo.userID
I think it should be:
SELECT eventInfo.userID --- there is no "events.userID" column
, SUM(events.hours) AS hours --- alias added
FROM events
JOIN eventInfo --- no need for LEFT JOIN
ON events.ID = eventInfo.refID
WHERE events.orgID = '$orgID'
GROUP BY eventInfo.userID
The problem lies probably in that you are trying to print the "hours" with: $event['hours'] but there is no hours column returned (only userID and SUM(event.hours). Use an alias in the SELECT list, as above.
Or since eventINFO seems to be you primary table in the query:
SELECT eventINFO.userID, SUM(events.hours)
FROM eventINFO
JOIN events ON eventINFO.refID = events.ID
WHERE events.orgID = '$orgID'
GROUP BY eventINFO.userID
Should result in the same as ypercube but to me seems a little more clear calling your primary table in the FROM call
To your new problem, you need to get rid of this:
sum(events.hours + miscEvents.hours)
If I'm not mistaken, you can't have multiple columns in a SUM you can only add a single column per call.
I would try something like:
SUM(events.hours) AS hours, SUM(miscEvents.hours) AS mhours
Then, in your function, add together those values $total = hours + mhours
Related
I will explain my problem with an example:
Tables:
place - comment - activities_comment - activities
User can comment a place and select what kind of activities are being able to do in that place.
Activities are for example: run, swim, walk and climb.
So some users would vote more than 1 activity, so i can't put it right in the same table COMMENT, i need to create a pivote table ACTIVITIES_COMMENT, the problem goes here, i was able to show all the comments with their activities selected in each place..
But now i want to count the number of activities and order them by the most selected activity to the less selected by the users in the comments for each place.
How can I do that??
The best thing i can do is:
$place = Place::with('city','comments')
->where('id',$placeId)->first();
$place->activities = Activity::join('activities_comment', 'activity.id', '=', 'activities_comment.activity_id')
->join('comment', 'comment.id', '=', 'activities_comment.comment_id')
->select('comment.id','activities_comment.activity_id',
DB::raw('(SELECT count(activities_comment.activity_id) FROM activities_comment WHERE activities_comment.activity_id = 1) as run'),
DB::raw('(SELECT count(activities_comment.activity_id) FROM activities_comment WHERE activities_comment.activity_id = 2) as swim'),
DB::raw('(SELECT count(activities_comment.activity_id) FROM activities_comment WHERE activities_comment.activity_id = 3) as walk'),
DB::raw('(SELECT count(activities_comment.activity_id) FROM activities_comment WHERE activities_comment.activity_id = 4) as climb'),
)
->where('comment.place_id',$place->id)
->get();
The problem is that this query counts the most selected activities but in ALL places, i want to count only in each place.
EDIT:
Example rows:
place table:
id | name
----------
1 | Alaska
2 | Peru
3 | Argentina
comment table:
id | user_id | place_id | text
------------------------------------
1 | 1 | 1 | some text
2 | 3 | 1 | some text
3 | 2 | 2 | some text
activity table:
id | name
----------
1 | run
2 | swim
3 | walk
4 | climb
activity_comment table:
id | comment_id | activity_id
------------------------------
1 | 1 | 1
2 | 1 | 2
3 | 2 | 2
4 | 3 | 2
When i get into Alaska comments, i would like to see the times that users selected an activity there, for alaska it will show:
run: 1 time
swim: 2 times
walk: 0 times
climb: 0 times
If i go to Peru comments:
run: 0 times
swim: 1 time
walk: 0 times
climb: 0 times
Thank you in advance.
First, get the tables all linked up via the joins showing how each table is related to the next by their respective keys. Then, its just a matter of each name (city/location), and name of the activity and getting a count grouped by the respective location and activity.
The order by clause will put all same locations first, then within each location will put the highest count activity secondary... if any have same counts, then the names will be alpha ordered
SELECT
p.name,
a.name as Activity,
COUNT(*) as NumComments
from
place p
join comment c
ON p.id = c.place_id
join activity_comment ac
ON c.id = ac.comment_id
join activity a
ON ac.activity_id = a.id
group by
p.name,
a.name
order by
p.name,
COUNT(*) desc,
a.name
Now, you will just need to plug in your WHERE clause (before the group by) for whatever location or activity you may be interested in.
The following is a simplified version of the problem I'm trying to solve. I just can't figure it out.
I have two tables:
Table 1: messages
id | timestamp | message
------------------------
1 | 2014-01-20 09:00:00 | Hello I'm a message
2 | 2014-01-20 09:00:00 | Second message for you
3 | 2014-01-27 11:00:00 | This message has been updated
4 | 2014-01-28 13:45:00 | Last message for now
and
Table 2: reads
id | message_id | user_id | last_read_timestamp
------------------------
1 | 1 | 1 | 2014-01-20 09:10:00
1 | 2 | 1 | 2014-01-20 09:15:00
1 | 3 | 1 | 2014-01-24 19:25:00
1 | 1 | 2 | 2014-01-28 13:45:00
id in Table a relates to message_id in Table 2.
The messages in Table A are updated frequently and the timestamp is updated. So I need to run a query that will return the Table A id of any messages that have been updated SINCE they were read for a particular user_id, for that the user hasn't read (and therefore no corresponding entry appears in Table 2.
So for user_id = 1, message_id 3 has been updated since being read, and message_id 4 is a new message. The result for user_id = 1 would be:
Unread
------
3
4
I've tried a LEFT JOIN between the tables but just can't seem to get the correct result.
If you just need to retrieve messages unread for a single user, you could use something like this:
SELECT messages.id AS unread
FROM
messages LEFT JOIN (SELECT message_id, MAX(last_read_timestamp) as last_rd
FROM reads
WHERE user_id = 1
GROUP BY message_id) lr
ON messages.id = lr.message_id
AND messages.timestamp < lr.last_rd
WHERE
lr.message_id IS NULL
The Table A id of any messages that have been updated SINCE they were read for a particular user_id; will give below query.(not tested)
select * from table1
left join table2 on (table1.id=table2.message_id && table1.timestamp>table2.last_read_timestamp)
where table2.user_id=1;
I have one table - staff
id | staff Name | adress
-------------------------
1 | Mr.A | Any Address
2 | Mr. B | Any Address
2nd Table - employment_history
eid | staff_id | school_id | type | grade | date_of_appointmet
--------------------------------------------------------------------
1 | 1 | 1 |Promotion | 17 | 2012-12-12
2 | 1 | 2 |promotion | 18 | 2013-2-2
3 | 2 | 2 |appointment | 17 | 2013-3-3
and so on tables moves
Now the Question is that
i want to get the latest job of the person with his details from the staff table
how can i count how many of 17 grade staff works in school_id 1
(remembering that staff_id 1 (mr.a) now have been promoted to 18 and now works in school_id 2.)
select staff_id, max(date_of_appointment) as date_of_appointment
from employment_history
group by staff_id
This query will return the most recent staff record for each staff_id. Turn it into a subquery and join onto the employment history table
Select grade, count(1)
from
(select staff_id, max(date_of_appointment) as date_of_appointment
from employment_history
group by staff_id) a
inner join employment_history e on e.staff_id = a.staff_id and a.date_of_appointment = e.date_of_appointment
group by grade
This solution makes the assumption that staff_id + date_of_appointment is a unique key...if you have multiple rows where one staff_id has multiple employment history entries for one date, this won't work. You need some logic to make the 'most recent employment history entry' return a unique combination of data...if staff_id + max(date_of_appointment) is not unique, you'll need to come up with logic in the 'a' subquery that returns unique data.
What about something like
SELECT *
FROM employment_history eh1
WHERE eh1.date_of_employment = (
SELECT max(eh2.date_of_employment)
FROM staff s
JOIN employment_history eh2 ON s.id = eh2.staff_id
WHERE s.id = ?
)
Recplacing the ?, or using bind_param() as appropriate.
I have a table that holds price information. I need to select the max value of every three rows. EXAMPLE:
Table `daily_high`
____ _______
| ID | HIGH |
| 1 | 24.65 |
| 2 | 24.93 |
| 3 | 26.02 |
| 4 | 25.33 |
| 5 | 25.16 |
| 6 | 25.91 |
| 7 | 26.05 |
| 8 | 28.13 |
| 9 | 27.07 |
|____|_______|
Desired output to new table (ID will be auto-increment so don't assume an association exists between this ID 1 and the daily_high ID 1:
____ ___________
| ID | 3MaxHIGH |
|____|___________|
| 1 | 26.02 |
| 2 | 25.91 |
| 3 | 28.13 |
|____|___________|
I want to compare IDs 1,2, and 3 to determine the high value among them. Then once I have compared 1-3, I want to move on to 4 through 6, then 7 through 9, etc until I've done this for all values contained in the table (currently about 400,000 values). I have written code that uses
SELECT max(HIGH) FROM daily_high as dh1 JOIN (SELECT max(HIGH) FROM daily_high WHERE id >= dh1 AND id < (dh1.id + 3))
This works but is horribly slow. I've tried using the SELECT statement where I identify the column values to be pull for display, meaning between the SELECT and FROM parts of the query.
I've tried to use JOIN to join all 3 rows onto the same table for comparison but it too is horribly slow. By slow I mean just under 10 seconds to gather information for 20 rows. This means that the query has analyzed 60 rows (20 groups of 3) in 9.65879893303 seconds (I didn't make this up, I used microtime() to calculate it.
Anyone have any suggestions for faster code than what I've got?
Keep in mind that my actual table is not the same as what I've posted above, but it the concept is the same.
Thanks for any help.
If you ID it continous you can make this
SELECT floor(id/3) as range, max(HIGH) FROM daily_high GROUP BY range;
Why not to use DIV operator for grouping your aggregation:
SELECT (id-1) DIV 3 + 1 AS ID, MAX(high) AS 3MaxHIGH
FROM daily_high
GROUP BY (id-1) DIV 3
This query gives the same result.
ID 3MaxHIGH
1 26.02
2 25.91
3 28.13
I was unable to run your query, and I believe that this one is faster.
UPD: To ensure that you have valid groups for your ranges, use this query:
select id, high, (id-1) div 3 + 1 from daily_high
result:
id high (id-1) div 3 + 1
1 24.65 1
2 24.93 1
3 26.02 1
4 25.33 2
5 25.16 2
6 25.91 2
7 26.05 3
8 28.13 3
9 27.07 3
Fuller answer with an example. The following code will do what I think you want.
SELECT FLOOR((row - 1) / 3), MAX(Sub1.high)
FROM (SELECT #row := #row + 1 as row, daily_high.*
FROM daily_high, (SELECT #row := 0) r) Sub1
GROUP BY FLOOR((row - 1) / 3)
ORDER BY Sub1.ID
The below query worked for me on a test table. perhaps not the best, but the other solutions failed on my test table.
This does require the ID's to be sequential. Also be sure to put an index on High aswell for speed.
SELECT FLOOR(T1.Id/3)+1 AS Id, ROUND(GREATEST(T1.High, T2.High, T3.High),2) AS High FROM `daily_high` T1, `daily_high` T2, `daily_high` T3
WHERE T2.Id=T1.Id+1
AND T3.Id=T2.Id+1
AND MOD(T1.Id, 3)=1
logic: if(id is divisible by 3, id/3-1, id/3)
select if(mod(id,3) = 0,floor(id/3)-1,floor(id/3)) as group_by_col , max(HIGH)
FROM daily_high GROUP BY group_by_col;
I have two tables one called meeting and one called attendance, attendance is a many to many relational database in the following format:
Attendance:
user_id | meeting_id | invited
--------+------------+--------
1 | 5 | 1
2 | 5 | 0
3 | 4 | 0
3 | 5 | 1
3 | 6 | 0
Meetings are in the following format:
Meetings:
meeting_id | meeting_name | owner_id
-----------+--------------+----------
3 | Awesome | 2
4 | Boring | 2
5 | Cool | 5
9 | Sexy | 3
There can only be one meeting row per meeting, but unlimited attendance rows per meeting (limited to for every possible user for every meeting).
How in SQL and/or Propel do I create something that would list all meetings where the (provided) user_id is either the owner_id in meetings OR were the user_id and invited in the attendance database.
I am looking for a result (based on the above data) when searching for userid 3 of:
Result for userid3:
meeting_id | meeting_name | owner_id
-----------+--------------+----------
5 | Cool | 5 - Because userid 3 is attending meeting 5
9 | Sexy | 3 - Because userid 3 owns meeting 9
I currently have the following which doesn't work really, and produces multiple rows per meeting (because the meeting exists more than once in the attendance DB).
$criteria->addJoin(MeetingMeetingsPeer::ID, MeetingAttendancePeer::MEETING_ID, Criteria::LEFT_JOIN);
$criterion = $criteria->getNewCriterion(MeetingMeetingsPeer::OWNER_ID, Meeting::getUserId());
$criterion->addOr($criteria->getNewCriterion(MeetingAttendancePeer::USER_ID, Meeting::getUserId()));
$criteria->add($criterion);
return $criteria;
Which is something like the below in SQL:
SELECT meeting_meetings.ID, meeting_meetings.OWNER_ID, meeting_meetings.GROUP_ID, meeting_meetings.NAME, meeting_meetings.COMPLETED, meeting_meetings.LOCATION, meeting_meetings.START, meeting_meetings.LENGTH, meeting_meetings.CREATED_AT, meeting_meetings.UPDATED_AT FROM `meeting_meetings` LEFT JOIN meeting_attendance ON (meeting_meetings.ID=meeting_attendance.MEETING_ID) WHERE (meeting_meetings.OWNER_ID=1 OR meeting_attendance.USER_ID=1)
Thanks for your time,
This should get you all meetings that are owned by user_id 1, as well as all meetings that are attended by user_id 1.
SELECT meeting_meetings.ID, meeting_meetings.OWNER_ID, meeting_meetings.GROUP_ID, meeting_meetings.NAME,
meeting_meetings.COMPLETED, meeting_meetings.LOCATION, meeting_meetings.START, meeting_meetings.LENGTH,
meeting_meetings.CREATED_AT, meeting_meetings.UPDATED_AT
FROM `meeting_meetings`
WHERE `meeting_meetings`.`owner_id` = 1
UNION DISTINCT
SELECT meeting_meetings.ID, meeting_meetings.OWNER_ID, meeting_meetings.GROUP_ID, meeting_meetings.NAME,
meeting_meetings.COMPLETED, meeting_meetings.LOCATION, meeting_meetings.START, meeting_meetings.LENGTH,
meeting_meetings.CREATED_AT, meeting_meetings.UPDATED_AT
FROM `meeting_meetings`
JOIN `meeting_attendance` ON `meeting_meetings`.`meeting_id` = `meeting_attendance`.`meeting_id` AND `meeting_attendance`.`invited`
WHERE `meeting_attendance`.`user_id` = 1
It's a bit clunky. Personally, I would consider adding an owner flag to the meetings_attendance table.