Times between laps - mysql

I have this SQL work fine except 1 point (at third value I need take previous time), this is the table (times) schema, output, and SQL:
id number time
-----------------------------------
1 9 00:00:10.000000
2 10 00:00:15.000000
3 9 00:00:22.000000
4 10 00:00:35.000000
1 9 00:00:55.000000
SELECT t.number, COUNT(1) laps,
GROUP_CONCAT(SEC_TO_TIME(time) ORDER BY t.id) times
FROM (
SELECT t1.id, t1.number,
TIME_TO_SEC(t1.time) - COALESCE(SUM(TIME_TO_SEC(t2.time)), 0) time
FROM times t1
LEFT JOIN times t2 ON t1.number = t2.number AND t2.id < t1.id
GROUP BY t1.id, t1.number, t1.time
) t
GROUP BY number
Result this output:
number laps times
-----------------------------------
9 3 00:00:10,00:00:12,00:00:23
10 2 00:00:15,00:00:20
I need this REsult expected:
number laps times
-----------------------------------
9 3 00:00:10,00:00:12,00:00:33
10 2 00:00:15,00:00:20
Where third time - previos calculated time, in this case (00:00:10,00:00:12,00:00:23 - 00:00:12) I Tried but SUM all times previous.
SQL Fiddle

Only need change SUM to MAX
SELECT t.number, COUNT(1) laps,
GROUP_CONCAT(SEC_TO_TIME(time) ORDER BY t.id) times
FROM (
SELECT t1.id, t1.number,
TIME_TO_SEC(t1.time) - COALESCE(MAX(TIME_TO_SEC(t2.time)), 0) time
FROM times t1
LEFT JOIN times t2 ON t1.number = t2.number AND t2.time < t1.time -- Suggest you don't use id to compare
GROUP BY t1.id, t1.number, t1.time
) t
GROUP BY number
See SQL FIDDLE DEMO
And here's another version without inner group by. They have the same effect, but different query plan. So you have choices to compare the performances.
SELECT t.number, COUNT(1) laps,
GROUP_CONCAT(SEC_TO_TIME(time) ORDER BY t.time) times
FROM (
SELECT t1.id, t1.number, t1.time tm1,
(SELECT TIME_TO_SEC(t1.time) - COALESCE(MAX(TIME_TO_SEC(t2.time)),0)
FROM times t2
WHERE t1.number = t2.number AND t2.time < t1.time) AS time
FROM times t1
) t
GROUP BY number;

