Fetching Latest Record from MySQL for each individual - mysql

I have a table like this
╔════╦════════╦═════════════╦═════════════════╗
║ PK ║ NAME ║ DEGREE ║ YEAR_OF_PASSING ║
╠════╬════════╬═════════════╬═════════════════╣
║ 1 ║ Shrey ║ B.E. ║ 2004 ║
║ 2 ║ Shrey ║ High School ║ 2000 ║
║ 3 ║ Gaurav ║ B.E. ║ 2000 ║
║ 4 ║ Gaurav ║ M.Sc. ║ 2002 ║
╚════╩════════╩═════════════╩═════════════════╝
How do I query to get a resultset of latest degree of each person as shown below?
╔════╦════════╦════════╦═════════════════╗
║ PK ║ NAME ║ DEGREE ║ YEAR_OF_PASSING ║
╠════╬════════╬════════╬═════════════════╣
║ 1 ║ Shrey ║ B.E. ║ 2004 ║
║ 4 ║ Gaurav ║ M.Sc. ║ 2002 ║
╚════╩════════╩════════╩═════════════════╝

SELECT a.*
FROM tableName a
INNER JOIN
(
SELECT Name, MAX(Year_Of_Passing) max_val
FROM tableName
GROUP BY Name
) b ON a.name = b.name AND
a.Year_Of_Passing = b.max_val
SQLFiddle Demo
UPDATE 1
SELECT a.*
FROM tableName a
INNER JOIN
(
SELECT Name, MAX(Year_Of_Passing) max_val, MAX(PK) max_pk
FROM tableName
GROUP BY Name
) b ON a.name = b.name AND
CASE WHEN b.max_val IS NULL
THEN a.pk = max_PK
ELSE a.Year_Of_Passing = b.max_val
END
SQLFiddle Demo

Related

Sort by value based on another column value with self joins

I have asked this question before, but now need to add more fields and the same method is now timing out because of too many self joins.
This is my current query. It is slow, but performant enough when I add the city and state columns, but when I add the zip column it times out. Is there another method of doing this?
SELECT um.user_id,
users.display_name,
GROUP_CONCAT(um.meta_value SEPARATOR ' '),
city.meta_value,
state.meta_value,
zip.meta_value
FROM `wp_7bv88z_usermeta` um,
`wp_7bv88z_pmpro_memberships_users` u,
`wp_7bv88z_users` users,
`wp_7bv88z_usermeta` lname,
`wp_7bv88z_usermeta` fname,
`wp_7bv88z_usermeta` city,
`wp_7bv88z_usermeta` state,
`wp_7bv88z_usermeta` zip
WHERE u.user_id = um.user_id
and users.id = u.user_id
and (lname.user_id = u.user_id and lname.meta_key = 'pmpro_slastname')
and (fname.user_id = u.user_id and fname.meta_key = 'pmpro_sfirstname')
and (city.user_id = u.user_id and city.meta_key = 'pmpro_scity')
and (state.user_id = u.user_id and state.meta_key = 'pmpro_sstate')
and (zip.user_id = u.user_id and zip.meta_key = 'pmpro_szipcode')
and (um.meta_key='pmpro_sfirstname' or um.meta_key='pmpro_slastname')
and u.status='active'
GROUP BY lname.meta_value, fname.meta_value, city.meta_value,
state.meta_value, zip.meta_value, um.user_id
Sample Data:
user_meta table
╔════=====╦══════════════==╦══════=======╗
║ user_id ║ meta_key ║ meta_value ║
╠════=====╬══════════════==╬══════=======╣
║ 1 ║ pmpro_sfirstname ║ Jeff ║
║ 1 ║ pmpro_slasttname ║ Atwood ║
║ 1 ║ pmpro_scity ║ Chicago ║
║ 1 ║ pmpro_sstate ║ IL ║
║ 1 ║ pmpro_szip ║ 60007 ║
║ 2 ║ pmpro_sfirstname ║ Geoff ║
║ 2 ║ pmpro_slasttname ║ Davis ║
║ 2 ║ pmpro_scity ║ Fairfax ║
║ 2 ║ pmpro_sstate ║ VA ║
║ 2 ║ pmpro_szip ║ 22030 ║
║ 3 ║ pmpro_sfirstname ║ Jarrod ║
║ 3 ║ pmpro_slasttname ║ Minor ║
║ 3 ║ pmpro_scity ║ Dallas ║
║ 3 ║ pmpro_sstate ║ TX ║
║ 3 ║ pmpro_szip ║ 75001 ║
║ 4 ║ pmpro_sfirstname ║ Joel ║
║ 4 ║ pmpro_slasttname ║ Brown ║
║ 4 ║ pmpro_scity ║ Boston ║
║ 4 ║ pmpro_sstate ║ MA ║
║ 4 ║ pmpro_szip ║ 02101 ║
╚════=====╩════════════=====╩══════======╝
Expected Results:
1, Jeff Atwood, Chicago, IL, 60007
4, Joel Brown, Fairfax, VA, 22030
2, Geoff Davis, Dallas, TX, 75001
3, Jarrod Minor, Boston, MA, 02101
You can use aggregation to pick values from EAV data stucture
select user_id,
max( case when meta_key = 'pmpro_sfirstname' then meta_value else null end) fname,
max( case when meta_key = 'pmpro_slasttname' then meta_value else null end) lname,
max( case when meta_key = 'pmpro_scity' then meta_value else null end) city,
max( case when meta_key = 'pmpro_sstate' then meta_value else null end) state,
max( case when meta_key = 'pmpro_szip' then meta_value else null end) zip
from user_meta
group by user_id
Demo

