Simple MySQL query using SELF JOIN - mysql

I have a table called image which is a table of images taken by a camera which records a car registration under variable reg, the camera number it was taken on under variable camera and a timestamp in the form yyyy-mm-dd 00:00:00 under variable whn. I have been asked to find the following:
"For each of the vehicles caught by camera 19 - show the registration, the earliest time at camera 19 and the time and camera at which it left the zone."
Therefore, I am finding the minimum time any particular cars were captured by camera 19, and then then the latest time on that date each car was captured along with the camera it was captured at. So far, I have the following code:
SELECT early.reg,
LEFT(MIN(early.whn), 10) AS date,
RIGHT(MIN(early.whn), 8) AS 'in',
RIGHT(MAX(late.whn), 8) AS 'out'
FROM image late
JOIN image early ON (early.reg = late.reg)
WHERE (early.camera = 19)
GROUP BY early.reg
This works perfectly fine, I just need to add the camera the maximum time was captured at where the max time is given by RIGHT(MAX(late.whn), 8) AS 'out' and I am struggling to do it. I tried adding late.camera within the SELECT call but then obviously you have to add GROUP BY late.camera which returns the latest time it was captured at each camera. Any help appreciated.

OK, Now a better understanding and clarified explanation to make sure I get what you want... The impression I am getting is as follows:
You are monitoring traffic, such as on toll roads. There are different cameras along the route both north/south or east/west bound. On any given date, you have a list of all recorded transactions read by all cameras along the route. You want to know that for any car that passed a specific camera (and we don't know the directional basis of it), you want to know where did the car finally get out of camera ranges on that route. Ex: Cameras 1-30. You know about cars getting within sight of camera 19, but they may get off the road after camera 22, 26, 29, whatever. So, for those cars that were seen at Camera 19, where was the LAST camera they passed.
If this is correct, I was close on the intent. The inner query was ALMOST the same. For a given vehicle registration ID, I am still storing the minimum and maximum dates it was spotted (could have gotten on the road at camera 4 for example, and off at camera 27 assuming cameras are sequential, but not required). The HAVING clause based on that requires that camera 19 WAS ONE of the cameras that were included in the trip. If someone got on at camera 1 and off at camera 18, they would NOT be included (provided cameras are truly sequential, but more for following along purposes).
So now I have all registrations, min and max date/times. Now, I am re-joining to the same image table based on the registration and respective min or max date since it will only be a single record per registration, no need for group by at the outer level. You would never have duplicate times for a given camera and it had to exist from the PQ query.
Now, just pull the respective camera. So the query below actually gives both the camera they were FIRST identified at, qualified as passing camera 19, and where they were LAST identified by camera.
SELECT
PQ.Reg,
LEFT(PQ.MinDateForDay, 10) AS date,
RIGHT(PQ.MinDateForDay, 8) AS 'in',
iMinDateCam.Camera CameraIn,
PQ.TimeAtCamera19,
RIGHT(PQ.MaxDateForDay, 8) AS 'out',
iMaxDateCam.Camera CameraOut
from
( SELECT
i.reg,
min( i.whn ) as MinDateForDay,
MAX( case when i.Camera = 19 then i.whn else '' end ) TimeAtCamera19,
max( i.whn ) as MaxDateForDay
FROM
image i
GROUP BY
i.reg
having
MAX( case when i.Camera = 19 then 1 else 0 end ) = 1 ) PQ
join image iMinDateCam
ON PQ.reg = iMinDateCam.reg
AND PQ.MinDateForDay = iMinDateCam.whn
join image iMaxDateCam
ON PQ.reg = iMaxDateCam.reg
AND PQ.MaxDateForDay = iMaxDateCam.whn

You are nearly there with the extra groupby required, just add a where clause to restrict the late.whn returned only to the max one
SELECT early.reg,
LEFT(MIN(early.whn), 10) AS date,
RIGHT(MIN(early.whn), 8) AS 'in',
RIGHT(MAX(late.whn), 8) AS 'out',
late.camera
FROM image late
JOIN image early ON (early.reg = late.reg)
WHERE (early.camera = 19)
and (late.whn = (select max(whn) from late))
GROUP BY early.reg, late.camera

Related

GROUP BY one variable but display multiple SQL

I'm trying to GROUP BY one categorical variable but also show another corresponding variable in my output in SQL. Here is what the table looks like:Original Data Table
There are three main variables I'm working with here: Game, Platform and Week. Week variable states the position of the game under the global charts, so 1 would mean its the number 1 game. I want to find the game with the most weeks inside the top 10 BY platform, so I'm trying to get my table to look like this:
Platform | Game | Most_weeks_top10
Right now, I tried the following steps:
SELECT platform, game, COUNT(*) AS total
FROM global_weekly_charts_2013_2014
WHERE week <= 10
GROUP BY game, platform;
Which returns this:
Table Grouped By Platform AND Game
However, I only want the game, platform and total weeks of the game with max number of weeks in top 10. I tried
SELECT game, platform, Max(total) OVER (PARTITION BY platform)
from the derived table but did not get the desired output. I feel like the solution is right there and not that difficult but I can't seem to get the answer.
please add “ORDER BY total DESC LIMIT 10”.try again

mysql group by where another field is the same

I have a database full of train movement data when a train enters a stations we may get an arrival message and when the train leaves the station to head to the next destination we may get a departure message.
therefore when a train hits a station we will normally get 2 messages 1 for when it arrived and 1 for when it departed. However sometimes there are mistakes in this data and so we can get another movement message to correct the departure/arrival data. If a movement message is a correction of a previous one it will have a correction_ind of 1 otherwise it will have a correction_ind of 0.
This means that for a given station we can have a total of 4 messages (departure, arrival, fixed departure, fixed arrival)
I'm trying to get 0/1 departure messages and 0/1 arrival messages for each station along a route for a specific train. Where we select movement message in the following order:
Pick the fixed message (if it exists) otherwise
Pick the first movement message (if it exists) otherwise
don't pick anything
My query looks like this:
SELECT
tm.variation_status,
tm.planned_timestamp,
tm.platform,
tm.actual_timestamp,
tm.event_type,
tm.timetable_variation,
sched.tps_description
FROM
train_activation ta,
train_movement tm
LEFT JOIN
cif_tiploc sched ON sched.stanox = tm.loc_stanox
WHERE
train_uid = 'C40200'
AND date(creation_timestamp) = '2014-08-20'
AND tm.train_id = ta.train_id
ORDER BY tm.correction_ind ASC
the problem I have with this query is for a given station we can get 0-2 departure messages and 0-2 arrival messages. If I add the following GROUP BY tm.event_type (this is the field that tells us if this is a departure or arrival message) we will only get 2 messages in total as it will group all the depature's together and all the arrivals together!
how can I re-write this query so we only select the best arrival/depature message for a each station along the route?
a station can be identified by tm.loc_stanox or sched.tps_description
a message will tell us if its an depart or arrival by tm.event_type
a message will tell us if its a correction to a previous message by tm.correction_ind which will be 1 if its a correction or 0 if its not
any help on the issue would be amazing.
How about trying an order by case/when condition, but for each respective station.
order by
tm.loc_stanox,
case when tm.event_type like 'fixed%' then 1
when tm.event_type like 'arrive%' then 2
when tm.event_type like 'depart%' then 3 end
I wish I had more sample data to see the real scenarios you are describing to better assist.

Relational Database Logic

I'm fairly new to php / mysql programming and I'm having a hard time figuring out the logic for a relational database that I'm trying to build. Here's the problem:
I have different leaders who will be in charge of a store anytime between 9am and 9pm.
A customer who has visited the store can rate their experience on a scale of 1 to 5.
I'm building a site that will allow me to store the shifts that a leader worked as seen below.
When I hit submit, the site would take the data leaderName:"George", shiftTimeArray: 11am, 1pm, 6pm (from the example in the picture) and the shiftDate and send them to an SQL database.
Later, I want to be able to get the average score for a person by sending a query to mysql, retrieving all of the scores that that leader received and averaging them together. I know the code to build the forms and to perform the search. However, I'm having a hard time coming up with the logic for the tables that will relate the data. Currently, I have a mysql table called responses that contains the following fields,
leader_id
shift_date // contains the date that the leader worked
shift_time // contains the time that the leader worked
visit_date // contains the date that the survey/score was given
visit_time // contains the time that the survey/score was given
score // contains the actual score of the survey (1-5)
I enter the shifts that the leader works at the beginning of the week and then enter the survey scores in as they come in during the week.
So Here's the Question: What mysql tables and fields should I create to relate this data so that I can query a leader's name and get the average score from all of their surveys?
You want tables like:
Leader (leader_id, name, etc)
Shift (leader_id, shift_date, shift_time)
SurveyResult (visit_date, visit_time, score)
Note: omitted the surrogate primary keys for Shift and SurveyResult that I would probably include.
To query you join shifts and surveys group on leader and taking the average then jon that back to leader for a name.
The query might be something like (but I haven;t actually built it in MySQL to verify syntax)
SELECT name
,AverageScore
FROM Leader a
INNER JOIN (
SELECT leader_id
, AVG(score) AverageScore
FROM Shift
INNER JOIN
SurveyResult ON shift_date = visit_date
AND shift_time = visit_time --depends on how you are recording time what this really needs to be
GROUP BY leader ID
) b ON a.leader_id = b.leader_id
I would do the following structure:
leaders
id
name
leaders_timetabke (can be multiple per leader)
id,
leader_id
shift_datetime (I assume it stores date and hour here, minutes and seconds are always 0
survey_scores
id,
visit_datetime
score
SELECT l.id, l.name, AVG(s.score) FROM leaders l
INNER JOIN leaders_timetable lt ON lt.leader_id = l.id
INNER JOIN survey_scores s ON lt.shift_datetime=DATE_FORMAT('Y-m-d H:00:00', s.visit_datetime)
GROUP BY l.id
DATE_FORMAT here helps to cut hours and minutes from visit_datetime so that it could be matched against shift_datetime. This is MYSQL function, so if you use something else you'll need to use different function
Say you have a 'leader' who has 5 survey rows with scores 1, 2, 3, 4 and 5.
if you select all surveys from this leader, sum the survey scores and divide them by 5 (the total amount of surveys that this leader has). You will have the average, in this case 3.
(1 + 2 + 3 + 4 + 5) / 5 = 3
You wouldn't need to create any more tables or fields, you have what you need.

Using SQL to Aggregate and Calculate Stats

I have shoot 'em game where users compete against each other over the course of a week to accumulate the most points. I want to write a query that aggregates statistical data from the shots table. The tables and relationships of concern here are:
user has many competition_periods
competition_period belongs to user
competition_period has many shots
shot belongs to competition_period
In the shots table I have the following fields to work with:
result --> string values: WON, LOST or TIED
amount_won --> integer values: e.g., -100, 0, 2000, etc.
For each user, I want to return a result set with the following aggregated stats:
won_count
lost_count
tied_count
total_shots_count (won_count + lost_count + tied_count)
total_amount_won (sum of amount_won)
avg_amount_won_per_shot (total_amount_won / total_shots_count)
I've worked on this query for few hours now, but haven't made much headway. The statistical functions trip me up. A friend suggested that I try to return the results in a new virtual table called shot_records.
Here is the basic solution, computing the statistics across all shots for a given player (you didn't specify if you want them on a per-competition-period basis or not):
SELECT user, SUM(IF(result = 'WON', 1, 0)) AS won_count,
SUM(IF(result = 'LOST', 1, 0)) AS lost_count,
SUM(IF(result = 'TIED', 1, 0)) AS tied_count,
COUNT(*) AS total_shots_count,
SUM(amount_won) AS total_amount_won,
(SUM(amount_won) / COUNT(*)) AS avg_amount_won_per_shot
FROM user U INNER JOIN competition_periods C ON U.user_id = C.user_id
INNER JOIN shots S ON C.competition_period_id = S.competition_period_id
GROUP BY user
Note that this includes negatives in calculating the "total won" figure (that is, the total is decreased by losses). If that's not the correct algorithm for your game, you would change SUM(Amount) to SUM(IF(Amount > 0, Amount, 0)) in both places it occurs in the query.

Database design for Group Notification System

I am trying to create a group notification system. If I am in a group, then anyone who comment on the group's wall, a notification will send out to every group member. Here is my database design: I have two tables: Notification and NotificationRead.
NotificationRead
+userId (String)
+lastRead (int) - default is 0
Notification
...
+time(int)
...
Every user has one entry in NotificationRead, it keep track of when is the last time I read my notification.
The logic is: for a particular user, if Notification.time > NotificationRead.lastRead, then that notification is considered unread. Let say that in group A, there are 4 notifications I have not read, and their time is 7, 8, 9, 10, then when I click onto group A, I set my NotificationRead.lastRead = 10 (the largest time), so I wont read them again. New notifications will have their time start at 11. Now, here is my problem. Let say I have 3 groups, A, B and C
A (4): largest time is 10
B (1): largest time is 14
C (1): largest time is 12
if I click onto A, my NotificationRead.lastRead = 10, the 4 next to A clear off, 1 next to B and C stay put. Now if I click on B, my lastRead now is 14, so not only it clear off the 1 next to B but also the 1 next to C since 14 > 12. Can anyone help me think of a way to solve this. I am open to completely redesign everything
Cant you just add a groupID column to your NotificationRead table so you know the lastRead value for each User\Group combination>?
If you wish to know the last notification time per user per group, you must store that information. Therefore, each user must have more than one record in NotificationRead, which must become a separate table from the user table. This table will have three columns, the user_id, the group_id, and the lastread value for that user/group.