This was a bit tricky, but here is a solution:
The main problem is the "smaller than" condition in your join. So first you have to add a running number (rank) to your Groups based on this answer. Then you can join by that rank. The resulting query is a bit large but gives you the desired result:
SELECT t.number, COUNT(1) laps,
GROUP_CONCAT(SEC_TO_TIME(time) ORDER BY t.id) times
FROM (
SELECT t1.id, t1.number,
TIME_TO_SEC(t1.time) - COALESCE(SUM(TIME_TO_SEC(t2.time)), 0) time
FROM
(SELECT
number, id, time,
(
CASE number
WHEN #curType
THEN #curRow := #curRow + 1
ELSE #curRow := 1 AND #curType := number END
) AS rank
FROM times, (SELECT #curRow := 0, #curType := '') r
ORDER BY number, id
) as t1
LEFT JOIN
(SELECT
number, id, time,
(
CASE number
WHEN #curType
THEN #curRow := #curRow + 1
ELSE #curRow := 1 AND #curType := number END
) AS rank
FROM times, (SELECT #curRow := 0, #curType := '') r
ORDER BY number, id
) t2
ON t1.number = t2.number and t2.rank+1 = t1.rank
GROUP BY t1.id, t1.number, t1.time
) t
GROUP BY number
gives:
| NUMBER | LAPS | TIMES |
|--------|------|----------------------------|
| 9 | 3 | 00:00:10,00:00:12,00:00:33 |
| 10 | 2 | 00:00:15,00:00:20 |

Related

MySql join two tables sequentially

Table one
===================
id name
-------------------
1 m
2 m
3 a
4 u
5 g
Table two
===================
id name
-------------------
8 m
9 m
10 u
11 a
12 x
15 m
Expected result
===================
1 m 8
2 m 9
3 a 11
4 u 10
I need to find id from table 2 associated with table 1 by name. But ids from table 2 must be different.
If i make join i receive wrong intersections:
select t1.id as i1, t1.name, t2.id as i2 from t1
join t2 on t1.name = t2.name
i1 name i2
--------------------
'1','m','8'
'2','m','8'
'1','m','9'
'2','m','9'
'4','u','10'
'3','a','11'
'1','m','15'
'2','m','15'
I need this for tables synchronization from different systems.
You can use the following query:
SELECT t1.id, t1.name, t2.id
FROM (
SELECT id, name,
#rn1 := IF(#n = name, #rn1 + 1,
IF(#n := name, 1, 1)) AS rn1
FROM Table1
CROSS JOIN (SELECT #rn1 := 0, #n := '') AS vars
ORDER BY name, id) AS t1
INNER JOIN (
SELECT id, name,
#rn2 := IF(#n = name, #rn2 + 1,
IF(#n := name, 1, 1)) AS rn2
FROM Table2
CROSS JOIN (SELECT #rn2 := 0, #n := '') AS vars
ORDER BY name, id
) AS t2 ON t1.name = t2.name AND t1.rn1 = t2.rn2
ORDER BY t1.id
The query uses variables in order to simulate ROW_NUMBER() window function, currently not available in MySQL. Variables #rn1, #rn2 enumerate records that belong to the same name partition with an order determined by id field.
Demo here

MYSQL - Total registrations per day

I have the following structure in my user table:
id(INT) registered(DATETIME)
1 2016-04-01 23:23:01
2 2016-04-02 03:23:02
3 2016-04-02 05:23:03
4 2016-04-03 04:04:04
I want to get the total (accumulated) user count per day, for all days in DB
So result should be something like
day total
2016-04-01 1
2016-04-02 3
2016-04-03 4
I tried some sub querying, but somehow i have now idea how to achieve this with possibly 1 SQL statement. Of course if could group by per day count and add them programmatically, but i don't want to do that if possible.
You can use a GROUP BY that does all the counts, without the need of doing anything programmatically, please have a look at this query:
select
d.dt,
count(*) as total
from
(select distinct date(registered) dt from table1) d inner join
table1 r on d.dt>=date(r.registered)
group by
d.dt
order by
d.dt
the first subquery returns all distinct dates, then we can join all dates with all previous registrations, and do the counts, all in one query.
An alternative join condition that can give some improvements in performance is:
on d.dt + interval 1 day > r.registered
Not sure why not just use GROUP BY, without it this thing will be more complicated, anyway, try this;)
select
date_format(main.registered, '%Y-%m-%d') as `day`,
main.total
from (
select
table1.*,
#cnt := #cnt + 1 as total
from table1
cross join (select #cnt := 0) t
) main
inner join (
select
a.*,
if(#param = date_format(registered, '%Y-%m-%d'), #rowno := #rowno + 1 ,#rowno := 1) as rowno,
#param := date_format(registered, '%Y-%m-%d')
from (select * from table1 order by registered desc) a
cross join (select #param := null, #rowno := 0) tmp
having rowno = 1
) sub on main.id = sub.id
SQLFiddle DEMO

SELECT Current and Previous row WHERE condition

id value
---------
1 a
2 b
3 c
4 a
5 t
6 y
7 a
I want to select all rows where the value is 'a' and the row before it
id value
---------
1 a
3 c
4 a
6 y
7 a
I looked into
but I want to get all such rows in one query.
Please help me start
Thank you
I think the easiest way might be to use variables:
select t.*
from (select t.*,
(rn := if(value = 'a', 1, #rn + 1) as rn
from table t cross join
(select #rn := 0) params
order by id desc
) t
where rn in (1, 2)
order by id;
An alternative method uses a correlated subquery to get the previous value and then uses this in the where clause:
select t.*
from (select t.*,
(select t2.value
from table t2
where t2.id < t.id
order by t2.id desc
limit 1
) as prev_value
from table t
) t
where value = 'a' or prev_value = 'a';
With an index on id, this might even be faster than the method using variables.

MySQL - Parallel merge two unrelated queries with same # of rows

I have two tables:
exam_outline_items:
jml_quiz_pool:
Of all the things I've tried, this got me the closest:
select t1.sequence, t1.title, t2.q_cat, t2.q_count
from student_pl.exam_outline_items t1
cross join pe_joomla.jml_quiz_pool t2
where t1.exam_outline_id = 5 and t1.chapter_num > 0
and t2.q_id = 1109 and t2.q_count > 0
group by title
Which produces this result:
I just need those q_cat values to be different, like they are in the 2nd query.
Thanks in advance for your help.
You have to have something to connect them with. If you don't have such a column, you can simulate one by creating a rownumber with variables.
select sequence, title, q_cat, q_count from (
select t1.sequence, t1.title, #r1 := #r1 + 1 as rownumber
from student_pl.exam_outline_items t1
, (select #r1 := 0) var_init
where t1.exam_outline_id = 5 and t1.chapter_num > 0
order by t1.sequence
) a
inner join
(
select t2.q_cat, t2.q_count, #r2 := #r2 + 1 as rownumber
from pe_joomla.jml_quiz_pool t2
, (select #r2 := 0) var_init
where t2.q_id = 1109 and t2.q_count > 0
order by t2.q_cat
) b on a.rownumber = b.rownumber;
Also note, that I used order by in those queries. In a database you have no sort order unless you explicitly set it with order by.

Partition data in Percentile range and assign different value for different Range

I have table structure as shown in below
Temp
Customer_id | sum
Now I have to create view with extra column customer_type and assign value 1 if customer lies in top 10% customer (with descending order of sum,also total number of customer may vary) and 2 if customer lies between 10%-20%, 3 if customer lies between 20%-60% and 4 if customer lies between 60%-100%. How can I do this?
I just able to extract top 10% and between 10% - 20% data but couldn't able to assign value as (source)
SELECT * FROM temp WHERE sum >= (SELECT sum FROM temp t1
WHERE(SELECT count(*) FROM temp t2 WHERE t2.sum >= t1.sum) <=
(SELECT 0.1 * count(*) FROM temp));
and (not efficient just enhance above code)
select * from temp t1
where (select count(*) from temp t2 where t2.sum>=t2.sum)
>= (select 0.1 * count(*) from temp) and (select count(*) from temp t2 where t2.sum>=t1.sum)
<= (select 0.2 * count(*) from temp);
Sample data are available at sqlfiddle.com
This should help you. You need to get row number for sum and total number of rows. I'm sure you can figure out the rest easily.
SELECT
*,
#curRow := #curRow + 1 AS row_number,
(#curRow2 := #curRow2 + 1) / c as pct_row
FROM
temp t
JOIN (SELECT #curRow := 0) r
JOIN (SELECT #curRow2 := 0) r2
join (select count(*) c from temp) s
order by
sum desc
This is based on this answer
I had solve this as like this. Thanks for #twn08 for his answer which guide me upto this.
select customer_id,sum,case
when pct_row<=0.10 then 1
when pct_row>0.10 and pct_row<=0.20 then 2
when pct_row>0.20 and pct_row<=0.60 then 3
when pct_row>0.60 then 4
end as customer_label from (
select customer_id,sum,(#curRow := #curRow+1)/c as pct_row
from temp t
jOIN (SELECT #curRow := 0) r
JOIN (SELECT #curRow2 := 0) r2
join (select count(*) c from temp) s
order by sum desc) p;
I don't know whether this is efficient method or not but work fine for small data set.