i have table structure like this
'encounterID' , 'dateAndTime'
1234 2016-02-12 17:57:57
1234 2016-02-12 17:58:59
1234 2016-02-12 17:59:05
12345 2016-02-12 17:57:57
12345 2016-02-12 17:58:59
12345 2016-02-12 17:59:05
i want to find second latest entry for every encounterId?
Anyone Please help
You can use variables to select top-n-per-group records:
SELECT encounterID, dateAndTime
FROM (
SELECT encounterID, dateAndTime,
#rn := IF(#eID = encounterID, #rn + 1,
IF(#eID := encounterID, 1, 1)) AS rn
FROM mytable
CROSS JOIN (SELECT #rn := 0, #eID := 0) AS vars
ORDER BY encounterID, dateAndTime DESC) AS t
WHERE t.rn = 2
The outer query selects the second latest record per encounterID group.
Demo here
Related
I have a table
Id Name Id_collection Price
1 good1 2 10
2 good2 1 101
3 good3 3 102
4 good4 2 10
5 good5 2 10
I need to Group By id_collection, but i need to show 2 rows (to be able to change this value via variable or ... Ex: to change to 3 or 4 )
not
1
2
3
Ex
1
1
2
2
3
3
or
Ex
1
1
1
2
2
2
3
3
3
so the result must be
Id Name Id_collection Price
1 good1 2 10
4 good4 2 10
2 good2 1 101
3 good3 3 102
I was thinking about procedure or loop, but i didn't that before, Please help!!!
You want to group by adjacent values . . . in MySQL. You can use variables to assign the group. Alternatively, you can use this method to assign the group: count the number of rows that have id_collection different from each row with a smaller id.
You don't specify how to calculate the other columns, but here is a guess:
select min(id) as id, min(name) as name, id_collection, avg(price) as price
from (select t.*,
(select count(*)
from t t2
where t2.id_collection <> t.id_collection and
t2.id < t.id
) as grp
from t
) t
group by id_collection, grp;
EDIT:
I just realized that you probably don't want to aggregate the results; you probably just want the first row. For that, use variables:
select t.*
from (select t.*,
(#rn := if(#id = id_collection, #rn,
if(#id := id_collection, #rn + 1, #rn + 1)
)
) as rn
from t cross join
(select #id = -1, #rn := 0) params
order by id
) t
where rn = 1;
I edited a little the answer which #Gordon_Linoff posted, and it works now, you just have to change rn <= 3 this number and will get the various results.
#Gordon_Linoff - Thank you, this really helped me
select t.*
from (select t.*,
(#rn := if(#id = id_collection, #rn + 1,
if(#id := id_collection, 1, 0)
)
) as rn
from t cross join
(select #id := -1, #rn := 0) params
order by id_collection
) t
where rn <= 3;
I have a below table and wants to select only last 2 entries of all users.
Source table:
-------------------------------------
UserId | QuizId(AID)|quizendtime(AID)|
--------------------------------------
1 10 2016-5-12
2 10 2016-5-12
1 11 2016-6-12
2 12 2016-8-12
3 12 2016-8-12
2 13 2016-8-12
1 14 2016-9-12
3 14 2016-9-12
3 11 2016-6-12
Expected output is like, (should list only recent 2 quizid entries for all users)
-------------------------------------
UserId | QuizId(AID)|quizendtime(AID)|
--------------------------------------
1 14 2016-9-12
1 11 2016-6-12
2 13 2016-8-12
2 12 2016-8-12
3 14 2016-9-12
3 12 2016-8-12
Any idea's to produce this output.
Using MySQL user defined variables you can accomplish this:
SELECT
t.UserId,
t.`QuizId(AID)`,
t.`quizendtime(AID)`
FROM
(
SELECT
*,
IF(#sameUser = UserId, #a := #a + 1 , #a := 1) row_number,
#sameUser := UserId
FROM your_table
CROSS JOIN (SELECT #a := 1, #sameUser := 0) var
ORDER BY UserId , `quizendtime(AID)` DESC
) AS t
WHERE t.row_number <= 2
Working Demo
Note: If you want at most x number of entries for each user then change the condition in where clause like below:
WHERE t.row_number <= x
Explanation:
SELECT
*,
IF(#sameUser = UserId, #a := #a + 1 , #a := 1) row_number,
#sameUser := UserId
FROM your_table
CROSS JOIN (SELECT #a := 1, #sameUser := 0) var
ORDER BY UserId , `quizendtime(AID)` DESC;
This query sorts all the data in ascending order of userId and descending order of quizendtime(AID).
Now take a walk on this (multi) sorted data.
Every time you see a new userId assign a row_number (1). If you see the same user again then just increase the row_number.
Finally filtering only those records which are having row_number <= 2 ensures the at most two latest entries for each user.
EDIT: As Gordon pointed out that the evaluation of expressions using user defined variables in mysql is not guaranteed to follow the same order always so based on that the above query is slightly modified:
SELECT
t.UserId,
t.`QuizId(AID)`,
t.`quizendtime(AID)`
FROM
(
SELECT
*,
IF (
#sameUser = UserId,
#a := #a + 1,
IF(#sameUser := UserId, #a := 1, #a:= 1)
)AS row_number
FROM your_table
CROSS JOIN (SELECT #a := 1, #sameUser := 0) var
ORDER BY UserId , `quizendtime(AID)` DESC
) AS t
WHERE t.row_number <= 2;
WORKING DEMO V2
User-defined variables are the key to the solution. But, it is very important to have all the variable assignments in a single expression. MySQL does not guarantee the order of evaluation of expressions in a select -- and, in fact, sometimes processes them in different orders.
select t.*
from (select t.*,
(#rn := if(#u = UserId, #rn + 1,
if(#u := UserId, 1, 1)
)
) as rn
from t cross join
(select #u := -1, #rn := 0) params
order by UserId, quizendtime desc
) t
where rn <= 2;
I have this following table:
name value year
A 1 2015
A 2 2014
A 3 2013
B 1 2015
B 3 2013
C 1 2015
C 2 2014
How can I get, for each name, the row with the second highest year, like this:
name value year
A 2 2014
B 3 2013
C 2 2014
I tried the following query but no success:
select name, value, year
from TABLE_NAME
WHERE year IN (select year from TABLE_NAME order by year desc limit 1,1)
The previous query gives me this error:
"SQL Error (1235): This version of MySQL doesn't yet support 'LIMIT & IN/ALL/ANY/SOME subquery' "
And I can't change the MySQL version (5.6.25) right now, because the solution is already in production.
Any help, please?
One way to solve n per group in MySQL is to simulate ROW_NUMBER. Note that this will only return one value per name.
SELECT
name,
value,
year
FROM
(SELECT
t.name,
t.value,
t.year,
#rn := if(#prev = t.name, #rn + 1,1) as rn,
#prev:=t.name
FROM
test_table as t
JOIN (SELECT #Prev:= Null, #Rn := 0) as v
ORDER BY
t.name,
T.year desc) as t
WHERE
rn = 2;
How this works.
SELECT #Prev:= Null, #Rn := 0 initializes two variables #Prev and #Rn.
#rn := if(#prev = t.name, #rn + 1,1) as rn set the variable of #rn to either 1 or #rn + 1 depending on if #prev = t.Name and returns the value of #rn as the column rn
#prev:=t.name sets the value of #prev equal to the current value of name
if you run
SELECT
t.name,
t.value,
t.year,
#prev = t.name as eval,
#rn := if(#prev = t.name, #rn + 1,1) as rn,
#prev:=t.name as prev
FROM
test_table as t
JOIN (SELECT #Prev:= Null, #Rn := 0) as v
ORDER BY
t.name,
T.year desc
I would expect something like
name value year eval rn prev
A 1 2015 false 1 null
A 2 2014 true 2 A
A 3 2013 true 3 A
B 1 2015 false 1 A
B 3 2013 true 2 B
C 1 2015 false 1 B
C 2 2014 true 2 C
Wrapping into a subquery and the filtering for rn=2 gives you the desired result
My strategy is to use a grouping to find the highest years. Then join with the original table to remove the highest years. Finally do a grouping on the combined table to find the second highest year for each name. (If you need value you can do an INNER JOIN with the original table to find it.)
SELECT name, MAX(year)
FROM
(SELECT name, year
FROM TABLE_NAME) AS x1
INNER JOIN
(SELECT name, MAX(year) AS year
FROM TABLE_NAME
GROUP BY name, year
) AS x2
ON x1.name = x2.name AND x1.year <> x2.year
GROUP BY name
ORDER BY name ASC ;
Try this :
select * from test_table where year = (select distinct year from test_table order by year desc limit 1,1)
It should work if you rewrite it as a join:
select b.name, b.value, b.year
from (select year
from table_name
order by year desc
limit 1, 1) a
join table_name b
on b.year = a.year
MySQL server 5.6.20 (latest version at the moment)
Given a price by date table. I added a new column "Rank", which represent the ranking to the item price by date.
Date Item Price Rank
1/1/2014 A 5.01 0
1/1/2014 B 31 0
1/1/2014 C 1.5 0
1/2/2014 A 5.11 0
1/2/2014 B 20 0
1/2/2014 C 5.5 0
1/3/2014 A 30 0
1/3/2014 B 11.01 0
1/3/2014 C 22 0
How do I write a SQL statement to calculate the ranking and update the original table? Below is the expected table with ranking filled in. The ranking calculation is grouped by date (1/1, 1/2, 1/3, etc).
Date Item Price Rank
1/1/2014 A 5.01 2
1/1/2014 B 31 1
1/1/2014 C 1.5 3
1/2/2014 A 5.11 3
1/2/2014 B 20 1
1/2/2014 C 5.5 2
1/3/2014 A 30 1
1/3/2014 B 11.01 3
1/3/2014 C 22 2
Also, if the price is the same for several items, how would MySQL handle the ranking? For example:
Date Item Price Rank
1/4/2014 A 31 0
1/4/2014 B 31 0
1/4/2014 C 1.5 0
Thanks.
You can get the rank in a query using varibles:
select t.*,
(#rn := if(#d = date, #rn + 1,
if(#d := date, 1, 1)
)
) as rank
from pricebydate t cross join
(select #d := NULL, #rn := 0) vars
order by date, price desc;
You can put this in an update using a join:
update pricebydate pbd join
(select t.*,
(#rn := if(#d = date, #rn + 1,
if(#d := date, 1, 1)
)
) as rank
from pricebydate t cross join
(select #d := NULL, #rn := 0) vars
order by date, price desc
) r
on pbd.date = r.date and pbd.item = item
set pbd.rank = r.rank;
I believe this will do exactly what you want:
Update YourTable As T1
Set ItemRank = (
Select ItemRank From (
Select Rank() Over (Partition By ItemDate Order By Price Desc)
As ItemRank, Item, ItemDate
From YourTable
) As T2
Where T2.Item = T1.Item
And T2.ItemDate = T1.ItemDate
)
Duplicate Ranks would be handled as having equal ranks.
I have a table that contains categories, dates and rates. Each category can have different rates for different dates, one category can have only one rate at a given date.
Id CatId Date Rate
------ ------ ------------ ---------
000001 12 2009-07-07 1
000002 12 2009-07-08 1
000003 12 2009-07-09 1
000004 12 2009-07-10 2
000005 12 2009-07-15 1
000006 12 2009-07-16 1
000007 13 2009-07-08 1
000008 13 2009-07-09 1
000009 14 2009-07-07 2
000010 14 2009-07-08 1
000010 14 2009-07-10 1
Unique index (catid, Date, Rate)
I would like for each category to group all continuous dates ranges and keep only the begin and the end of the range.
For the previous example, we would have:
CatId Begin End Rate
------ ------------ ------------ ---------
12 2009-07-07 2009-07-09 1
12 2009-07-10 2009-07-10 2
12 2009-07-15 2009-07-16 1
13 2009-07-08 2009-07-09 1
14 2009-07-07 2009-07-07 2
14 2009-07-08 2009-07-08 1
14 2009-07-10 2009-07-10 1
I found a similar solution in the forum which did not exactly give the result
WITH q AS
(
SELECT *,
ROW_NUMBER() OVER (PARTITION BY CatId, Rate ORDER BY [Date]) AS rnd,
ROW_NUMBER() OVER (PARTITION BY CatId ORDER BY [Date]) AS rn
FROM my_table
)
SELECT CatId AS catidd, MIN([Date]) as beginn, MAX([Date])as endd, Rate
FROM q
GROUP BY CatId, rnd - rn, Rate
SEE SQL FIDDLE
How can I do the same thing in mysql?
Please help!
MySQL doesn't support analytic functions, but you can emulate such behaviour with user-defined variables:
SELECT CatID, Begin, MAX(Date) AS End, Rate
FROM (
SELECT my_table.*,
#f:=CONVERT(
IF(#c<=>CatId AND #r<=>Rate AND DATEDIFF(Date, #d)=1, #f, Date), DATE
) AS Begin,
#c:=CatId, #d:=Date, #r:=Rate
FROM my_table JOIN (SELECT #c:=NULL) AS init
ORDER BY CatId, Rate, Date
) AS t
GROUP BY CatID, Begin, Rate
See it on sqlfiddle.
SELECT catid,min(ddate),max(ddate),rate
FROM (
SELECT
Catid,
Ddate,
rate,
#rn := CASE WHEN (#prev <> rate
or DATEDIFF(ddate, #prev_date)>1) THEN #rn+1 ELSE #rn END AS rn,
#prev := rate,
#prev_id := catid ,
#prev_date :=ddate
FROM (
SELECT CatID,Ddate,rate
FROM rankdate
ORDER BY CatID, Ddate ) AS a ,
(SELECT #prev := -1, #rn := 0, #prev_id:=0 ,#prev_date:=-1) AS vars
) T1 group by catid,rn
Note: The line (SELECT #prev := -1, #rn := 0, #prev_id:=0 ,#prev_date:=-1) AS vars is not necessary in Mysql Workspace, but it is in the PHP mysql_query function.
SQL FIDDLE HERE
I know I am late, still posting a solution that worked for me.
Had the same issue, here's how I got it
Found a good solution using variables
SELECT MIN(id) AS id, MIN(date) AS date, MIN(state) AS state, COUNT(*) cnt
FROM (
SELECT #r := #r + (#state != state OR #state IS NULL) AS gn,
#state := state AS sn,
s.id, s.date, s.state
FROM (
SELECT #r := 0,
#state := NULL
) vars,
t_range s
ORDER BY
date, state
) q
GROUP BY gn
More details at : https://explainextended.com/2009/07/24/mysql-grouping-continuous-ranges/