Mysql query with multiple LEFT join issue - mysql

I want to get this kind of result from a mysql query:
Name | Asked Date | Granted Date | Duration
Joe | 2016-07-01 10:02:01 | 2016-07-01 10:02:05 | 10
Ben | 2016-07-01 10:04:24 | 2016-07-01 10:04:26 | 12
....
Every entries are stored in a table that look like this:
id | action_date | action_type | unique_instance | name
12 | 2016-07-01 10:02:01 | Asked | 6546532161654 | Joe
13 | 2016-07-01 10:02:06 | Granted | 6546532161654 | Joe
14 | 2016-07-01 10:05:12 | Asked | 6546532161654 | Ben
15 | 2016-07-01 10:05:15 | Granted | 6546532161654 | Ben
16 | 2016-07-01 10:06:06 | Finished | 6546532161654 | Joe
I've tried to do this query, but it didn't worked:
Select table.name as Name,
table.action_date as Asked,
g.action_date as Granted,
TIMESTAMPDIFF(SECOND, g.action_date, q.action_date) as Duration
FROM table
LEFT JOIN table g ON table.unique_instance = g.unique_instance AND g.action_type = 'Granted'
LEFT JOIN table q ON table.unique_instance = q.unique_instance AND q.action_type = 'Finished'
WHERE table.action_type = 'Asked'
AND table.unique_instance = '6546532161654'
GROUP BY table.action_date;

