I have a database table like below
___________
id | speed
-----------
1 | 3
2 | 2
3 | 0
4 | 0
5 | 0
6 | 2
7 | 0
8 | 0
9 | 2
10 | 0
Now I want to get the records where speed is 0 but only from 3 to 5 which are continuous and greater than any other continuous records. I don't want 7,8 records or the 10th record. How can I achieve this?
Probably the fastest method is to use MySQL session variables to increment the "group" each time the speed changes, as you scan through the rows.
select n.*, #groupid:=IF(#prev_speed=speed,#groupid,#groupid+1) as groupid, #prev_speed:=speed
from (select #groupid:=0, #prev_speed=-1) _init
cross join n
order by id;
+----+-------+---------+--------------------+
| id | speed | groupid | #prev_speed:=speed |
+----+-------+---------+--------------------+
| 1 | 3 | 1 | 3 |
| 2 | 2 | 2 | 2 |
| 3 | 0 | 3 | 0 |
| 4 | 0 | 3 | 0 |
| 5 | 0 | 3 | 0 |
| 6 | 2 | 4 | 2 |
| 7 | 0 | 5 | 0 |
| 8 | 0 | 5 | 0 |
| 9 | 2 | 6 | 2 |
| 10 | 0 | 7 | 0 |
+----+-------+---------+--------------------+
Then using the above query as a derived table, calculate the lowest and highest id per group, and the count of rows. Sort the groups by the count of rows.
select min(id) as minid, max(id) as maxid, count(*) as count
from (
select n.*, #groupid:=IF(#prev_speed=speed,#groupid,#groupid+1) as groupid, #prev_speed:=speed
from (select #groupid:=0, #prev_speed=-1) _init
cross join n
order by id
) as t1
group by t1.groupid
order by count desc;
+-------+-------+-------+
| minid | maxid | count |
+-------+-------+-------+
| 3 | 5 | 3 |
| 7 | 8 | 2 |
| 1 | 1 | 1 |
| 2 | 2 | 1 |
| 6 | 6 | 1 |
| 9 | 9 | 1 |
| 10 | 10 | 1 |
+-------+-------+-------+
Then using the first row from the above as another derived table, join to the original table for the rows in the range from the min to max id.
select n.*
from (
select min(id) as minid, max(id) as maxid, count(*) as count
from (
select n.*, #groupid:=IF(#prev_speed=speed,#groupid,#groupid+1) as groupid, #prev_speed:=speed
from (select #groupid:=0, #prev_speed=-1) _init
cross join n
order by id
) as t1
group by t1.groupid
order by count desc limit 1
) as t2
inner join n on n.id between t2.minid and t2.maxid
+----+-------+
| id | speed |
+----+-------+
| 3 | 0 |
| 4 | 0 |
| 5 | 0 |
+----+-------+
Related
How to make more rows from the table? How to decompress table in MySQL?
+-----+-----------+
| num | frequency |
+-----+-----------+
| 0 | 3 |
| 1 | 1 |
| 2 | 2 |
| 3 | 1 |
+-----+-----------+
so that it looks like:
+-----+-----------+
| num | frequency |
+-----+-----------+
| 0 | 3 |
| 0 | 3 |
| 0 | 3 |
| 1 | 1 |
| 2 | 2 |
| 2 | 2 |
| 3 | 1 |
+-----+-----------+
I tried:
SELECT num,
ROW_NUMBER() OVER (PARTITION BY num ORDER BY frequency) AS x
FROM numbers
You could use aa join approach:
INSERT INTO yourTable (num, frequency)
SELECT t1.num, t1.frequency
FROM yourTable t1
INNER JOIN (SELECT 1 AS frequency UNION ALL SELECT 2 UNION ALL SELECT 3) t2
ON t2.frequency < t1.frequency;
You may add more frequency vales to the t2 subquery above as needed.
Let's say, in given num_table, there is a column, in which only numbers from 1 to 35 are stored.
Code for count nums in last 25rows is:
select num, count(*)
from (select C_1 as num from num_table order by id desc limit 25) n
group by num
order by num asc;
Result:
| num | count(*) |
|------|----------|
| 2 | 1 |
| 3 | 1 |
| 4 | 1 |
| 5 | 2 |
| 10 | 1 |
| 11 | 1 |
| 12 | 1 |
| 15 | 1 |
| 16 | 2 |
| 17 | 1 |
| 20 | 1 |
| 21 | 1 |
| 22 | 1 |
| 23 | 1 |
| 25 | 1 |
| 28 | 2 |
| 29 | 2 |
| 30 | 1 |
| 32 | 2 |
|------|----------|
How to get a result, where nums from 1 to 35 - which occured 0 times within last 25 rows - will be also displayed?
Example of desired result:
| num | count(*) |
|------|----------|
| 1 | 0 |
| 2 | 1 |
| 3 | 1 |
| 4 | 1 |
| 5 | 2 |
| 6 | 0 |
| 7 | 0 |
| 8 | 0 |
| 9 | 0 |
| 10 | 1 |
| ... | ... |
| 35 | 0 |
Maybe the quickest way is to make your existing query as sub-query and LEFT JOIN your num_table with it like :
SELECT A.C_1, IFNULL(cnt,0) total_count
FROM num_table A
LEFT JOIN
(SELECT num, COUNT(*) cnt
FROM (SELECT C_1 AS num FROM num_table ORDER BY id DESC LIMIT 25) n
GROUP BY num) B
ON A.C_1=B.num
GROUP BY A.C_1, cnt
ORDER BY A.C_1 ASC;
Here's a fiddle for reference:
https://dbfiddle.uk/?rdbms=mysql_5.7&fiddle=3ced94d698fd8a55a8ad07a9d3b42f3d
And by the way, the current result you're showing is only 24 rows despite you did LIMIT 25 in the first sub-query. So in my example fiddle, the result is slightly different.
Here is another way to solve your problem.
In this solution, first, you need a table with numbers between 1 and 35, but only for the query, so then you can left join (because with a left join you can have also 0 counter values) it with your existent num_table.
You can do it like this:
WITH RECURSIVE numbers(id) AS (
SELECT 1 as id
UNION ALL
SELECT id+1 FROM numbers WHERE id < 35
)
SELECT numbers.id AS num, count(nt.id) AS total
FROM numbers
LEFT JOIN (SELECT C_1 FROM num_table ORDER BY id DESC LIMIT 25) nt ON (nt.C_1 = numbers.id)
GROUP BY numbers.id
How to select rows which are not defined? Like row 2 have undefined day 3 and row 3 have undefined day 1. I want them to be 0 in result set.
+----+-----+-------+
| id | day | count |
+----+-----+-------+
| 1 | 1 | 262 |
| 1 | 2 | 685 |
| 1 | 3 | 984 |
| 2 | 1 | 692 |
| 2 | 2 | 962 |
| 3 | 2 | 355 |
| 3 | 3 | 741 |
+----+-----+-------+
EDIT:
I want select count from days 1, 2 and 3 (not whole table) and display 0 on undefined day.
We can get all unique id values in a Derived Table.
For day, you seem to want only 1,2 and 3 only. So we can directly consider these values only using UNION ALL.
CROSS JOIN between them to get all possible combinations.
LEFT JOIN from all_combinations table to the main table on id and day.
We can use Coalesce() function to consider 0 value for count, for the cases where there is no matching row in the main table
Try the following:
SELECT all_combinations.id,
all_combinations.day,
COALESCE(t.count, 0) AS count
FROM
(
SELECT ids.id, days.day
FROM
(SELECT DISTINCT id FROM your_table) AS ids
CROSS JOIN
(SELECT 1 AS day UNION ALL SELECT 2 UNION ALL SELECT 3) AS days
) AS all_combinations
LEFT JOIN your_table AS t
ON t.id = all_combinations.id AND
t.day = all_combinations.day
Result:
| id | day | count |
| --- | --- | ----- |
| 1 | 1 | 262 |
| 2 | 1 | 692 |
| 3 | 1 | 0 |
| 1 | 2 | 685 |
| 2 | 2 | 962 |
| 3 | 2 | 355 |
| 1 | 3 | 984 |
| 2 | 3 | 0 |
| 3 | 3 | 741 |
View on DB Fiddle
I have the next query:
SELECT a.id, a.brand_id
FROM articles a
WHERE a.deleted=0 AND a.brand_id IN (5,6)
LIMIT 4
How can I get 4 articles from all the brand_id's named at the IN sentence? For example, I would like to get 2 articles from brand_id=5 and 2 articles from brand_id=6
You can use union all
(
SELECT a.id, a.brand_id
FROM articles a
WHERE a.deleted=0 AND a.brand_id = 5 limit 2
)
union all
(
SELECT a.id, a.brand_id
FROM articles a
WHERE a.deleted=0 AND a.brand_id = 6 limit 2
)
UPDATE , this could be achieved using m-per-group logic and one way would be as -
Consider the table
mysql> select * from articles ;
+------+----------+---------+
| id | brand_id | deleted |
+------+----------+---------+
| 1 | 5 | 0 |
| 2 | 6 | 0 |
| 3 | 2 | 0 |
| 4 | 4 | 1 |
| 5 | 5 | 0 |
| 6 | 5 | 1 |
| 7 | 5 | 0 |
| 8 | 6 | 0 |
| 9 | 4 | 0 |
| 10 | 4 | 0 |
| 11 | 4 | 1 |
| 12 | 6 | 0 |
| 13 | 5 | 1 |
| 14 | 5 | 0 |
+------+----------+---------+
So with the query below will return n-per-group as
select
id,
brand_id
from (
select
id,
brand_id,
#r := if(#brand = brand_id,#r+1,1) as row_num,
#brand:= brand_id
from articles,(select #r:=0,#brand:='')rr
where
brand_id in (4,5,6)
and deleted = 0
order by brand_id
)x
where x.row_num <=2 limit 6;
+------+----------+
| id | brand_id |
+------+----------+
| 9 | 4 |
| 10 | 4 |
| 1 | 5 |
| 5 | 5 |
| 2 | 6 |
| 8 | 6 |
+------+----------+
6 rows in set (0.00 sec)
So here the limit will be always number of items inside IN clause * 2
SELECT a.id, a.brand_id
FROM articles a
WHERE a.deleted=0 AND a.brand_id=5
LIMIT 2
UNION ALL
SELECT a.id, a.brand_id
FROM articles a
WHERE a.deleted=0 AND a.brand_id=6
LIMIT 2
Suppose I have such a table:
+-----+---------+-------+
| ID | TIME | DAY |
+-----+---------+-------+
| 1 | 1 | 1 |
| 2 | 2 | 1 |
| 3 | 3 | 1 |
| 1 | 1 | 2 |
| 2 | 2 | 2 |
| 3 | 3 | 2 |
| 1 | 1 | 3 |
| 2 | 2 | 3 |
| 3 | 3 | 3 |
| 1 | 1 | 4 |
| 2 | 2 | 4 |
| 3 | 3 | 4 |
| 1 | 1 | 5 |
| 2 | 2 | 5 |
| 3 | 3 | 5 |
+-----+---------+-------+
I want to fetch a table which represents 2 IDs which got the largest sum of TIME within the last 3 days (means from 3 to 5 in a DAY column)
So the correct result would be:
+-----+---------+
| ID | SUM |
+-----+---------+
| 3 | 9 |
| 2 | 6 |
+-----+---------+
The original table is much larger and more complex. So i need a generic approach.
Thanks in advance.
And so I just learned that MySQL used LIMIT instead of TOP...
fiddle
CREATE TABLE tbl (ID INT,tm INT,dy INT);
INSERT INTO tbl (id, tm, dy) VALUES
(1,1,1)
,(2,2,1)
,(3,3,1)
,(1,1,2)
,(1,1,1)
SELECT ID
,SUM(SumTimeForDay) SumTimeFromLastThreeDays
FROM (SELECT ID
,SUM(tm) SumTimeForDay
FROM tbl
GROUP BY ID, dy
HAVING dy > MAX(dy) -3) a
GROUP BY id
ORDER BY SUM(SumTimeForDay) DESC
LIMIT 2
select t1.`id`, sum(t1.`time`) as `sum`
from `table` t1
inner join ( select distinct `day` from `table` order by `day` desc limit 3 ) t2
on t2.`da`y = t1.`day`
group by t1.`id`
order by sum(t1.`time`) desc
limit 2