Getting latest entry from multiple groups in Mysql - mysql

Apologies if the question title is not clear. Don't really know how to phrase the following in one line.
I hope I mange to explain what I want to achieve. This is wrecking me head.
Currently I have a table which keeps a record for the training status of employees for different projects. Structure is like this:
--------------------------------------------------------------
|id|timestamp |employee |project |status |
--------------------------------------------------------------
|1 |2015-10-01 00:00:00 | john doe | project_1 | In Training |
|2 |2015-10-04 00:00:00 | jane doe | project_1 | In Training |
|3 |2015-11-01 00:00:00 | john doe | project_1 | Live |
|4 |2015-10-08 00:00:00 | john doe | project_2 | In Training |
|5 |2015-11-01 00:00:00 | jane doe | project_2 | In Training |
|6 |2015-11-09 00:00:00 | jane doe | project_3 | Live |
Now management requested a table / view which gives an overview about the latest status per employee. So I'm looking for a query which returns this:
-----------------------------------------------------
| employee | project_1 | project_2 | project_3 |
-----------------------------------------------------
| John Doe | Live | In Training | |
| Jane Doe | In Training | In Training | Live |
So the query should look for the latest entry per employee and project and return the status of that project.
Is there any way this is doable?

Well, you're conflating 2 problems into 1. The first problem is how to get the latest status per employee per project. The second is how to show the data in columns. The first problem is more interesting, so that's the one I'll answer.
SELECT DISTINCT x.employee
, y.project
, z.timestamp
, z.status
FROM my_table x
JOIN my_table y
LEFT
JOIN
( SELECT a.*
FROM my_table a
JOIN
( SELECT employee
, project
, MAX(timestamp) max_timestamp
FROM my_table
GROUP
BY employee
, project
) b
ON b.employee = a.employee
AND b.project = a.project
AND b.max_timestamp = a.timestamp
) z
ON z.employee = x.employee
AND z.project = y.project;

Related

MYSQL selecting new column based on multiple joins