Path from a group

I have the following data:
╔════╦═══════╦═══════╗
║ id ║ group ║ place ║
╠════╬═══════╬═══════╣
║ 1 ║ 1 ║ a ║
║ 2 ║ 1 ║ b ║
║ 3 ║ 1 ║ b ║
║ 4 ║ 1 ║ a ║
║ 5 ║ 1 ║ c ║
║ 6 ║ 2 ║ a ║
║ 7 ║ 2 ║ b ║
║ 8 ║ 2 ║ c ║
╚════╩═══════╩═══════╝
How can I get the path of each group in MySQL?
The expected result is:
╔═══════╦════════════╗
║ group ║ path ║
╠═══════╬════════════╣
║ 1 ║ a-b-a-c ║
║ 2 ║ a-b-c ║
╚═══════╩════════════╝
Assuming that the end goal is to sort by group and id, and then simplify each group's sequence so that consecutive repeated places are only shown once:
Start by determining, for each row, whether the place or the group have changed since the previous row. There's a good solution to this problem in this answer.
Then use GROUP_CONCAT to merge the places together into a path.
Be aware that GROUP_CONCAT has a user-configurable maximum length, which by default is 1,024 characters.
SELECT
`group`,
GROUP_CONCAT(place ORDER BY id SEPARATOR '-') path
FROM
(SELECT
COALESCE(#place != place OR #group != `group`, 1) changed,
id,
#group:=`group` `group`,
#place:=place place
FROM
place_table, (SELECT #place:=NULL, #group:=NULL) s
ORDER BY `group`, id) t
WHERE
changed = 1
GROUP BY `group`;

SQL: Select ID/Value combinations that do not possess a value in another table

So here's basically the issue (I'm turning this into more of a universal question in case people need something like this in the future).
I have one table ("People") that is basically this
╔══════════╦═══════╗
║ PersonID ║ Letter║
╠══════════╬═══════╣
║ 1 ║ A ║
║ 1 ║ B ║
║ 1 ║ C ║
║ 1 ║ D ║
║ 2 ║ A ║
║ 2 ║ B ║
║ 2 ║ C ║
║ 3 ║ B ║
║ 3 ║ C ║
║ 4 ║ A ║
║ 4 ║ C ║
║ 4 ║ D ║
║ 5 ║ E ║
╚══════════╩═══════╝
And lets say I have another table ("Letters") which can lists all possible "Letters" a person can have.
╔══════════╦══════╗
║ LetterID ║ Text ║
╠══════════╬══════╣
║ 1 ║ A ║
║ 2 ║ B ║
║ 3 ║ C ║
║ 4 ║ D ║
║ 5 ║ E ║
╚══════════╩══════╝
I need to make a new table that will have all persons listed and letters that they DONT have. So for this example, the result would be this
╔══════════╦══════════════╗
║ PersonID ║ LetterNotHad ║
╠══════════╬══════════════╣
║ 1 ║ E ║
║ 2 ║ D ║
║ 2 ║ E ║
║ 3 ║ A ║
║ 3 ║ D ║
║ 3 ║ E ║
║ 4 ║ B ║
║ 4 ║ E ║
║ 5 ║ A ║
║ 5 ║ B ║
║ 5 ║ C ║
║ 5 ║ D ║
╚══════════╩══════════════╝
Any and all help or guidance is greatly appreciated.
Edit: Here's basically what I was trying, something like this
select p.PersonId, l.value
from letters l
left join people p
on l.Text = p.Letter
where p.personid is null
Here is the idea
WITH cte
AS (SELECT *
FROM (SELECT DISTINCT personid
FROM people) B
CROSS JOIN (SELECT DISTINCT Text as letter
FROM letters) A)
SELECT *
FROM cte c
WHERE NOT EXISTS (SELECT 1
FROM first_table f
WHERE c.personid = f.personid
AND c.letter = f.letter)
Note: You need to use letterid in People table instead of Letter and define a foreign key which make the table's consistent
So what you want to do in order to find the missing values is to generate the set that represents all possible values. This is the cartesian product between the two sets (people and letters) and in SQL you use the cross join operator (or unqualified join) to do this.
From this set you want to remove the combinations you already have, and the remainder will be the missing ones.
There are many ways to do this; using a left join it could look like this:
select sub.*
from (
select distinct personid, text
from people
cross join letters
) sub
left join people p on p.letter = sub.text and p.personid = sub.personid
where p.personid is null
Or using the except set operator (for MSSQL (minus in Oracle) - MySQL does not have this):
select personid, text from people cross join letters
except
select personid, letter from people

MySql Query for identifying sequence in a table

I Need help with mysql query to update a new column of the same table based on series of entry and exit dates.
Below is table:
╔════╦═══════════╦═════════════╦═════════════╦════════════════════╗
║ ID ║ PLACE ║ ENTRYDATE ║ EXITDATE ║ LAST_PLACE_VISITED ║
╠════╬═══════════╬═════════════╬═════════════╬════════════════════╣
║ 1 ║ Delhi ║ 1-Jan-2012 ║ 5-Jan-2012 ║ ║
║ 1 ║ Agra ║ 10-Jan-2012 ║ 11-Jan-2012 ║ ║
║ 1 ║ Bangalore ║ 21-Jan-2012 ║ 24-Jan-2012 ║ ║
║ 1 ║ Mumbai ║ 12-Jan-2012 ║ 19-Jan-2012 ║ ║
║ 2 ║ LA ║ 1-Mar-2012 ║ 3-Mar-2012 ║ ║
║ 2 ║ SFO ║ 10-Mar-2012 ║ 14-Mar-2012 ║ ║
║ 2 ║ NY ║ 4-Mar-2012 ║ 9-Mar-2012 ║ ║
║ 3 ║ Delhi ║ 12-Apr-2012 ║ 13-Apr-2012 ║ ║
╚════╩═══════════╩═════════════╩═════════════╩════════════════════╝
The data type of ENTRYDATE and EXITDATE is DATE.
From the above table i need to write a query to update "Last_Place_Visited" column based on entry and exit date of the ID.
Any help with this query would be much appriciated.
Thanks.
Bhargav
Here's a very messy one since MySQL doesn't support window functions,
UPDATE TravelTbl a
INNER JOIN
(
SELECT a.ID,
a.Place,
a.EntryDate,
a.ExitDate,
b.Place Last_Place_Visited
FROM
(
SELECT ID,
Place,
EntryDate,
ExitDate,
Last_Place_Visited,
#grp := if(#ID = ID, #grp ,0) + 1 GRP_RecNo,
#ID := ID
FROM TravelTbl,
(SELECT #ID := '', #grp := 0) vars
ORDER BY EntryDate
) a
LEFT JOIN
(
SELECT ID,
Place,
EntryDate,
ExitDate,
Last_Place_Visited,
#grp2 := if(#ID2 = ID, #grp2 ,0) + 1 GRP_RecNo,
#ID2 := ID
FROM TravelTbl,
(SELECT #ID2 := '', #grp2 := 0) vars
ORDER BY EntryDate
) b ON a.ID = b.ID AND
a.GRP_RecNo = b.GRP_RecNo + 1
) b ON a.ID = b.ID AND
a.Place = b.Place AND
a.EntryDate = b.EntryDate AND
a.ExitDate = b.ExitDate AND
b.Last_Place_Visited IS NOT NULL
SET a.Last_Place_Visited = b.Last_Place_Visited
SQLFiddle Demo
OUTPUT
╔════╦═══════════╦═════════════╦═════════════╦════════════════════╗
║ ID ║ PLACE ║ ENTRYDATE ║ EXITDATE ║ LAST_PLACE_VISITED ║
╠════╬═══════════╬═════════════╬═════════════╬════════════════════╣
║ 1 ║ Delhi ║ 1-Jan-2012 ║ 5-Jan-2012 ║ (null) ║
║ 1 ║ Agra ║ 10-Jan-2012 ║ 11-Jan-2012 ║ Delhi ║
║ 1 ║ Bangalore ║ 21-Jan-2012 ║ 24-Jan-2012 ║ Mumbai ║
║ 1 ║ Mumbai ║ 12-Jan-2012 ║ 19-Jan-2012 ║ Agra ║
║ 2 ║ LA ║ 1-Mar-2012 ║ 3-Mar-2012 ║ (null) ║
║ 2 ║ SFO ║ 10-Mar-2012 ║ 14-Mar-2012 ║ NY ║
║ 2 ║ NY ║ 4-Mar-2012 ║ 9-Mar-2012 ║ LA ║
║ 3 ║ Delhi ║ 12-Apr-2012 ║ 13-Apr-2012 ║ (null) ║
╚════╩═══════════╩═════════════╩═════════════╩════════════════════╝
I tried to modify the table itself:
UPDATE T SET LAST_PLACE_VISITED = (
SELECT t2.PLACE
FROM T t2
WHERE t2.EXITDATE = (
SELECT MAX(t1.EXITDATE)
FROM T t1
WHERE t1.ID = ID
AND t1.EXITDATE < EXITDATE
));
MySQL won't permit this:
You can't specify target table 'T' for update in FROM clause:
But you could work with a view or a temporary table and use this:
UPDATE: Inserted LIMIT 1 for dealing with the case of multiple occurences of the maximum value of EXITDATE for distinct IDs. Disadvantage: We cannot predict which row with maximum value will be taken.
UPDATE 2: Added condition AND t2.ID = t0.ID
SELECT t0.ID, t0.PLACE, t0.ENTRYDATE, t0.EXITDATE, (
SELECT t2.PLACE
FROM T t2
WHERE t2.EXITDATE = (
SELECT MAX(t1.EXITDATE)
FROM T t1
WHERE t1.ID = t0.ID
AND t1.EXITDATE < t0.EXITDATE
)
AND t2.ID = t0.ID
LIMIT 1
) AS LAST_PLACE_VISITED
FROM T t0;
See my SQLFiddle demo

MySQl query Select all rows with same value in limit so that no value is left outside the limit defined

╔════════╦═══════════╦═══════╗
║ MSG_ID ║ RANDOM_ID ║ MSG ║
╠════════╬═══════════╬═══════╣
║ 1 ║ 22 ║ apple ║
║ 2 ║ 22 ║ bag ║
║ 3 ║ 0 ║ cat ║
║ 4 ║ 0 ║ dog ║
║ 5 ║ 0 ║ egg ║
║ 6 ║ 21 ║ fish ║
║ 7 ║ 21 ║ hen ║
║ 8 ║ 20 ║ glass ║
╚════════╩═══════════╩═══════╝
Want to fetch 3 records in a lot such a way that all the data of a particular random_id is picked up .
Result Required:
║ MSG_ID ║ RANDOM_ID ║ MSG ║
╠════════╬═══════════╬═══════╣
║ 1 ║ 22 ║ apple ║
║ 2 ║ 22 ║ bag ║
║ 3 ║ 0 ║ cat ║
Current Result:
║ MSG_ID ║ RANDOM_ID ║ MSG ║
╠════════╬═══════════╬═══════╣
║ 1 ║ 22 ║ apple ║
║ 3 ║ 0 ║ cat ║
║ 4 ║ 0 ║ dog ║
______________________________
Query Used:
SELECT ID,Random_ID, GROUP_CONCAT(message SEPARATOR ' ' ),FLAG,mobile,sender_number,SMStype
FROM messagemaster
WHERE Random_ID > 0
GROUP BY Random_ID
UNION
SELECT ID,Random_ID, message,FLAG,mobile,sender_number,SMStype
FROM messagemaster
WHERE Random_ID = 0
order by random_id LIMIT 100;
I don't want to pick up records using group by.I want to fetch all the records w rt random_ids .Like , if there is a random_id for which there are 3 records and if the query has limit =3 , then i want all the data w r t those random_id to be picked up.
The situation is if I fetch rows with limit 100 , i dont want that some of the data with the random id present in the result set is not picked.
For example if i am picking records limit by 3 then for random id=22 , all records with random id =22 should be picked .
Consider the following...
SELECT b.*
FROM
( SELECT x.*, SUM(y.cnt)
FROM
( SELECT random_id,COUNT(*) cnt FROM messagemaster GROUP BY random_id) x
JOIN
( SELECT random_id,COUNT(*) cnt FROM messagemaster GROUP BY random_id) y
ON y.random_id >= x.random_id
GROUP
BY x.random_id
HAVING SUM(y.cnt) < 4
) a
JOIN messagemaster b
ON b.random_id = a.random_id;