im very noob and this became ungoogleable (is that a word?)
the rank is by time but..
time done with ( A=0 ) AND ( B=0 ) beat everyone
time done with ( A=0 ) AND ( B=1 ) beat everyone with ( A=1 )
time done with ( A=1 ) AND ( B=0 ) beat everyone with ( A=1 + B=1 )
rank example (track=desert)
pos--car------time---A----B
1.---yellow----90----No---No
2.---red-------95----No---No
3.---grey-----78-----No---Yes
4.---orange--253---No---Yes
5.---black----86----Yes---No
6.---white----149---Yes---No
7.---pink-----59----Yes---Yes
8.---blue-----61----Yes---Yes
to make it even worst, the table accept multiple records for the same car
here is the entries
create table `rank`
(
`id` int not null auto_increment,
`track` varchar(25) not null,
`car` varchar(32) not null,
`time` int not null,
`a` boolean not null,
`b` boolean not null,
primary key (`id`)
);
insert into rank (track,car,time,a,b) values
('desert','red','95','0','0'),
('desert','yellow','89','0','1'),
('desert','yellow','108','0','0'),
('desert','red','57','1','1'),
('desert','orange','120','1','0'),
('desert','grey','85','0','1'),
('desert','grey','64','1','0'),
('desert','yellow','90','0','0'),
('desert','white','92','1','1'),
('desert','orange','253','0','1'),
('desert','black','86','1','0'),
('desert','yellow','94','0','1'),
('desert','white','149','1','0'),
('desert','pink','59','1','1'),
('desert','grey','78','0','1'),
('desert','blue','61','1','1'),
('desert','pink','73','1','1');
please, help? :p
ps: sorry about the example table
To prioritize a, then b, then time, use order by b, a, time.
You can use a not exists subquery to select only the best row per car.
Finally, you can add a Pos column using MySQL's variables, like #rn := #rn + 1.
Example query:
select #rn := #rn + 1 as pos
, r.*
from rank r
join (select #rn := 0) init
where not exists
(
select *
from rank r2
where r.car = r2.car
and (
r2.a < r.a
or (r2.a = r.a and r2.b < r.b)
or (r2.a = r.a and r2.b = r.b and r2.time < r.time)
)
)
order by
b
, a
, time
See it working at SQL Fiddle.
Related
I have a table with column "time" (INT unsigned), every row represents one second and I need to find gaps in time (missing seconds).
I have tried with this query (to find the first time before a gap):
SELECT t1.time
FROM `table` AS t1
LEFT JOIN `table` AS t2 ON t2.time=(t1.time+1)
WHERE t2.time IS NULL
ORDER BY TIME ASC
LIMIT 1
And it works but it's too slow for big tables (near 100M rows)
Is there some faster solution?
EXPLAIN query:
SHOW CREATE:
CREATE TABLE `candles` (
`time` int(10) unsigned NOT NULL,
`open` float unsigned NOT NULL,
`high` float unsigned NOT NULL,
`low` float unsigned NOT NULL,
`close` float unsigned NOT NULL,
`vb` int(10) unsigned NOT NULL,
`vs` int(10) unsigned NOT NULL,
`trades` int(10) unsigned NOT NULL,
PRIMARY KEY (`time`)
) ENGINE=MyISAM DEFAULT CHARSET=utf8
If DB version is 8.0, then The Recursive Common Table Expression might be used such as
WITH RECURSIVE cte AS
(
SELECT 1 AS n
UNION ALL
SELECT n + 1 AS value
FROM cte
WHERE cte.n < (SELECT MAX(time) FROM tab )
)
SELECT n AS gaps
FROM cte
LEFT JOIN tab
ON n=time
WHERE cte.n > (SELECT MIN(time) FROM tab )
AND time IS NULL
Demo
In MySQL 5.7, this is a use case where user variables might be helpful:
select max(time)
from (
select t.time, #rn := #rn + 1 as rn
from (select time from mytable order by time) t
cross join (select #rn := 0) r
) t
group by time - rn
This addresses the question as a gaps-and-islands problem. The idea is to identify groups of records where time increments without gaps (the islands). For this, we assign an incrementing id to each row, ordered by time; whenever the difference between time and the auto-increment changes, you know there is a gap.
With mysql 8, you can use LEAD():
select time from (
select time, lead(time, 1) over (order by time) next_time
from `table`
) t
where time+1 != next_time
In earlier versions, I might do something like:
select prev_time as time from (
select #prev_time+0 as prev_time,if(#prev_time:=time,time,time) as time
from (select #prev_time:=null) initvars
cross join (select time from `table` order by time) t
) t
where time != prev_time+1
Either will not include the greatest time, where your original query would have.
I think the group by required to treat it as a strict gaps and islands problem would be too expensive with that many records.
fiddle
I'm trying to select every n-th row from mysql, I read this answer.
There is a table sys_request_log:
CREATE TABLE `sys_request_log`
(
`id` bigint(20) NOT NULL,
`user_id` bigint(20) DEFAULT NULL,
`ip` varchar(50) DEFAULT NULL,
`data` mediumtext,
`create_time` datetime DEFAULT NULL,
PRIMARY KEY (`id`) USING BTREE,
KEY `user_id` (`user_id`) USING BTREE
) ENGINE=InnoDB DEFAULT CHARSET=utf8 ROW_FORMAT=DYNAMIC;
It contains 11837 rows.
I try to select every 5-th row from table, first I try to execute:
SELECT
*
FROM
(SELECT #ROW := #ROW + 1 AS rownum, log.* FROM ( SELECT #ROW := 0 ) r, sys_request_log log ) ranked
WHERE
rownum % 5 = 1
The result is:
rownum id user_id create_time
-------------------------------------------------------------------
1 1271446699071639552 1 2020-06-12 22:18:10
6 1271446948980854784 1 2020-06-12 22:19:10
11 1271447016878247936 1269884071484461056 2020-06-12 22:19:26
It costs 1.001s time
I found there is a unrelated column rownum. So I modify the SQL like this:
SELECT
log.*
FROM
(SELECT #ROW := #ROW + 1 AS rownum FROM (SELECT #ROW := 0) t) r,
sys_request_log log
WHERE
rownum % 5 = 1
Now the result is clean (no rownum), but It costs 2.516s time!
Why?
Mysql version: 5.7.26-log
In the first case, the row number values are selected during the selection from the table(sys_request_log), but for the second case there occurs a cartesian product among subquery r and the selection from the table because of the CROSS JOIN occurence for each individual rownum versus each individual row value of the table.
If I understand correctly, you can do what you want by moving the variable assignment to the where clause:
select srl.*
from sys_request_log srl cross join
(select #rn := 0) params
where (#rn := (#rn + 1)) % 5 = 1;
Note this happens to work in this case, because the query needs to do a full table scan and run the WHERE clause on each row. It might not work if the query has a JOIN, GROUP BY or ORDR BY.
The use of variables in this way is deprecated in MySQL now. You should upgrade and learn about window functions.
Your second query has different result from the first one and returns all rows, so take much more time from the first one.
To remove rownum from first query, Just name fields in SELECT clause.
try this:
SELECT
ranked.id, ranked.user_id ,ranked.create_time
FROM
( SELECT #ROW := #ROW + 1 AS rownum, log.* FROM ( SELECT #ROW := 0 ) r, sys_request_log log ) ranked
WHERE
rownum % 5 = 1
I have a table, in which there are date wise quiz score of different users. I want to load top 5 scorers for every date.
Table sample create statement:
CREATE TABLE `subscriber_score` (
`msisdn` varchar(25) COLLATE utf8_unicode_ci NOT NULL,
`date` date NOT NULL,
`score` int(11) NOT NULL DEFAULT '0',
`total_questions_sent` int(11) NOT NULL DEFAULT '0',
`total_correct_answers` int(11) NOT NULL DEFAULT '0',
`total_wrong_answers` int(11) NOT NULL DEFAULT '0',
PRIMARY KEY (`msisdn`,`date`),
KEY `fk_subscriber_score_subscriber1` (`msisdn`),
CONSTRAINT `fk_subscriber_score_subscriber1` FOREIGN KEY (`msisdn`) REFERENCES `subscriber` (`msisdn`) ON DELETE NO ACTION ON UPDATE NO ACTION
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci;
Query which I have tried:
SELECT subscriber.msisdn AS msisdn,subscriber.name AS name,subscriber.gender AS gender,tmp2.score AS score,tmp2.date AS winning_date
FROM subscriber,
(SELECT msisdn,tmp.date,tmp.score
FROM subscriber_score,
(SELECT date,MAX(score) AS score
FROM subscriber_score
WHERE date > '2014-10-10' AND date < '2014-11-10' GROUP BY date)
tmp
WHERE subscriber_score.date=tmp.date AND subscriber_score.score=tmp.score)
tmp2
WHERE subscriber.msisdn=tmp2.msisdn ORDER BY winning_date
Actual output: Only one top scorer for every date is shown.
Wanted Output Top 5(or say 10) records for every date are required.
I think you can do this using variables to assign each row a row number, then filter the top 5 for each date.
SELECT s.name AS name,
s.gender AS gender,
s.msisdn,
ss.date,
ss.score
FROM ( SELECT ss.msisdn,
ss.score,
#r:= CASE WHEN ss.Date = #d THEN #r + 1 ELSE 1 END AS RowNum,
#d:= ss.date AS winning_date
FROM subscriber_score AS ss
CROSS JOIN (SELECT #d:= '', #r:= 0) AS v
WHERE ss.date > '2014-10-10'
AND ss.date < '2014-11-10'
ORDER BY ss.Date, ss.Score DESC
) AS ss
INNER JOIN Subscriber AS s
ON s.msisdn = ss.msisdn
WHERE ss.RowNum <= 5;
Example on SQL Fiddle
refer this query its not complete but hope it helps
SELECT SCORE
FROM table
WHERE date='somedate'
ORDER BY SCORE DESC LIMIT 5
select bc.msisdn msisdn,bc.name name,bc.gender gender,ab.score score,ab.date winning_date
(
select msisdn,date,score,
dense_rank() over (partition by date order by score desc) rnk
from subscriber_score
) ab,subscriber bc
where bc.msisdn=ab.msisdn and ab.rnk<=5
order by winning_date ;
This is how you can get solution of your problem in oracle sql.
try below
SELECT subscriber.msisdn AS msisdn,subscriber.name AS name,subscriber.gender AS gender,tmp2.score AS score,tmp2.date AS winning_date
FROM subscriber inner join
(select msisdn,date, score, ROW_NUMBER() OVER(PARTITION BY date ORDER BY score DESC) AS Row
FROM subscriber_score
WHERE date > '2014-10-10' AND date < '2014-11-10' GROUP BY date)
tmp
on subscriber.msisdn=tmp.msisdn and tmp.row<=5
I have a MySQL table like this
CREATE TABLE IF NOT EXISTS `vals` (
`DT` datetime NOT NULL,
`value` INT(11) NOT NULL,
PRIMARY KEY (`DT`)
);
the DT is unique date with time
data sample:
INSERT INTO `vals` (`DT`,`value`) VALUES
('2011-02-05 06:05:00', 300),
('2011-02-05 11:05:00', 250),
('2011-02-05 14:35:00', 145),
('2011-02-05 16:45:00', 100),
('2011-02-05 18:50:00', 125),
('2011-02-05 19:25:00', 100),
('2011-02-05 21:10:00', 125),
('2011-02-06 00:30:00', 150);
I need to get something like this:
start|end|value
NULL,'2011-02-05 06:05:00',300
'2011-02-05 06:05:00','2011-02-05 11:05:00',250
'2011-02-05 11:05:00','2011-02-05 14:35:00',145
'2011-02-05 14:35:00','2011-02-05 16:45:00',100
'2011-02-05 16:45:00','2011-02-05 18:50:00',125
'2011-02-05 18:50:00','2011-02-05 19:25:00',100
'2011-02-05 19:25:00','2011-02-05 21:10:00',125
'2011-02-05 21:10:00','2011-02-06 00:30:00',150
'2011-02-06 00:30:00',NULL,NULL
I tried the following query:
SELECT T1.DT AS `start`,T2.DT AS `stop`, T2.value AS value FROM (
SELECT DT FROM vals
) T1
LEFT JOIN (
SELECT DT,value FROM vals
) T2
ON T2.DT > T1.DT ORDER BY T1.DT ASC
but it returns to many rows (29 instead of 9) in result and I cold not find any way to limit this using SQL. Is it Possible in MySQL?
Use a subquery
SELECT
(
select max(T1.DT)
from vals T1
where T1.DT < T2.DT
) AS `start`,
T2.DT AS `stop`,
T2.value AS value
FROM vals T2
ORDER BY T2.DT ASC
You can also use a MySQL specific solution employing variables
SELECT CAST( #dt AS DATETIME ) AS `start` , #dt := DT AS `stop` , `value`
FROM (SELECT #dt := NULL) dt, vals
ORDER BY dt ASC
But you need to do it precisely
the ORDER by must be present otherwise the variables don't roll properly
the variable needs to be NULLified within the query using a subquery to set it, otherwise if you run it twice in a row, the 2nd time it will not start with NULL
You can use a server-side variable to simulate it:
select #myvar as start, end, value, #myvar := end as next_rows_start
from vals
Variables are interpreted from left-right in sequence, so the two references to #myvar (start and next_rows_start) will output with two different values.
Just remember to reset #myvar to null before and/or after the query, otherwise the second and subsequent runs will have a wrong first row:
select #myvar := null
This would be easier if the table had a running ID column which corresponds to the times in DT (same order). If you don't want to change the table you can use a temp:
drop table if exists temp;
CREATE TABLE temp (
`id` INT(11) AUTO_INCREMENT,
`DT` datetime NOT NULL,
`value` INT(11) NOT NULL,
PRIMARY KEY (`id`)
);
insert into temp (DT,value) select * from vals order by DT asc;
select t1.DT as `start`, t2.DT as `end`, t2.value
from temp t2
left join temp t1 ON t2.id = t1.id + 1;
Have looked through many examples, but i have not found a good answer for my specific situation.
Basically, I have table with an unique ID and a sequence for each ID.
I want to detect gaps in the sequence for each ID.
I managed to do it using a query, but the query takes the exact ID in the where clause, and given the number of rows in my table, this is not wanted.
CREATE TABLE `t` (
`target_id` varchar(100) NOT NULL,
`version` int(11) NOT NULL,
PRIMARY KEY (`target_id`,`version`)
);
insert into t values
('abc',1),
('abc',2),
('abc',3),
('abc',4),
('abc',5),
('abc',6),
('abc',7),
('abc',8),
('xyz',1),
('xyz',2),
('xyz',3),
('xyz',5),
('xyz',6);
http://sqlfiddle.com/#!9/8c280a/7
I tried with something like
select distinct target_id as target, gap_ends_at,gap_starts_at from
category_event e inner join
(
SELECT target_id as x, (t1.version + 1) as gap_starts_at,
(SELECT MIN(t3.version) -1 FROM category_event t3 WHERE t3.version >
t1.version and target_id=e.target) as gap_ends_at
FROM category_event t1
WHERE NOT EXISTS (SELECT t2.version FROM category_event t2 WHERE t2.version = t1.version + 1 and target_id=e.target) and target_id=e.target
HAVING gap_ends_at IS NOT NULL
) as x;
But that fails.
I expect a resultset like
id, gap_starts_at, gap_ends_at
Because this is MySQL, you can solve it just using vars:
MySQL 5.6 Schema Setup:
CREATE TABLE `t` (
`target_id` varchar(100) NOT NULL,
`version` int(11) NOT NULL,
PRIMARY KEY (`target_id`,`version`)
);
insert into t values
('abc',1),
('abc',2),
('abc',3),
('abc',4),
('abc',5),
('abc',6),
('abc',7),
('abc',8),
('xyz',1),
('xyz',2),
('xyz',3),
('xyz',5),
('xyz',6)
Query 1:
select
gap
from (
Select
target_id,
#v := case when #t <> target_id then 1 else #v+1 end,
#t := case when #t <> target_id then target_id else #t end,
case when #v <> version then CONCAT_WS(' ' ,
'gap for ',
target_id,
' start at',
CAST(#v-1 as CHAR(50)) ,
'ends at ',
CAST(version as CHAR(50))
)
else null end as gap,
#v := version
from
(select target_id, version
from t
order by target_id, version ) S,
( select #t:='', #v=0 ) I
) X
where gap > ''
Results:
| gap |
|-------------------------------------|
| gap for xyz start at 3 ends at 5 |