How to get temporal sequence by mysql - mysql

In my table there is an id column, a date column and a status column like this:
ID DATE STATUS
1 0106 A
1 0107 A
1 0112 A
1 0130 B
1 0201 A
2 0102 C
2 0107 C
I want to get a temporal sequence of each ID. Which means if in the neighboring time one id is in the same status, then the former ones will be omitted. The query result is like:
ID DATE STATUS
1 0112 A
1 0130 B
1 0201 A
2 0107 C
How can I realize it by MySQL?

You have to use variable to do this:
select `id`, `date`, `status`
from (
select *, #rowno:=if(#grp = `STATUS`, #rowno + 1 , 1) as rowno, #grp := `STATUS`
from yourtable
cross join (select #grp := null, #rowno := 0) t
order by `id`, `date` desc
) t1
where rowno = 1
order by `id`, `date`
SqlFiddle Demo

Related

Mysql - Create view combined 2 tables but only merged by select without merge tables

In MySQL, I have 2 tables, named table_rebate and table_bonus
table_rebate ( have 3 same columns, 2 different columns )
a_id a_value a_time
1 1000 2018-05-05 10:25:15
2 3000 2018-05-05 11:35:15
table_bonus ( have 3 same columns, 3 different colums )
b_id b_value b_time
01 500 2018-05-05 11:20:15
02 700 2018-05-05 12:30:15
I need to select that 3 same columns into 1 tables to my PHP (CI) views.
Number from Values Time
1 Rebate 1000 2018-05-05 10:25:15
2 Bonus 500 2018-05-05 11:20:15
3 Bonus 700 2018-05-05 11:35:15
4 Rebate 3000 2018-05-05 12:30:15
How can I do this ? It's not to be merged, but need to print like merged table and can be sort by (a_time & b_time) ascending.
EXPLAIN select (#rn := #rn + 1) as id, `from`, `values`, `time`
from ((select 'rebate' as `from`, a_value as `values`, a_time as `time`
from table_rebate
) union all
(select 'bonus' as `from`, b_value, b_time
from table_bonus
)
) br cross join
(select #rn := 0) params
order by `time`;
You can use union all:
select (#rn := #rn + 1) as id, `from`, `values`, `time`
from ((select 'rebate' as `from`, a_value as `values`, a_time as `time`
from table_rebate
) union all
(select 'bonus' as `from`, b_value, b_time
from table_bonus
)
) br cross join
(select #rn := 0) params
order by `time`;
Note that from, values, and time are all keywords in SQL (even if not reserved). That makes them very bad names for columns.

How to group data based on ranking in mysql

I am struggling to create query I want to group data by customer id based on score . customer have multiple score I want to combine customer score by their ranking
below the table structure
CREATE TABLE `score` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`customer_id` varchar(10) DEFAULT NULL,
`score` int(6) DEFAULT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=11 DEFAULT CHARSET=latin1;
insert into `score`(`id`,`customer_id`,`score`)
values (1,'C1',20), (2,'C1',10),(3,'C3',30),(4,'C1',30),(5,'C2',40),
(6,'C2',50),(7,'C2',20),(8,'C1',50),(9,'C3',20),
(10,'C1',50);
Table result look like
id customer_id score
1 C1 20
2 C1 10
3 C3 30
4 C1 30
5 C2 40
6 C2 50
7 C2 20
Desire result :
customer_id score Rank
C1 30 1
C1 20 2
C1 10 3
C2 50 1
C2 40 2
C2 20 3
C3 30 1
try this
SELECT
a.score AS score,
#rn := IF(#PREV = customer_id, #rn + 1, 1) AS rank,
#PREV := customer_id AS cutomerId
FROM score AS a
JOIN (SELECT #PREV := NULL, #rn := 0) AS vars
ORDER BY customer_id, score DESC, id
You can use variables for this:
SELECT id, customer_id, score,
#rnk := IF(#cid = customer_id, #rnk + 1,
IF(#cid := customer_id, 1, 1)) AS rank
FROM score
CROSS JOIN (SELECT #rnk := 0, #cid := '') AS v
ORDER BY customer_id, score DESC
Demo here

MySQL to calculate ranking and update the original table

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.

Select from a select statement from a defined value

I've the following table structure:
id |name |date
1 a 2012-01-01
2 a 2011-01-01
3 a 2010-01-01
4 a 2014-01-01
5 a 2011-01-01
I'd like to perform a select order by date (desc), and after select the first 3 rows from the results by a condition which would be where id = 1. So the second part of the query would be "give me the first 3 rows starting from the row whose id equals to 1"
EDIT:
After the first "part" the result would be:
SELECT id, name, date FROM table ORDER BY date DESC
id |name |date
4 a 2014-01-01
1 a 2012-01-01
2 a 2011-01-01
5 a 2011-01-01
3 a 2010-01-01
After the second part it should look like this (so the first 3 after the row whose id is 1):
id |name |date
2 a 2011-01-01
5 a 2011-01-01
3 a 2010-01-01
I have no any idea how could I solve it, please help me.
EDIT:
This is the concrete code I'd like to re-write:
SELECT `id`, `questions`.`userid`, `categories`.`name`, `user`.`username`, `title`,
`details`, `date` FROM `questions`
LEFT JOIN `user`
ON `questions`.`userid` = `user`.`userid`
LEFT JOIN `categories`
ON `questions`.`categoryid` = `categories`.`categoryid`
ORDER BY `date` DESC LIMIT 10
SELECT *
FROM table
WHERE date < (SELECT date FROM table WHERE id = 1)
ORDER BY date DESC
LIMIT 3
This isn't pretty because MySQL doesn't support row_number() or common table expressions, but it should work. Basically, get the row number ordered by the date, then select those whose row number is greater than an arbitrary value (in this case 1). Finally use limit to select the number of records you want.
SELECT id, name, mydate
FROM (
SELECT id, name, mydate, #rn:=#rn+1 rn
FROM mytable, (select #rn:=0) t
ORDER BY mydate DESC
) t2
WHERE rn > (
select rn
from (
SELECT id, name, mydate, #rn:=#rn+1 rn
FROM mytable, (select #rn:=0) t
ORDER BY mydate DESC
) t2
where id = 1
)
LIMIT 3
SQL Fiddle Demo
This is what you want to do... if finds the first id thats equal to 4 and then selects those out. then limit the offset to go to the next row and pull out 3
SELECT id, name, m_date from(
SELECT id, name, m_date, #a := id, if(#a = 4, #b := 1, #b) AS join_id
FROM test
join(SELECT #a := 0, #b := 0) t
ORDER BY m_date DESC
) AS tt
WHERE join_id = 1
LIMIT 1,3
SELECT temp.`id`, temp.`userid`, `categories`.`name`, `user`.`username`, temp.`title`,
temp.`details`, temp.`date` FROM (
SELECT `id`, `categoryid`, `details`, `title`, `userid`, `date`, #a := id, if(#a = 11, #b := 1, #b) AS join_id
FROM `questions`
join(SELECT #a := 0, #b := 0) t
ORDER BY `date` DESC
) as temp
LEFT JOIN `user`
ON temp.`userid` = `user`.`userid`
LEFT JOIN `categories`
ON temp.`categoryid` = `categories`.`categoryid`
WHERE join_id = 1
LIMIT 1,10;
SEE FIDDLE for clarification

How to group continuous ranges using MySQL

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/