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
Related
I am trying to select all but the last row of grouped data from a table.
+----+--------+--------+ +----+--------+--------+
| id | userID | amount | | id | userID | amount |
+----+--------+--------+ +----+--------+--------+
| 1 | 20 | 400 | | 1 | 20 | 400 |
| 2 | 20 | 200 | | 2 | 20 | 200 |
| 3 | 21 | 100 | => | 3 | 21 | 100 |
| 4 | 11 | 500 | | 4 | 11 | 500 |
| 5 | 11 | 250 | | 6 | 21 | 50 |
| 6 | 21 | 50 |
| 7 | 20 | 100 |
| 8 | 21 | 200 |
+----+--------+--------+
I have tried to use the query
SELECT *
FROM table
WHERE userID != (SELECT MAX(userID) FROM table)
GROUP
BY userID
but it only fetches one unique row of data even though there are more left
You have not aggreagtion function so you don't need group by
SELECT *
FROM table
WHERE userID != (
SELECT MAX(userID) FROM table
)
This can happen with mysql version <5.7 for mysql version > 5.7 (by default setting) this use of group by raise an error
E.g....
SELECT a.*
FROM my_table a
LEFT
JOIN
( SELECT MAX(id) id
FROM my_table
GROUP
BY userid
) b
ON b.id = a.id
WHERE b.id IS NULL
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 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 |
+----+-------+
i'm build an exercises web app and i'm working with two tables like this:
Table 1: weekly_stats
| id | code | type | date | time |
|----|--------------|--------------------|------------|----------|
| 1 | CC | 1 | 2015-02-04 | 19:15:00 |
| 2 | CC | 2 | 2015-01-28 | 19:15:00 |
| 3 | CPC | 1 | 2015-01-26 | 19:15:00 |
| 4 | CPC | 1 | 2015-01-25 | 19:15:00 |
| 5 | CP | 1 | 2015-01-24 | 19:15:00 |
| 6 | CC | 1 | 2015-01-23 | 19:15:00 |
| .. | ... | ... | ... | ... |
Table 2: global_stats
| id | exercise_number |correct | wrong |
|----|-----------------|--------|-----------|
| 1 | 138 | 1 | 0 |
| 2 | 246 | 1 | 0 |
| 3 | 988 | 1 | 10 |
| 4 | 13 | 5 | 0 |
| 5 | 5 | 4 | 7 |
| 6 | 5 | 4 | 7 |
| .. | ... | ... | ... |
What i would like is to get MAX(correct-wrong) and MIN(correct-wrong) and now i'm working with this query:
SELECT
exercise_number,
date,
time
FROM weekly_stats AS w JOIN global_stats AS g
ON w.id=g.id
WHERE correct - wrong = (SELECT MAX(correct - wrong) from global_stats)
UNION
SELECT
exercise_number,
date,
time
FROM weekly_stats AS w JOIN global_stats AS g
ON w.id=g.id
WHERE correct - wrong = (SELECT MIN(correct - wrong) from global_stats);
This query is working good, except for one thing: when "WHERE correct - wrong = (SELECT MIN(correct - wrong)[...]" selects more than one row, the row selected is the first but i would like to have returned the most recent (in other words: ordered by datetime(date, time)). Is it possible?
Thanks!
I think you can solve it like this:
SELECT * FROM (
SELECT
1 as sort_column,
exercise_number,
date,
time
FROM weekly_stats AS w JOIN global_stats AS g
ON w.id=g.id
WHERE correct - wrong = (SELECT MAX(correct - wrong) from global_stats)
ORDER BY date DESC, time DESC
LIMIT 1 ) as a
UNION
SELECT * FROM (
SELECT
2 as sort_column,
exercise_number,
date,
time
FROM weekly_stats AS w JOIN global_stats AS g
ON w.id=g.id
WHERE correct - wrong = (SELECT MIN(correct - wrong) from global_stats)
ORDER BY date DESC, time DESC
LIMIT 1) as b
ORDER BY sort_column;
Here is the documentation about how UNION works.
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