As stated in the comments.
Your design of your database seem incorrect as you are not even making use of your ID.
You aren't able to identify which record is unique and related to what, however you can still use the name but it's not unique, since later on in the future you are still going to have records with the same name.
My suggestion would be having action_type and ID as primary key. That way you can always use the same ID and changing the action_type accordingly.
Example (note that you can't have more than 1 record containing this):
ID: 1, action_type: Asked
ID: 1, action_type: Granted
ID: 1, action_type: Finished
In order to debug your code, this query should return just 1 record:
select * from table where unique_instance = '6546532161654' AND action_type = 'Granted'
But since unique_instance is not unique after all, it is going to grab other unwanted data.
Note: Your query is correct.

Related

MySQL Query isn't showing most recent username

I have a special problem with my statistics query. I want to get the most recent username, duration, and connection count of every client that connected to a server. This is the query:
SELECT name, SUM(duration) AS time, COUNT(auth) AS connections
FROM analytics
WHERE duration IS NOT NULL
GROUP BY auth
ORDER BY time DESC;
The problem is that they query isn't showing the most recent username. It takes the first entrance of the client (identified by auth) in the database even if the client changed their username already.
Is there a way to get the most recent username of the client in the query above without slowing it down a lot?
Example Table:
| id | auth | name | duration |
|----|------|------|----------|
| 1 | u123 | Fire | 50 |
| 2 | u555 | Dan | 20 |
| 3 | u123 | Ice | 30 |
What I get:
| name | time | connections |
|------|----------|-------------|
| Fire | 80 | 2 |
| Dan | 20 | 1 |
What I want
| name | time | connections |
|------|----------|-------------|
| Ice | 80 | 2 |
| Dan | 20 | 1 |
I assume that you want most recent auth order by ID coz there is no other data to get it.
SELECT A.name, B.time, B.connections
FROM analytics AS A
INNER JOIN
(
SELECT auth, MAX(ID) AS MAXID, SUM(duration) AS time, COUNT(auth) as connections
FROM analytics B
WHERE duration IS NOT NULL
GROUP BY auth
) AS B
ON A.auth = B.auth AND A.ID = B.MAXID
If you have a TimeStamp column for the recent registred user.
you can get it using the max()
select * from database where registered like (select max(registered) from database;
I wish it will help. Just analyze the idea.

Troubles conceptualizing a query

I have a 'Course' table and an 'Event' table.
I would like to have all the courses that actually take place, i.e. they are not cancelled by an event.
I have done this by a simple request for all the course and a script analysis (basically some loops), but this request take a time that I believe too long. I think what I want is possible in one query and no loops to optimize this request.
Here are the details :
'Course' c have the fields 'date', 'duration' and a many to many relation with the 'Grade' table
'Event' e have the fields 'begin', 'end', 'break' and a many to many relation with the 'Grade' table
A course is cancelled by an event if they occur at the same time and if the event is a break (e.break = 1)
A course is cancelled by an event if all the grades of the course are in the events that occurs at the same time (many events can occurs, I have to sum up the grades of these events and compare them to the grades of the courses). This is the part I'm doing with a loop, I have some trouble to conceptualize that.
Any help is welcome,
Thanks in advance,
PS : I'm using mysql
EDIT : Tables details
-Course
+-----------+-------------+------+-----+---------+----------------+
| Field | Type | Null | Key | Default | Extra |
+-----------+-------------+------+-----+---------+----------------+
| id | int(11) | NO | PRI | NULL | auto_increment |
| date | datetime | NO | | NULL | |
| duration | time | NO | | NULL | |
| type | int(11) | NO | | NULL | |
+-----------+-------------+------+-----+---------+----------------+
+-------+---------------------+----------+------+
| id | date | duration | type |
+-------+---------------------+----------+------+
| 1 | 2013-12-10 10:00:00 | 02:00:00 | 0 |
| 2 | 2013-12-11 10:00:00 | 02:00:00 | 0 |
+-------+---------------------+----------+------+
-Event
+-------------+-------------+------+-----+---------+----------------+
| Field | Type | Null | Key | Default | Extra |
+-------------+-------------+------+-----+---------+----------------+
| id | int(11) | NO | PRI | NULL | auto_increment |
| begin | datetime | NO | | NULL | |
| end | datetime | YES | | NULL | |
| break | tinyint(1) | NO | | NULL | |
+-------------+-------------+------+-----+---------+----------------+
+----+---------------------+---------------------+-------+
| id | begin | end | break |
+----+---------------------+---------------------+-------+
| 1 | 2013-12-10 00:00:00 | 2013-12-11 23:59:00 | 1 |
+----+---------------------+---------------------+-------+
-course_grade
+-----------+----------+
| course_id | grade_id |
+-----------+----------+
| 1 | 66 |
| 2 | 65 |
| 2 | 66 |
+-----------+----------+
-event_grade
+----------+----------+
| grade_id | event_id |
+----------+----------+
| 66 | 1 |
+----------+----------+
So here, only the course 2 should appear, because course 1 has only one grade, and this grade has an event.
I like riddles, this is a nice one, has many solutions, I think
As you say 'Any help is welcome', I give an answer altough its not the solution (and it does not fit into a comment)
I dont know, if you just want (A) the naked statement (over and out), or if you want (B) to understand how to get to the solution, I take (B)
I start with 'what would I change' before starting about the solution:
you are mixing date,datetime,start,end and duration, try to use only one logic (if it is your model ofcourse) ie.
an event/course has a start and an end time (or start/duration)
duration should (IMHO) not be a time
try to find a smallest timeslice for events/course (are there 1 sec events? or is a granularity of 5' (ie. 10:00, 10:05, 10:10 ans so on) a valid compromise?
My solution, a prgmatic one not academic
(sounds funny, but does work good in a simillar prob I had see annotation)
Create a table (T_TIME_OF_DAY) having all from 00:00, 00:05, .. 23:55
Create a Table (T_DAYS) in a valid and usefull range (a year?)
the carthesian product - call it points in time - (ie. select date, time from T_DAYS,T_TIME_OF_DAY no condition) of them (days x times) 300*24*12 ~ 100.000 rows if you need to look at a whole year (and 5' are ok for you) - thats not much and no prob
the next step is to join the curses fitting to your points in time (and the rows are down to <<100.000)
if you next join these with your events (again using point in time) you should get what you want.
simplyfied quarters of a day:
12 12 12 12 12 12 12 12
08 09 10 11 12 13 14 15
|...|...|...|...|...|...|...|...
grade 65 (C).............2..................
grade 66 (C).........1...2..................
grade 65 (E)................................
grade 66 (e)........1111..................
(annotation: I use this logic to calculate the availabillity of services regarding to their downtimes per Month / Year, and could use the already in timeslices prepared data for display)
(second answer, because it is a totaly different and mor3 standard aproach)
I made an SQLFiddle for you
so what to do:
and thats the a solution:
step one (in mind) select course,grades (lets call them C)
step two (in mind) select events, grades (lets call them E)
and - tada -
select all from C where there a no rows in E that have the same grade and the same date(somehow) and eventtype='break'
so your solution:
select
id, date start_time, date+duration end_time, grade_id
from Course c join course_grade cg on c.id=cg.course_id
where not exists (
select grade_id, begin start_time, end end_time
from event_grade eg join event e on eg.event_id=e.id
where
eg.grade_id=cg.grade_id
and e.break=1
and
(
(e.begin<=c.date and e.end >=c.date+c.duration)
or e.begin between c.date and c.date+c.duration
or e.end between c.date and c.date+c.duration
)
)
I did take no attention to optimize here

Self join and recursive selection in a table

Assuming a table as below
| ID | NAME | ROLE | MGRID |
---------------------------
| 1 | ONE | 5 | 5 |
| 2 | TWO | 5 | 5 |
| 3 | THREE | 5 | 6 |
| 4 | FOUR | 5 | 6 |
| 5 | FIVE | 15 | 7 |
| 6 | SIX | 25 | 8 |
| 7 | SEVEN | 25 | 7 |
| 8 | EIGHT | 5 | 8 |
How do I get a list of all employees reporting to an employee, including the ones who are in subsequent reporting levels below?
I mean, given emp id 5, I should get [1, 2] and given 7, I should get [1, 2, 5, 7]. How do I get this done?
Will self joins be of help here? Need to brush up my knowledge on joins now.
SELECT id
FROM emp
START WITH id = 7
CONNECT BY NOCYCLE mgrid = PRIOR id
SQLFIDDLE LINK
Here is a SQL statement using Oracle.
select id, name, role, mgrID
from employees
start with id = 7
connect by NoCycle prior id = mgrid;
Please note that the manager for employee 7 is the employee 7 - they are their own manager. This will cause an error - "Connect By loop in user data'. By using the NoCycle keyword you can tell Oracle to detect this and avoid the error.
Does this solve your issue?
I know this isn't exactly what you were asking, but if you are willing to choose a finite number of level's to recurse it isn't too bad to write.
SELECT table_2.id
FROM table LEFT JOIN
(table AS table_1 LEFT JOIN table AS table_2 ON table_1.id = table_2.MgrID)
ON table.id = table_1.MgrID
WHERE (((table.id)=7));
ETC.

retrieving data from two mysql tables where the second table depends on the first

I have two MySql tables:
users(id_user, name, age, gender ).
ways(#id_user,id_way, start, end, date).
What I want is to retrieve all the ways with their corresponding users details.
So my result would be like this:
id_way | start | end | date | id_user | name | age | gender
---------------------------------------------------------------------------
2 | place1 | place2 | 12/06/2013 | 145 | john | 28 | m
Have you tried JOIN?
SELECT ways.id_way, ways.start, ways.end, ways.date, users.*
FROM ways JOIN users USING (id_user)

mysql query returns an empty result set

I have been tasked with a query I am having problems with. Here is the query:
Given a user id and a month, produce a list containing student name, list of files they own (largest to smallest) including total number of files and number of bytes used in a month specified.
Here is what I have so far:
(Select * from htmp_cs368
Join roster_cs368 ON htmp_cs368.userId =
roster_cs368.lastName Where htmp_cs368.userId =
(SELECT lastName FROM roster_cs368 WHERE userId = 'userId' AND htmp_cs368.monthIn = 'monthIn'))
UNION
(Select * from atmp_cs368
JOIN roster_cs368 ON atmp_cs368.userId =
roster_cs368.userId Where roster_cs368.userId =
'userId' AND atmp_cs368.monthIn = 'monthIn') ORDER BY fileSize DESC;
I am getting a result of empty set. My tables are full. I am hoping somone can correct my mistakes.
I have included my schema:
mysql> select * from roster_cs368
-> ;
+--------+-----------+-----------+
| userId | firstName | lastName |
+--------+-----------+-----------+
| apn7cf | Allen | Newton |
| atggg3 | andrew | goebel |
Primary key is userId
mysql> select * from htmp_cs368;
+------------+----------+------------+----------+----------+-------+------+-------+----------------------+
| filePerms | numLinks | userId | idGroup | fileSize | monthIn | day | time | fileName |
+------------+----------+------------+----------+----------+-------+------+-------+----------------------+
| drwx------ | 2 | schulte | faculty | 289 | Nov | 7 | 2011 | Java |
| -rw-r--r-- | 1 | schulte | faculty | 136 | Apr | 29 | 2012 | LD |
| drwxr-xr-x | 3 | schulte | faculty | 177 | Mar | 20 | 2012 | Upgrade |
No primary key here
select * from atmp_cs368;
+------------+----------+--------------+----------+----------+-------+------+-------+-----------------------------+
| filePerms | numLinks | userId | idGroup | fileSize | monthIn | day | time | fileName |
+------------+----------+--------------+----------+----------+-------+------+-------+-----------------------------+
| drwxr-xr-x | 2 | remierm | 203 | 245 | Sep | 17 | 14:40 | 148360_sun_studio_12 |
| drwx---rwx | 31 | antognolij | sasl | 2315 | Oct | 24 | 12:28 | 275 |
| -rwx------ | 1 | kyzvdb | student | 36 | Sep | 19 | 13:05 | 275hh |
No primary key here as either.
I have had very little experience with mysql. I also have to come up with:
If no user id is specified, all files, if no month specified, all users and if neither specified, all months and users.
I am stuck and at a lost. I appreciate any help! Thanks!
You seem to have a number of problems in the SQL.
First
Join roster_cs368 ON htmp_cs368.userId = roster_cs368.lastName
You try to join the userId field to the lastName field, which definitely won't work. It should be userId in both tables.
Then
WHERE userId = 'userId' AND htmp_cs368.monthIn = 'monthIn'
Assuming those really are literal strings, they won't match anything in the table. You need to use a parameterized query, and substitute question marks in the SQL, as in
WHERE userId = ? AND htmp_cs368.monthIn = ?
and provide the actual values to be used in the Java code.
I think you're looking for something along these lines (untested, but this will give you a starting point)
List of files
select r.lastName, r.firstName, t.fileName, t.fileSize
from htmp_cs368 t join roster_cs368 r on t.userId=r.userId
where t.userId=? and t.monthIn=?
order by fileSize desc
Summary:
select r.lastName, r.firstName, count(t.fileName), sum(t.fileSize)
from htmp_cs368 t join roster_cs368 r on t.userId=r.userId
where t.userId=? and t.monthIn=?
group by t.userId
This is a simple approach that does not take into account files appearing and disappearing during a month, but you don't seem to have data in your tables for this.
Also, it's not clear what atmp_cs368 is for, or why the time column in one table seems to have year values.
As pointed out by others you seem to have a number of problems in your SQL. I dont think it can compile as well.
Try:
SELECT r.userId, files.*
FROM roster_cs368 AS r
JOIN (
Select * from htmp_cs368 WHERE userId = 'userId' AND monthIn = 'monthIn'
UNION
Select * from atmp_cs368 Where userId = 'userId' AND monthIn = 'monthIn'
) AS files ON files.userId = r.userId
ORDER BY files.fileSize DESC;
You need only one JOIN. This lists users and all their files. And take care to equate apples to apples (userId != lastName).
Now to get count of files and file sizes etc you need a GroupBy effectively. But you cannot list files and get count of files together "easily". It will have to be one way or other. Just for the count you can use Jim's solution.
This JOIN looks a tad suspicious...
JOIN roster_cs368 ON htmp_cs368.userId = roster_cs368.lastName
Even if userId in htmp_cs368 has an equivalent value in the lastName column of roster_cs368, this is very bad form. JOINS should typically be done on like-named columns.
If these two columns are unrelated (it's hard to tell when roster_cs368 also has a userId column), then that would be at least part of your problem.
Also, htmp_cs368.monthIn = 'monthIn' doesn't make sense. This won't match anything in that column either.