I'm still working through some kinks with MySQL so any help will be appreciated.
I have 3 tables -- equipment, states, zones.
equipment:
+---------------+------+------------+
| current_state | id | ...columns |
+---------------+------+------------+
states:
+----------+-------------+
| state | zone_id |
+----------+-------------+
zones:
+-----+------+
| id | zone |
+-----+------+
In equipment, there is one current_state per row.
In states, there is one zone_id per row.
In zones, there is one zone per row.
I would like to JOIN the three tables as a subquery select statement (not even sure if that's a thing) and have the output return as 1 alias'd column among the other columns I'm selecting
+--------------+-------------+
| current_zone | ....columns |
+--------------+-------------+
A sample expected output is:
+------------+-------------+--------+------------------+--------------+---------+
| c_id | g_id | e_id | equipment_type | impressionId | email |
+------------+-------------+--------+------------------+--------------+---------+
| 1234 | ABC1234 | 0001 | VEST | 2032 |ab#yc.com|
| 1234 | 1234ABC | 0001 | SHIRT | 4372 |ab#yc.com|
| 1234 | DCBA123 | 0001 | CAN | 4372 |ab#yc.com|
| 1234 | DCBA321 | 0001 | JACKET | ab#yc.com |ab#yw.com|
| 4567 | abc321d | 0002 | SHIRT | 2032 |db#yw.com|
| 4567 | cba123d | 0002 | CAN | 4372 |db#yw.com|
| 4567 | def4rg4 | 0002 | JEANS | 3210 |db#yw.com|
+------------+-------------+--------+------------------+--------------+---------+
The current query has multiple joins already referring to the zones and states table in order to determine a different value:
SELECT equipment.*,
...
FROM equipment
LEFT JOIN c on equipment.c_id = c.id
LEFT JOIN g on equipment.g_id = g.id
LEFT JOIN states on g.state = states.state
LEFT JOIN zones on zones.id = states.zone_id
Essentially, what I want to do is create a subquery in order to create a new column based on the results of the three joins, something like this:
SELECT equipment.*,
(SELECT
equipment.current_state
FROM equipment
LEFT JOIN equipment.current_state = states.state
LEFT JOIN zones.id = states.zone_id
) as current_zone,
...
This is even possible? Am I trying to select a new column in the wrong place?
Thanks to #TheImpaler I was able to clear up my Scalar Subquery. In my eyes, I thought I had to create another join based on the properties I wanted when in reality all I had to do was create a conditional scalar subquery:
SELECT equipment.*,
(SELECT zones.zone
FROM zones
WHERE equipment.current_state = states.state
AND zones.id = states.zone_id
) as current_zone,
...

How to count unique items in column in Access

I need help. I can't seem to find the logic behind this code.
I am working on a voting system, and I need to output the results of the votes.
I want to count all of the rows that has a unique name in it and output how many.
My table goes like this.
voterid | pres | vpres | sec | trea | PIO
---------------------------------------------
1 | John | Mitch | James | Jack | Eman
2 | John | Pao | Bryan | Jack | Faye
3 | Kelvin | Pao | James | Jeck | Faye
Output should be
Pres | Votes
--------------
John | 2
Kelvin | 1
Here's my code.
SELECT DISTINCT
pres,
(SELECT COUNT(pres) FROM (SELECT DISTINCT pres FROM tblVote AS Votes)) AS Votes
FROM tblVote
Thanks in advance!
I think you are just looking for a simple GROUP BY query:
SELECT pres, COUNT(*) AS Votes
FROM tblVote
GROUP BY pres

My sql listing entries which are repeated more than certain value

I have a staff table like this --->
+------+------------------+------+------------+--------+
| EC | Name | Code | Dob | Salary |
+------+------------------+------+------------+--------+
| 2001 | ROBBIE KEANE | VSS1 | 1990-05-16 | 18000 |
| 2002 | ANSUMAN BANERJEE | VSS1 | 1985-05-21 | 18000 |
| 2003 | OMAR GONZALEZ | SACP | 1989-04-16 | 20000 |
| 2004 | ALAN GORDON | IALO | 1989-05-03 | 20000 |
| 2005 | ROBBIE KEANE | IALO | 1988-01-16 | 18000 |
| 2006 | CHANDLER HOFFMAN | BBDP | 1988-07-17 | 22000 |
| 2007 | PAUL POGBA | BHSM | 1990-08-16 | 18000 |
| 2008 | SHINJI KAGAWA | LPDC | 1991-01-20 | 18000 |
+------+------------------+------+------------+--------+
And now i want to list those codes (like VSS1), which have less than specified number of people assigned with them(say like less than 2) , how can i do this please help.
My query up till now is-->
SELECT Code,count(*) as 'Number of Staff' from STAFF where Code IN (SELECT Code from STAFF GROUP BY CODE LIMIT 2);
But this is not working.
You can filter row count for each Code group with the HAVING clause :
SELECT Code
, COUNT(*)
FROM STAFF
GROUP BY Code
HAVING COUNT(*) < 2
If you need to know the names of the people having this count less than 2 then...
SELECT S.EC, S.Name, S.Code, S.DOB, S.Salary, SC.Code, SC.Cnt
FROM STAFF S
INNER JOIN (SELECT Count(*) cnt, Code FROM STAFF GROUP BY CODE) SC
on S.Code = SC.code
WHERE SC.CNT < 2
should work in SQL server and mySQL. Though SQL Sever could also use a windowed set which would be faster.
If however, you just need to know the Codes having less than a certain number, notulysses having clause should fit the bill.

MYSQL - Show sum of total work_time but only display results from last updated date_time

Sorry for the confusing Title. I have been struggling with this query for quite a long time and I will do my best to explain what I am looking for.
I have 3 tables I am trying to pull from. We will call them:
headers
details
users
The headers table contains two important fields:
ref_num
headers_uid
The details table has the following important rows:
details_uid
headers_uid
work_time
user_uid
disposition
date_time
The users table has the following:
user_uid
username
An example of the details table which contains the majority of the information I need is as follows:
details_uid | headers_uid | work_time | user_uid | disposition | date_time
1 | 10 | 25:00 | 5 | o | 2013-07-02 12:14:48
2 | 10 | 10:00 | 7 | p | 2013-07-02 13:55:37
3 | 10 | 5:00 | 5 | c | 2013-07-02 15:04:28
4 | 12 | 7:00 | 5 | o | 2013-07-02 15:20:21
5 | 12 | 12:00 | 7 | p | 2013-07-02 15:35:27
6 | 12 | 3:00 | 7 | c | 2013-07-02 15:40:19
What I'm trying to do is display the headers.refnum, sum of total work_time for the unique user for ALL details.details_uids with the same details.headers_uid and only the LAST disposition of the details.headers_uid for the each user. The results must look for a specific date_time (I generally search by > CURDATE() to grab events for today) Also, instead of displaying the user_uid, I will be searching within a WHERE clause by users.username (I have usernames stored in a txt file which is turned into an IN statement).
Ideally, this is what I would like to see:
ref_num | work_time | username | disposition |
A10 | 30:00 | mike | c |
A10 | 10:00 | james | p |
A12 | 7:00 | mike | o |
A12 | 15:00 | james | c |
Any help is greatly appreciated! I know this will probably involve a good deal of join statements and subqueries and I've been banging my head on the table trying to get it right. I know this would be much easier using php, but sadly, I don't have php access at work yet (don't ask..)
I think this does what you want:
select h.ref_num, sum(d.work_time), u.username, d.disposition
from details d join
headers h
on d.headers_uid = h.headers_uid join
users u
on d.user_uid = u.user_uid
where d.disposition = (select disposition
from details d2
where d2.headers_uid = d.headers_uid and
d2.users_uid = d.users_uid
order by date_time desc
limit 1
)
group by h.ref_num, u.username, d.disposition;
The key is the where clause that selects the last disposition for a given set of details records.

Tricky SQL query involving consecutive values

I need to perform a relatively easy to explain but (given my somewhat limited skills) hard to write SQL query.
Assume we have a table similar to this one:
exam_no | name | surname | result | date
---------+------+---------+--------+------------
1 | John | Doe | PASS | 2012-01-01
1 | Ryan | Smith | FAIL | 2012-01-02 <--
1 | Ann | Evans | PASS | 2012-01-03
1 | Mary | Lee | FAIL | 2012-01-04
... | ... | ... | ... | ...
2 | John | Doe | FAIL | 2012-02-01 <--
2 | Ryan | Smith | FAIL | 2012-02-02
2 | Ann | Evans | FAIL | 2012-02-03
2 | Mary | Lee | PASS | 2012-02-04
... | ... | ... | ... | ...
3 | John | Doe | FAIL | 2012-03-01
3 | Ryan | Smith | FAIL | 2012-03-02
3 | Ann | Evans | PASS | 2012-03-03
3 | Mary | Lee | FAIL | 2012-03-04 <--
Note that exam_no and date aren't necessarily related as one might expect from the kind of example I chose.
Now, the query that I need to do is as follows:
From the latest exam (exam_no = 3) find all the students that have failed (John Doe, Ryan Smith and Mary Lee).
For each of these students find the date of the first of the batch of consecutively failing exams. Another way to put it would be: for each of these students find the date of the first failing exam that comes after their last passing exam. (Look at the arrows in the table).
The resulting table should be something like this:
name | surname | date_since_failing
------+---------+--------------------
John | Doe | 2012-02-01
Ryan | Smith | 2012-01-02
Mary | Lee | 2012-03-04
How can I perform such a query?
Thank you for your time.
You can take advantage of the fact that if someone passed the most recent exam, then they have not failed any exams since their most recent pass: therefore the problem reduces to finding the first exam failed since the most recent pass:
SELECT name, surname, MIN(date) date_since_fail
FROM results NATURAL LEFT JOIN (
SELECT name, surname, MAX(date) lastpass
FROM results
WHERE result = 'PASS'
GROUP BY name, surname
) t
WHERE result = 'FAIL' AND date > IFNULL(lastpass,0)
GROUP BY name, surname
See it on sqlfiddle.
I should use a subquery that fetch last passed exam,
somthing like:
SET #query_exam_no = 3;
SELECT
name,
surname,
MIN(IF(date > last_passed_exam, date, NULL)) AS date_failing_since
FROM
exam_results
LEFT JOIN (
SELECT
name,
surname,
MAX(date) AS last_passed_exam
FROM exam_results
WHERE result = 'PASS'
GROUP BY name, surname
) AS last_passed_exams USING (name, surname)
HAVING
MAX(IF(exam_no = #query_exam_no, result, NULL)) = 'FAIL'
GROUP BY name, surname
This is enough:
select t.name,
t.surname,
t.date as 'date_since_failing'
from tablename t
inner join
(
select name,
surname,
max(exam_no) as exam_no
from tablename
group by name, surname
having min(result) = 'FAIL'
) aux on t.name = aux.name and t.surname = aux.surname and t.exam_no = aux.exam_no
The condition you are asking for is good for nothing you can do it without it. Here is the working example.
select
e.name,
e.sur_name,
min(e.date) as `LastFailed`
from exams as e
where e.result = 'Fail'
group by e.name
order by e.name
This produces this result
name sur_name LastFailed
Ann Evans 2012-02-03
John Doe 2012-02-01
Mary Lee 2012-01-04
Ryan Smith 2012-01-02