Group, count and concat laps - mysql

I can't resolve this problem (race), I need show the time for each LAP. This is times table
id | race_id | car_num | time |
+-----+-------------+---------+------------+
1 | 8 | 25 | 00:09:05 |
2 | 8 | 33 | 00:09:35 |
3 | 8 | 10 | 00:09:55 |
4 | 8 | 25 | 00:18:15 |
5 | 8 | 33 | 00:19:05 |
6 | 8 | 25 | 00:39:45 |
I tried this query:
SELECT
car_num, COUNT(car_num) as laps, race_id, concat(vlap,'-',time) as times
FROM
(SELECT num_car, concat(time,'-',v1) vlap
FROM tiempos) vti
GROUP BY
car_num
This is output required:
car_num | laps | race_id | times |
+-----+-------------+---------+------------------------------------------------+
25 | 3 | 8 | lap1 00:09:05, lap2 00:18:15, lap3 00:39:45 |
33 | 2 | 8 | lap1 00:09:35, lap2 00:19:05 |
10 | 1 | 8 | lap1 00:09:55 |
I'm dizzy, some idea please

You can get most of what you want with a simple group_concat():
select car_num, count(*) as laps, race_id, group_concat(time order by id separator ', ' ) as times
from tiempos t
group by car_num, race_id;
If you need the lap number, you can get that using variables:
select car_num, count(*) as laps, race_id, group_concat('lap', rn, ' ', time order by id separator ', ' ) as times
from (select t.*,
(#rn := if(#t = time, #rn + 1,
if(#t := time, 1, 1)
) as rn
from tiempos t cross join
(select #rn := 0, #t := '') vars
order by race_id, car_num, time
) t
group by car_num, race_id;

Related

SELECT user ranking on MySQL by an offset

I'm trying to make a query in MySQL that returns me only 10 users from the table, but with a rank value that is the ORDER result of the xp column. Right now I have this:
SELECT id, xp, #curRank := #curRank + 1 AS rank
FROM usuarios, (SELECT #curRank := 0) r
ORDER BY xp DESC LIMIT 10;
It looks to be working perfectly when fetching the first 10 users.
+--------------------+------+------+
| id | xp | rank |
+--------------------+------+------+
| 373901344995803138 | 5863 | 1 |
| 701198768049225770 | 5692 | 2 |
| 239203656405221376 | 4961 | 3 |
| 692489002942726154 | 4508 | 4 |
| 416988898628206593 | 3669 | 5 |
| 312003290378534912 | 3155 | 6 |
| 608344569381126167 | 3059 | 7 |
| 671949142473310238 | 3041 | 8 |
| 549743978191519744 | 2991 | 9 |
| 592440479577145383 | 2519 | 10 |
+--------------------+------+------+
But when I try to fetch for example LIMIT 10,10 to get the users between 11 and 20, although they are ordered, their global rank is incorrect because #curRank is not increasing for all the users before the offset.
+--------------------+------+------+
| id | xp | rank |
+--------------------+------+------+
| 638196238436532234 | 1888 | 1 |
| 601269358349516833 | 1447 | 2 |
| 548357514497097743 | 1338 | 3 |
| 203591312031744000 | 1330 | 4 |
| 379034072519016469 | 1283 | 5 |
| 563804445654122497 | 1086 | 6 |
| 421296425981181952 | 1025 | 7 |
| 263816867100098560 | 850 | 8 |
| 631330775379214371 | 776 | 9 |
| 442529076511637504 | 702 | 10 |
+--------------------+------+------+
I don't know a way to make the global ranking work when using LIMIT.
In MySQL 8.0, just use window functions, as demonstrated by Gordon Linoff.
In earlier versions, you basically need a subquery to do what you want. I would recommend:
SELECT *
FROM (
SELECT id, xp, #curRank := #curRank + 1 AS rank
FROM (SELECT * FROM usuarios ORDER BY xp DESC) u
CROSS JOIN (SELECT #curRank := 0) r
ORDER BY xp DESC
) t
ORDER BY xp DESC
LIMIT 10, 10;
The subquery ranks all users first, then you can safely filter in the outer query. Note that the query pre-orders the table by xp in a subquery first: this is safer (user variables are tricky in MySQL).
Actually, you don't even needLIMIT in the outer query; you can use a WHERE clause instead:
SELECT *
FROM (
SELECT id, xp, #curRank := #curRank + 1 AS rank
FROM (SELECT * FROM usuarios ORDER BY xp DESC) u
CROSS JOIN (SELECT #curRank := 0) r
ORDER BY xp DESC
) t
WHERE rank BETWEEN 11 AND 20
ORDER BY rank
Instead, use row_number():
SELECT id, xp, row_number() over (order by cp desc) as rnk
FROM usuarios
ORDER BY xp DESC
LIMIT 10;

Get 10 latest records per category even some has less then 10

This is example of my table :
+-----+-----+------------+--------+-------------+--------------+
| LID | AID | Created | TypeID | PaymentDate | PaymentValue |
+-----+-----+------------+--------+-------------+--------------+
| 1 | 529 | 2017-05-12 | 1 | 2017-05-12 | 100 |
+-----+-----+------------+--------+-------------+--------------+
| 2 | 529 | 2018-04-10 | 4 | 2018-04-10 | 200 |
+-----+-----+------------+--------+-------------+--------------+
| 3 | 441 | 2014-01-23 | 3 | 2014-01-23 | 300 |
+-----+-----+------------+--------+-------------+--------------+
| 4 | 324 | 2017-09-14 | 1 | 2017-09-14 | 400 |
+-----+-----+------------+--------+-------------+--------------+
| 5 | 111 | 2018-05-12 | 0 | 2018-05-12 | 340 |
+-----+-----+------------+--------+-------------+--------------+
| 6 | 529 | 2018-05-12 | 1 | 2018-05-12 | 100 |
+-----+-----+------------+--------+-------------+--------------+
| 7 | 529 | 2018-06-12 | 1 | 2018-05-12 | 100 |
+-----+-----+------------+--------+-------------+--------------+
| 8 | 529 | 2018-07-12 | 1 | 2018-05-12 | 100 |
+-----+-----+------------+--------+-------------+--------------+
| 9 | 529 | 2018-08-12 | 1 | 2018-05-12 | 100 |
+-----+-----+------------+--------+-------------+--------------+
| 10 | 529 | 2018-09-12 | 1 | 2018-05-12 | 100 |
+-----+-----+------------+--------+-------------+--------------+
| 11 | 529 | 2018-01-12 | 1 | 2018-05-12 | 100 |
+-----+-----+------------+--------+-------------+--------------+
| 12 | 529 | 2018-05-14 | 1 | 2018-05-12 | 100 |
+-----+-----+------------+--------+-------------+--------------+
| 13 | 529 | 2018-05-21 | 1 | 2018-05-12 | 100 |
+-----+-----+------------+--------+-------------+--------------+
| 14 | 529 | 2018-03-12 | 1 | 2018-05-12 | 100 |
+-----+-----+------------+--------+-------------+--------------+
Here another table
+-----+-------+
| ID |caption|
+-----+-------+
| 0 | bad |
+-----+-------+
| 1 | good |
+-----+-------+
I need to get 10 latest records per AID. If there less than 10 records for some AID anyway i need to get ten rows and put "No payment date" into PaymentDate and Created fields, Null into TypeID and 0 into PaymentValue. I can get 10 or less latest records with
select *
from (select *,
(#rn := if(#c = AID, #rn + 1,
if(#c := AID, 1, 1)
)
) as rn
from history cross join
(select #rn := 0, #c := -1) params
order by AID, Created desc
) t
having rn <= 10;
But i dont know how force mysql to output 10 rows for each AID. Help me please.
Result should be in a form
AID,TypeId,Created,Caption
I have done it.
This query needs to create a row of 10 records to combine with distinct AID valies in the table. I was able to show the result for Amount and Create date and will leave it to you to continue since you will get the idea.
The critical part is to build a table with 10 rows times distinct AID so about 40 rows in table r. Then do a left join to table t which is similar to what you have done. Table t gets a rank of at most 10 records. Any missing rank up to 10 recs will be filled by table r. Coalesce will assign the default values such as 0 fro amount and 'no create date' for date.
http://sqlfiddle.com/#!9/855c21/2
SELECT coalesce(r.aid, t.aid) as aid,
coalesce(t.paymentvalue, 0) as paymentvalue,
coalesce(cast(t.created as char), 'no create date') as created
FROM (select * from (
select 1 as rw union
select 2 union select 3
union select 4 union select 5
union select 6 union select 7
union select 8 union select 9
union select 10) u
cross join (select distinct aid
from history) h
) as r
LEFT JOIN (
SELECT a.aid, a.paymentvalue,
a.created, count(*) rn
FROM history a
JOIN history b
ON a.aid = b.aid
AND a.created <= b.created
GROUP BY a.aid, a.created
HAVING COUNT(*) <= 10) t
on r.rw=t.rn and r.aid=t.aid
order by aid, created;
I have added RIGHT JOIN to bring in the null rows to top up to 10 (or n) rows per AID. Initially I use SELECT 1 UNION SELECT 2 ... to generate the 10 rows. In order to make it easier to increase the number of rows (say 100), I am trying this idea of generate_series equivalent for mysql. In order for this to work, the number of rows in history table must be equal to greater than the number of rows required per AID.
select t1.lid
,t2.aid
,coalesce(t1.created, "no created date") as created
,t1.typeID
,coalesce(t1.paymentdate, "no payment date") as paymentDate
,coalesce(t1.paymentvalue, 0) as paymentValue
,t2.rn
from
(
select *,
(#rn := if(#c = AID, #rn + 1,
if(#c := AID, 1, 1)
)
) as rn
from history cross join
(select #rn := 0, #c := -1) params
order by AID, Created desc
) t1
right join
( select *
from (select distinct aid from history ) h1
cross join
(select rn -- generate table with n rows numbered from 1 to n
from
(select
#num:= 0) init
cross join
(select #num := #num +1 rn
from history ) t -- assume history has at least 10 rows
limit
10 ) h2 -- n = 10; change it to the number of rows per aid required
) t2
on t1.aid = t2.aid and t1.rn = t2.rn
order by t2.aid, t2.rn

MySQL - Check if consecutive columns are the same and display only those rows

I have an events table that contains IDs (id) and dates (eventDate) corresponding to those IDs (id and eventDate are not the only columns in the table).
SQLFiddle here.
+--------+----+---------------------+
| row_id | id | eventDate |
+--------+----+---------------------+
| 1 | 1 | 2014-02-27 23:19:41 |
| 2 | 1 | 2014-02-27 23:21:41 |
| 3 | 1 | 2014-02-27 23:21:41 |
| 4 | 2 | 2014-02-27 23:23:08 |
| 5 | 2 | 2014-02-27 23:25:08 |
| 6 | 2 | 2014-02-27 23:25:08 |
| 9 | 3 | 2014-02-28 15:36:55 |
| 8 | 3 | 2014-02-28 15:36:55 |
| 7 | 3 | 2014-02-28 15:34:55 |
| 10 | 4 | 2014-02-28 19:31:31 |
| 11 | 4 | 2014-02-28 19:33:31 |
| 12 | 4 | 2014-02-28 19:33:31 |
| 13 | 5 | 2014-02-28 19:33:34 |
| 14 | 5 | 2014-02-28 19:33:33 |
| 15 | 5 | 2014-02-28 19:31:33 |
| 16 | 6 | 2014-03-04 22:40:21 |
| 17 | 6 | 2014-03-04 22:38:21 |
| 18 | 6 | 2014-03-04 22:40:21 |
| 19 | 7 | 2014-03-04 23:08:37 |
| 20 | 7 | 2014-03-04 23:08:38 |
+--------+----+---------------------+
I want to select only those rows from the table, where consecutive event dates are the same for the same ID.
Thus, I would like to see only these entries -
+----+---------------------+
| id | eventDate |
+----+---------------------+
| 1 | 2014-02-27 23:21:41 |
| 1 | 2014-02-27 23:21:41 |
| 2 | 2014-02-27 23:25:08 |
| 2 | 2014-02-27 23:25:08 |
| 3 | 2014-02-28 15:36:55 |
| 3 | 2014-02-28 15:36:55 |
| 4 | 2014-02-28 19:33:31 |
| 4 | 2014-02-28 19:33:31 |
Note that there is no
| 6 | 2014-03-04 22:40:21 |
| 6 | 2014-03-04 22:40:21 |
in the above result, because they're not consecutive.
I know I can store the output of the SQL query in a file and then use a unix tool to do this, but I want to know if this is achievable directly through SQL.
Should be able to accomplish this leveraging a group by although my mySql is a bit rusty.
SELECT t.*
FROM (
SELECT
id,
eventDate,
COUNT(0) AS numRows
FROM tabl
GROUP BY id, DATE(eventDate)
HAVING COUNT(0) > 1
ORDER BY eventDate
) t
Then you just join this correlated sub query back to the original table if you need additional columns.
select id,eventDate from your_tableName where eventDate in (select eventDate from your_tableName group by id,eventDate having count(eventDate) > 1);
select ta.id, ta.eventDate from
(
select row_id as ra, t1.id, t1.eventDate
from events t1
) as ta
join
(
select row_id as rb, t2.id, t2.eventDate
from events t2
) as tb
on rb = ra+1 and ta.id = tb.id and ta.eventDate = tb.eventDate
I have found a way to match the eventDate of the next row but the only drawback is that it will return the number of consecutive dates - 1 rows. But in your code you can just loop 1 extra time.
SET #inc = 0;
SET #innerInc = 1;
SELECT t1.id, t1.eventDate
FROM (
SELECT id, eventDate, (#inc := #inc + 1) as increment FROM temp
) t1
WHERE t1.eventDate = (
SELECT t2.eventDate FROM (
SELECT eventDate, (#innerInc := #innerInc + 1) as increment FROM temp
) t2
WHERE t2.increment = t1.increment
);
Here is the SQLFiddle for this: Here
This should be able to do it with a single table scan (no subqueries,joins,etc..)
SELECT t.id,t.eventDate
FROM (
SELECT
IF(id = #prevID AND eventDate = #prevDate, #counter, #counter := #counter+1) as c,
#prevID := id as id,
#prevDate := eventDate as eventDate
FROM events e
JOIN (SELECT #counter := 0, #prevID := NULL, #prevDate := NULL) as stuff
WHERE 1 #or some where condition for events
ORDER BY row_id ASC
) as t
GROUP BY t.c
If it's not specifically that you need the positionally consecutive entry, but rather that if you grouped by eventDate and found entries with the same eventDate then you'd want those records, then the following:
select *
from Table a
join (select eventDate, count(*)
from Table
group by eventDate
having count(*) > 1) b
on (a.eventDate = b.eventDate)
The arbitrary dependence on the position of the data suggests that there is some other property you're not sharing, and it is by that property that the records are retrieved and ordered. If such a property determines the record's position, then it's precisely by ordering or grouping with that property that you can efficiently solve this.
After throwing away my self-join I think you're going to have to generate row_numbers for each sub_query:
select #rn1 := #rn1+1 as ra, t1.id, t1.eventDate
from events t1
join (select #rn1 := 0) r;
and then join that to
select #rn2 := #rn2+1 as rb, t2.id, t2.eventDate
from events t2
join (SELECT #rn2 := 0) r;
so final answer:
select ta.id, ta.eventDate from
(
select #rn1 := #rn1+1 as ra, t1.id, t1.eventDate
from events t1
join (select #rn1 := 0) r
) as ta
join
(
select #rn2 := #rn2+1 as rb, t2.id, t2.eventDate
from events t2
join (SELECT #rn2 := 0) r
) as tb
on rb = ra+1 and ta.id = tb.id and ta.eventDate = tb.eventDate
Results:
1 February, 27 2014 23:21:41+0000
2 February, 27 2014 23:25:08+0000
3 February, 28 2014 15:36:55+0000
4 February, 28 2014 19:33:31+0000

MYSQL Sum of eatch row and limit to the best 15

I've this table : extended
+--------+-----+-----+-----+-----+- -+-----+-----+
| Name | T1 | T2 | T1 | T3 | .. | T19 | T20 |
+--------+-----+-----+-----+-----+- -+-----+-----+
| john | 5 | 10 | 50 | 10 | .. | 20 | 8 |
| bill | 2 | 8 | 11 | 5 | .. | 9 | 55 |
| james | 30 | 15 | 12 | 40 | .. | 13 | 10 |
| elsie | 28 | 35 | 20 | 32 | .. | 18 | 1 |
| .... | .. | .. | .. | .. | .. | .. | .. |
+--------+-----+-----+-----+-----+- -+-----+-----+
And i want to return this one :
+--------+-------+-----+-----+-----+-----+- -+-----+-----+
| Name | TOTAL | T1 | T2 | T1 | T3 | .. | T19 | T20 |
+--------+-------+-----+-----+-----+-----+- -+-----+-----+
| bill | 250 | 2 | 8 | 11 | 5 | .. | 9 | 55 |
| john | 230 | 5 | 10 | 50 | 10 | .. | 20 | 8 |
| elsie | 158 | 28 | 35 | 20 | 32 | .. | 18 | 1 |
| james | 129 | 30 | 15 | 12 | 40 | .. | 13 | 10 |
| .... | .... | .. | .. | .. | .. | .. | .. | .. |
+--------+-------+-----+-----+-----+-----+----+-----+-----+
Order by TOTAL. This total is the sum of best of 15 Tx ...
I don't now how to do this.
The table come from a request ( CREATE VIEW ) from another table with a lot of data.
Can you help me ?
At this point, i do the sum of ALL Tx, but it's not what i want ...
SELECT `Name`, (T1+T2+ T3+T4+T5+T6+T7+T8+T9+T10+T11+T12+T13+T14+T15+T16+T17+T18+T19+T20) AS TOTAL, T1,T2, T3,T4,T5,T6,T7,T8,T9,T10,T11,T12,T13,T14,T15,T16,T17,T18,T19,T20
FROM `extended`
ORDER BY TOTAL DESC
If you wanted to remove the lowest value, that is easy:
select name,
(t1 + . . . + t20) -
least(t1, . . . , t20)
from table;
Unfortunately, MySQL does not have the nth function, so getting the second least and others is quite difficult.
If you had the values in separate rows, you could do:
select name, sum(t)
from (select en.*,
if(#name = name, #rn := #rn + 1, #rn := 1) as rn,
#name := name
from extended_norm en cross join
(select #name := '', #rn := 0) const
order by name, t desc
) en
where rn <= 15
group by name;
With your data structure, you'll probably need to write a user-defined function to do what you want.
EDIT:
If you want the list of t's, you can do it two ways. You can modify the above to include the pivot (this assumes that you have a column called something like tnumber to identify which t-value):
select name, sum(case when rn <= 15 then t end) as Total,
max(case when en.tnumber = 1 then t end) as T1,
max(case when en.tnumber = 2 then t end) as T2,
. . .
max(case when en.tnumber = 1 then t end) as T20
from (select en.*,
if(#name = name, #rn := #rn + 1, #rn := 1) as rn,
#name := name
from extended_norm en cross join
(select #name := '', #rn := 0) const
order by name, t desc
) en
group by name;
Otherwise, take the above query and join it to the denormalized table:
select e.*, tt.total
from extended e join
(the above query) tt
on e.name = tt.name;
It's awkward but this can also work!
select name, sum(t) from
(select name, t from (
select name, t1 as t from extended
union
select name, t2 as t from extended
union
select name, t3 as t from extended
union
select name, t4 as t from extended
union
select name, t5 as t from extended
union
select name, t6 as t from extended
union
select name, t7 as t from extended
union
select name, t8 as t from extended
union
select name, t9 as t from extended
union
select name, t10 as t from extended
union
select name, t11 as t from extended
union
select name, t12 as t from extended
union
select name, t13 as t from extended
union
select name, t14 as t from extended
union
select name, t15 as t from extended
union
select name, t16 as t from extended
union
select name, t17 as t from extended
union
select name, t18 as t from extended
union
select name, t19 as t from extended
union
select name, t20 as t from extended
) z
where name='bill' order by name, t desc limit 0,15);
But you have to run the query for each user by replacing 'bill' by other name

Limiting the output of specific column sql hana

I have a table structure as given below and what I'd like to be able to do is retrieve the top three records with the highest value for each Company code.
I've googled and I couldn't find a better way so hopefully you guys can help me out.
By the way, I'm attempting this in MySQL and SAP HANA. But I am hoping that I can grab the "structure" if the query for HANA if I can get help for only MySQL
Thanks much!
Here's the table:
http://pastebin.com/xgzCgpKL
In MySQL you can do
To get exactly three records per group (company) no matter ties emulating ROW_NUMBER() analytic function. Records with the same value get the same rank.
SELECT company, plant, value
FROM
(
SELECT company, plant, value, #n := IF(#g = company, #n + 1, 1) rnum, #g := company
FROM table1 CROSS JOIN (SELECT #n := 0, #g := NULL) i
ORDER BY company, value DESC, plant
) q
WHERE rnum <= 3;
Output:
| COMPANY | PLANT | VALUE |
|---------|-------|-------|
| 1 | C | 5 |
| 1 | B | 4 |
| 1 | A | 3 |
| 2 | G | 6 |
| 2 | C | 5 |
| 2 | D | 3 |
| 3 | E | 8 |
| 3 | A | 7 |
| 3 | B | 3 |
Get all records per group that have a rank from 1 to 3 emulating DENSE_RANK() analytic function
SELECT company, plant, value
FROM
(
SELECT company, plant, value, #n := IF(#g = company, IF(#v = value, #n, #n + 1), 1) rnum, #g := company, #v := value
FROM table1 CROSS JOIN (SELECT #n := 0, #g := NULL, #v := NULL) i
ORDER BY company, value DESC, plant
) q
WHERE rnum <= 3;
Output:
| COMPANY | PLANT | VALUE |
|---------|-------|-------|
| 1 | C | 5 |
| 1 | B | 4 |
| 1 | A | 3 |
| 1 | E | 3 |
| 1 | G | 3 |
| 2 | G | 6 |
| 2 | C | 5 |
| 2 | D | 3 |
| 3 | E | 8 |
| 3 | A | 7 |
| 3 | B | 3 |
| 3 | G | 3 |
Here is SQLFiddle demo
UPDATE: Now it looks like HANA supports analytic functions so the queries will look like
SELECT company, plant, value
FROM
(
SELECT company, plant, value,
ROW_NUMBER() OVER (PARTITION BY company ORDER BY value DESC) rnum
FROM table1
)
WHERE rnum <= 3;
SELECT company, plant, value
FROM
(
SELECT company, plant, value,
DENSE_RANK() OVER (PARTITION BY company ORDER BY value DESC) rank
FROM table1
)
WHERE rank <= 3;
Here is SQLFiddle demo It's for Oracle but I believe it will work for HANA too