BACKGROUND
I am working on a project where I need to capture the 30 day average of values for some id# then use this average to determine if some new value is anomalous. For the purposes of this question, we can assume I only need a 10-day average since the solutions are probably similar. I currently have two tables: history which holds the actual values that I have recorded for specific id# numbers by day but can have some missing days and calendar a date table that has all of the days that I need in my 30 day average.
create table history (
day date not null,
id bigint not null,
category int not null,
value int not null default '0',
primary key (day, id, category),
key category (category)
);
create table calendar (
day date not null primary key
);
I would like to take the existing data that I have in the history table and fill in the missing data by either copying forward a previous value or copying back a forward value. E.g given this data in the history table:
+------------+-----------+----------+-------+
| day | id | category | value |
+------------+-----------+----------+-------+
| 2015-02-19 | 159253663 | 364 | 212 |
| 2015-02-20 | 159253663 | 364 | 211 |
| 2015-02-22 | 159253663 | 364 | 199 |
| 2015-02-23 | 159253663 | 364 | 192 |
| 2015-02-24 | 159253663 | 364 | 213 |
+------------+-----------+--------+---------+
Note: there is no entry for 2015-02-21
I would like to fill in enough data so that I can compute the 10-day average i.e. copy the oldest value (2015-02-19) back to the beginning of my 10-day range then fill in the missing 2015-02-21 value with the previous day's value. The result would be this (stars mark the newly added rows):
+------------+-----------+----------+-------+
| day | id | category | value |
+------------+-----------+----------+-------+
| 2015-02-14 | 159253663 | 364 | 212 | *
| 2015-02-15 | 159253663 | 364 | 212 | *
| 2015-02-16 | 159253663 | 364 | 212 | *
| 2015-02-17 | 159253663 | 364 | 212 | *
| 2015-02-18 | 159253663 | 364 | 212 | *
| 2015-02-19 | 159253663 | 364 | 212 |
| 2015-02-20 | 159253663 | 364 | 211 |
| 2015-02-21 | 159253663 | 364 | 211 | *
| 2015-02-22 | 159253663 | 364 | 199 |
| 2015-02-23 | 159253663 | 364 | 192 |
| 2015-02-24 | 159253663 | 364 | 213 |
+------------+-----------+--------+---------+
ATTEMPT
My initial thought was to left join to a calendar table that has the date ranges I need, when I do that I get something like this:
select c.day, h.id, h.value
from calendar c
left join history h using (day)
where c.day between curdate() - interval 10 day and curdate();
+------------+-----------+----------+-----------+
| day | id | category | value |
+------------+-----------+----------+-----------+
| 2015-02-14 | NULL | NULL | NULL |
| 2015-02-15 | NULL | NULL | NULL |
| 2015-02-16 | NULL | NULL | NULL |
| 2015-02-17 | NULL | NULL | NULL |
| 2015-02-18 | NULL | NULL | NULL |
| 2015-02-19 | 159253663 | 364 | 212 |
| 2015-02-19 | 159253690 | 364 | 222 |
| 2015-02-20 | 159253663 | 364 | 211 |
| 2015-02-20 | 159253690 | 364 | 221 |
| 2015-02-21 | NULL | NULL | NULL |
| 2015-02-22 | 159253663 | 364 | 199 |
| 2015-02-22 | 159253690 | 364 | 209 |
| 2015-02-23 | 159253663 | 364 | 192 |
| 2015-02-23 | 159253690 | 364 | 202 |
| 2015-02-24 | 159253663 | 364 | 213 |
| 2015-02-24 | 159253690 | 364 | 213 |
+------------+-----------+----------+-----------+
I am not sure where to proceed from this point, because I need an entry for each day for each distinct id#. This join only returns a single day if they are missing. I am looking for a better approach. I would like to push as much of the work as possible on the MySQL server, but can do some things programmaticaly. Any/all ideas or suggestions are welcome.
Here is a SQLFiddle that has the DDL definitions I am testing with: http://sqlfiddle.com/#!2/cc206/2
The following uses an # variable and in-statement assignments to roll backward the value (and id):
SET #lastval = 0, #lastid = 0;
SELECT c.day, #lastid := COALESCE(h.id,#lastid) id, #lastval := COALESCE(h.value,#lastval) VALUE, h.id id1,h.value v1
FROM (SELECT DISTINCT c.day,h.id FROM history h, calendar c) c
LEFT JOIN history h ON h.day = c.day AND h.id = c.id
WHERE c.day BETWEEN CURDATE() - INTERVAL 10 DAY AND CURDATE()
ORDER BY COALESCE(h.id,#lastid),c.day DESC
The sub-query seems to be necessary, never been too sure why (some do, some don`t).
If it looks like the results are in the wrong order you might have to add :
SET optimizer_switch='block_nested_loop=off';
before the statement as the block nested loop optimisation can mess with the Order mysql uses when collecting the rows.
Related
I'm developing a dashboard with graphs.
What's the problem?
Let's say, that I have a table with the folowing sctructure:
+-------+------+------+-----+---------+-------+
| Field | Type | Null | Key | Default | Extra |
+-------+------+------+-----+---------+-------+
| total | int | NO | | NULL | |
| new | int | NO | | NULL | |
| date | date | YES | | NULL | |
+-------+------+------+-----+---------+-------+
where total stands for Total Members and new for New Members (date is a date of course - in format: yyyy-mm-dd).
Example of columns:
+-------+-------+------------+
| total | new | date |
+-------+-------+------------+
| 3450 | 21 | 2021-11-06 |
| 3650 | 200 | 2021-11-07 |
| 3694 | 34 | 2021-11-08 |
| 3520 | 26 | 2021-11-09 |
| 3399 | -321 | 2021-11-10 |
| 3430 | 31 | 2021-11-11 |
| 3450 | 20 | 2021-11-12 |
| 3410 | -40 | 2021-11-13 |
| 3923 | 513 | 2021-11-14 |
| 4019 | 96 | 2021-11-15 |
| 4119 | 100 | 2021-11-16 |
| 4000 | -119 | 2021-11-17 |
| 3000 | -1000 | 2021-11-18 |
| 3452 | 452 | 2021-11-19 |
| 3800 | 348 | 2021-11-20 |
| 3902 | 102 | 2021-11-21 |
| 4050 | 148 | 2021-11-22 |
+-------+-------+------------+
And there are a few options, where the dashboard user can select between 2 dates and type of graphs (daily, weekly, monthly).
Image, that describes the Setting options.
The Point
I need to take these 2 dates and somehow get all data from the database between the given term. Well, but that's not all. The Daily, Weekly and Monthly option means, that graphs will be showing average newcoming and total members per every week (so if I will grab 7 days from the database, I need to create an average - and do this between all these days / weeks / months in a term), if it's weekly, etc. So the final graph will be showing something like:
250 new 20 new 31 new
1000 total 1020 total 1051 total
Nov 7 Nov 14 Nov 21
etc...
More informations:
Ubuntu: 21.04
MySQL: 8.0.27
PHP: 7.4.23
Apache: 2.4.46
Does anyone have any ideas?
I don't get where your numbers come from
But your query would go like this.
For the month you need to group by MONTHof course
CREATE TABLE members (
`total` INTEGER,
`new` INTEGER,
`date` date
);
INSERT INTO members
(`total`, `new`, `date`)
VALUES
('3450', '21', '2021-11-06'),
('3650', '200', '2021-11-07'),
('3694', '34', '2021-11-08'),
('3520', '26', '2021-11-09'),
('3399', '-321', '2021-11-10'),
('3430', '31', '2021-11-11'),
('3450', '20', '2021-11-12'),
('3410', '-40', '2021-11-13'),
('3923', '513', '2021-11-14'),
('4019', '96', '2021-11-15'),
('4119', '100', '2021-11-16'),
('4000', '-119', '2021-11-17'),
('3000', '-1000', '2021-11-18'),
('3452', '452', '2021-11-19'),
('3800', '348', '2021-11-20'),
('3902', '102', '2021-11-21'),
('4050', '148', '2021-11-22');
SELECT `new`,sumtotal, `date` FROM members m
INNER JOIN (SELECT SUM(`new`) sumtotal, MIN(`date`) mindate FROM members GROUP BY WEEK(`date`)) t1
ON m.`date`= t1.mindate
WHERE m.`date` BETWEEN '2021-11-07' AND '2021-11-22'
new | sumtotal | date
--: | -------: | :---------
200 | -50 | 2021-11-07
513 | 390 | 2021-11-14
102 | 250 | 2021-11-21
db<>fiddle here
I have two table one contains updated_at (can be more than one row) datetime And second contains started_date and stopped_date(one or more records).
I want select updated_at date which should not in between started_date and stopped_date.
Thanks in advance.
I saw the other Question "Check overlap of date ranges in MySQL".
But this not what I want.
user_location
+---+-----+-----+---------------------+
|id | lat | lon | updated_date |
+---+-----+-----+---------------------+
| 1 |16.45|75.45|2018-01-09 12:50:57 |
| 2 |16.85|75.15|2018-01-09 12:53:45 |
| 3 |16.78|75.25|2018-01-09 12:55:48 |
| 4 |16.43|75.35|2018-01-09 13:57:35 |
| 5 |16.48|75.47|2018-01-09 14:59:30 |
| 6 |16.49|75.49|2018-01-10 05:59:58 |
| 7 |16.50|75.50|2018-01-10 07:35:15 |
+---+-----+-----+---------------------+
location_blocked_datetime
+---+--------------------+---------------------+
|id | start_date | stopped_date |
+---+--------------------+---------------------+
| 1 |2018-01-09 05:55:48 | 2018-01-09 07:55:48 |
| 2 |2018-01-09 12:51:48 | 2018-01-09 12:56:48 |
| 3 |2018-01-10 04:30:48 | 2018-01-04 06:55:48 |
+---+--------------------+---------------------+
I want select location from user_location table where updated_date should not be there in start_date and stopped_date.start_date and stopped dates are not fixed and contain more than 1 records
The result of above query should look like this:-
If I want to select locations On 2018-01-09
Result Of Above Query
+---+-----+-----+---------------------+
|id | lat | lon | updated_date |
+---+-----+-----+---------------------+
| 1 |16.45|75.45|2018-01-09 12:50:57 |
| 2 |16.43|75.35|2018-01-09 13:57:35 |
| 3 |16.48|75.47|2018-01-09 14:59:30 |
+---+-----+-----+---------------------+
So I get this right, that the tables are not actually connected and you want to get the records where updated_date is not between any start_date - stopped_date range?
If so, then use not exists like this:
SELECT
l.*
FROM
user_location l
WHERE DATE(l.updated_date) = '2018-01-09'
and not exists (select 1 from location_blocked_datetime
where l.updated_date between start_date and stopped_date)
| id | lat | lon | updated_date |
|----|-----|-----|---------------------|
| 1 | 16 | 75 | 2018-01-09 12:50:57 |
| 4 | 16 | 75 | 2018-01-09 13:57:35 |
| 5 | 16 | 75 | 2018-01-09 14:59:30 |
see it working live in an sqlfiddle
In a sample project i wanted to display data in such a way that based on dates the records for same student comes in additional columns.
mysql> desc sch_student;
+----------------+--------------+
| Field | Type |
+----------------+--------------+
| s_first_name | varchar(128) |
| s_last_name | varchar(128) |
| rollcode | int(8) |
| regnum | int(8) |
| in_time | datetime |
| out_time | datetime |
| total_time | int(8) |
+----------------+--------------+
for below query i am getting sample output like below , my expected output is something i am unable to get. I tried Sample join but it didn't work.
mysql> select * from sch_student;
+-------------------+---------------+--------------+-----------+---------------------+---------------------+----------------+
| s_first_name | s_last_name | rollcode | regnum | in_time | out_time | total_time |
+-------------------+---------------+--------------+-----------+---------------------+---------------------+----------------+
| Suzan | Matsuo | 8900 | 2897 | 2017-12-02 22:30:11 | 2017-12-02 22:30:11 | 00:17:00 |
| Scottie | Ogletree | 5624 | 5627 | 2017-12-02 16:40:01 | 2017-12-02 16:40:05 | 00:26:04 |
| Cynthia | Zimmerman | 3107 | 6348 | 2017-12-02 16:35:01 | 2017-12-02 16:35:01 | 00:59:89 |
| Ricardo | Shurtliff | 3072 | 261 | 2017-12-02 15:33:01 | 2017-12-02 15:33:01 | 00:16:55 |
| Elizabeth | Milligan | 4722 | 3233 | 2017-12-02 15:06:00 | 2017-12-02 15:10:33 | 00:14:33 |
+-------------------+---------------+--------------+-----------+---------------------+---------------------+----------------+
Expected output is something like below
+-------------------+---------------+--------------+-----------+---------------------+---------------------+----------------+--------------+-----------+---------------------+---------------------+----------------+
| s_first_name | s_last_name | Today's Meeting | Day Before Yesterday's Meeting |
| | rollcode | regnum | in_time | out_time | total_time | rollcode | regnum | in_time | out_time | total_time |
+-------------------+---------------+--------------+-----------+---------------------+---------------------+----------------+--------------+-----------+---------------------+---------------------+----------------+
| Suzan | Matsuo | 8900 | 2897 | 2017-12-02 22:30:11 | 2017-12-02 22:30:11 | 00:17:00 | 8900 | 2897 | 2017-11-30 12:30:11 | 2017-11-30 12:50:11 | 00:17:00 |
| Scottie | Ogletree | 5624 | 5627 | 2017-12-02 16:40:01 | 2017-12-02 16:40:05 | 00:26:04 | 5624 | 5627 | 2017-11-30 18:40:01 | 2017-11-30 19:33:05 | 00:26:04 |
| Cynthia | Zimmerman | 3107 | 6348 | 2017-12-02 16:35:01 | 2017-12-02 16:35:01 | 00:59:89 | 3107 | 6348 | 2017-11-30 13:35:01 | 2017-11-30 14:15:01 | 00:59:89 |
| Ricardo | Shurtliff | 3072 | 261 | 2017-12-02 15:33:01 | 2017-12-02 15:33:01 | 00:16:55 | 3072 | 261 | 2017-11-30 19:33:01 | 2017-11-30 20:33:01 | 00:16:55 |
| Elizabeth | Milligan | 4722 | 3233 | 2017-12-02 15:06:00 | 2017-12-02 15:10:33 | 00:14:33 | 4722 | 3233 | 2017-11-30 18:06:00 | 2017-11-30 19:10:33 | 00:14:33 |
+-------------------+---------------+--------------+-----------+---------------------+---------------------+----------------+--------------+-----------+---------------------+---------------------+----------------+
I tried below join and it's not returning expected output. Is it possible to display conditional column from table?
select * from
(
(select s_first_name,s_last_name,rollcode,regnum,in_time from sch_student where sch_student.in_time BETWEEN CURDATE()- INTERVAL 1 DAY AND CURDATE() ) As TD,
(select s_first_name,s_last_name,rollcode,regnum,in_time from sch_student where sch_student.in_time BETWEEN CURDATE()- INTERVAL 3 DAY AND CURDATE() ) As DBYS
) ;
I think this is what you need. I haven't tested it. Basically the query gets todays data LEFT joins to the day before yesterday's data. I assumed regnum and rollcode makes your primary key. Change if that isnt the case.
SELECT TD.* , DBYS.*
FROM (
SELECT s_first_name
,s_last_name
,rollcode
,regnum
,in_time
FROM sch_student
WHERE sch_student.in_time BETWEEN CURDATE() - INTERVAL 1 DAY
AND CURDATE()) AS TD
LEFT JOIN (
SELECT s_first_name
,s_last_name
,rollcode
,regnum
,in_time
FROM sch_student
WHERE sch_student.in_time BETWEEN CURDATE() - INTERVAL 3 DAY
AND CURDATE() - INTERVAL 2 DAY) AS DBYS
ON (TD.regnum = DBYS.regnum AND
TD.rollcode = DBYS.rollcode);
If you want to get info for today's meeting and the "day-before-yesterday's" meeting, try using a LEFT JOIN instead:
SELECT s_first_name, s_last_name, rollcode, regnum, in_time
FROM sch_student AS sch_today
LEFT JOIN sch_student AS sch_daybeforeyesterday ON
sch_today.<PK_FIELD> = sch_daybeforeyesterday.<PK_FIELD> AND
sch_daybeforeyesterday.in_time BETWEEN CURDATE()- INTERVAL 3 DAY AND CURDATE() - INTERVAL 2 DAY
WHERE sch_student.in_time BETWEEN CURDATE()- INTERVAL 1 DAY AND CURDATE()
This will give you all rows with "in_time" within the last 0-24 hours. For each of those rows, it will return any corresponding rows with "in_time" within the 48-72 hours.
I have a ratings table, where each user can add one rating a day. But each user might miss several days between ratings.
I'd like to get the average rating for each user_id's first 7 entries of created_at.
My table:
mysql> desc entries;
+------------+------------------+------+-----+---------+----------------+
| Field | Type | Null | Key | Default | Extra |
+------------+------------------+------+-----+---------+----------------+
| id | int(10) unsigned | NO | PRI | NULL | auto_increment |
| rating | tinyint(4) | NO | | NULL | |
| user_id | int(10) unsigned | NO | MUL | NULL | |
| created_at | timestamp | YES | | NULL | |
+------------+------------------+------+-----+---------+----------------+
Ideally I'd just get something like:
+------------+------------------+
| day | average_rating |
+------------+------------------+
| 1 | 2.53 |
+------------+------------------+
| 2 | 4.30 |
+------------+------------------+
| 3 | 3.67 |
+------------+------------------+
| 4 | 5.50 |
+------------+------------------+
| 5 | 7.23 |
+------------+------------------+
| 6 | 6.98 |
+------------+------------------+
| 7 | 7.22 |
+------------+------------------+
The closest I've been able to get is:
SELECT rating, user_id, created_at FROM entries ORDER BY user_id asc, created at desc
Which isn't very close at all...
Is it even possible? Will the performance be terrible? It's something that would need to run every time a web page is loaded, so would it be better to just run this once a day and save the results? (to another table!?)
edit - second attempt
Working towards a solution, I think this would get the rating for each user's first day:
select rating from entries where user_id in
(select user_id from entries order by created_at limit 1);
But I get:
ERROR 1235 (42000): This version of MySQL doesn't yet support 'LIMIT & IN/ALL/ANY/SOME subquery'
So now I'm going to play around with JOIN to see if that helps.
edit - third attempt, getting closer
I found this stackoverflow post, which is closer to what I want.
select e1.* from entries e1 left join entries e2
on (e1.user_id = e2.user_id and e1.created_at > e2.created_at)
where e2.id is null;
It gets the rating for the first day for each user.
Next step is to work out how to get days 2 to 7. I can't use 1.created_at > e2.created_at for that, so I'm really confused now.
edit - fourth attempt
Okay, I think it's not possible. Once I worked out how to turn off 'full group by' mode, I realised I'll probably need to use a subquery with limit <user_id>, <day_num>, for which I get:
ERROR 1235 (42000): This version of MySQL doesn't yet support 'LIMIT & IN/ALL/ANY/SOME subquery'
My current method is to just get the entire table, and use PHP to calculate the average for each day.
If I understand correctly you want to take the last 7 ratings the user gave, ordered by the date they gave the rating. The last 7 ratings of one user may fall on different days to another user, however they will be averaged together regardless of date.
First we need to order the data by user and date and give each user their own incrementing row count. I do this by adding two variables, one for the last user id and one for the row number:
select e.created_at,
e.rating,
if(#lastUser=user_id,#row := #row+1, #row:=1) as row,
#lastUser:= e.user_id as user_id
from entries e,
( select #row := 0, #lastUser := 0 ) vars
order by e.user_id asc,
e.created_at desc;
If the previous user_id is different we reset the row counter to 1. The result from this is:
+---------------------+--------+------+---------+
| created_at | rating | row | user_id |
+---------------------+--------+------+---------+
| 2017-01-10 00:00:00 | 1 | 1 | 1 |
| 2017-01-09 00:00:00 | 1 | 2 | 1 |
| 2017-01-08 00:00:00 | 1 | 3 | 1 |
| 2017-01-07 00:00:00 | 1 | 4 | 1 |
| 2017-01-06 00:00:00 | 1 | 5 | 1 |
| 2017-01-05 00:00:00 | 1 | 6 | 1 |
| 2017-01-04 00:00:00 | 1 | 7 | 1 |
| 2017-01-03 00:00:00 | 1 | 8 | 1 |
| 2017-01-02 00:00:00 | 1 | 9 | 1 |
| 2017-01-01 00:00:00 | 1 | 10 | 1 |
| 2017-01-13 00:00:00 | 1 | 1 | 2 |
| 2017-01-11 00:00:00 | 1 | 2 | 2 |
| 2017-01-09 00:00:00 | 1 | 3 | 2 |
| 2017-01-07 00:00:00 | 1 | 4 | 2 |
| 2017-01-05 00:00:00 | 1 | 5 | 2 |
| 2017-01-03 00:00:00 | 1 | 6 | 2 |
| 2017-01-01 00:00:00 | 1 | 7 | 2 |
| 2017-01-13 00:00:00 | 1 | 1 | 3 |
| 2017-01-01 00:00:00 | 1 | 2 | 3 |
| 2017-01-03 00:00:00 | 1 | 1 | 4 |
| 2017-01-01 00:00:00 | 1 | 2 | 4 |
| 2017-01-02 00:00:00 | 1 | 1 | 5 |
+---------------------+--------+------+---------+
We now simply wrap this in another statement to select the avg where the row number is less than or equal to seven.
select e1.row day, avg(e1.rating) avg
from (
select e.created_at,
e.rating,
if(#lastUser=user_id,#row := #row+1, #row:=1) as row,
#lastUser:= e.user_id as user_id
from entries e,
( select #row := 0, #lastUser := 0 ) vars
order by e.user_id asc,
e.created_at desc) e1
where e1.row <=7
group by e1.row;
This outputs:
+------+--------+
| day | avg |
+------+--------+
| 1 | 1.0000 |
| 2 | 1.0000 |
| 3 | 1.0000 |
| 4 | 1.0000 |
| 5 | 1.0000 |
| 6 | 1.0000 |
| 7 | 1.0000 |
+------+--------+
I have put together a small sql query which brings data from one table and sorts it under new column names. The sql looks like this:
SELECT course_id AS course, NOW() as datum,
(SELECT COUNT(*) FROM users_courses WHERE course_id = course) AS antal_registrerade,
(SELECT COUNT(*) FROM users_courses WHERE status = 1 AND course_id = course) AS antal_aktiva,
(SELECT COUNT(*) FROM users_courses WHERE status = 3 AND course_id = course) AS antal_avklarade
FROM users_courses GROUP BY course_id
The above query returns the following:
| course | datum | antal_registrerade | antal_aktiva | antal_avklarade |
-----------------------------------------------------------------------------------------
| 31 | 2016-01-12 16:24:58 | 142 | 19 | 83 |
| 38 | 2016-01-12 16:24:58 | 826 | 45 | 49 |
| 39 | 2016-01-12 16:24:58 | 2 | 2 | NULL |
| 43 | 2016-01-12 16:24:58 | 169 | 29 | 32 |
| 44 | 2016-01-12 16:24:58 | 11 | 4 | 2 |
| 45 | 2016-01-12 16:24:58 | 67 | 8 | 7 |
| 46 | 2016-01-12 16:24:58 | 2 | 1 | 1 |
All good right? Just like I wanted it. BUT when I save this query as a view and run that the result is different. I get the same data for every row, except for the course and datum columns.
| course | datum | antal_registrerade | antal_aktiva | antal_avklarade |
-----------------------------------------------------------------------------------------
| 31 | 2016-01-12 16:24:58 | 1219 | 108 | 174 |
| 38 | 2016-01-12 16:24:58 | 1219 | 108 | 174 |
| 39 | 2016-01-12 16:24:58 | 1219 | 108 | 174 |
| 43 | 2016-01-12 16:24:58 | 1219 | 108 | 174 |
| 44 | 2016-01-12 16:24:58 | 1219 | 108 | 174 |
| 45 | 2016-01-12 16:24:58 | 1219 | 108 | 174 |
| 46 | 2016-01-12 16:24:58 | 1219 | 108 | 174 |
Anyone have any idea why this is? The sql found in the saved view looks like this:
SELECT `database`.`users_courses`.`course_id` AS `course`,now() AS `datum`,
(SELECT COUNT(0) from `database`.`users_courses` where (`database`.`users_courses`.`course_id` = `database`.`users_courses`.`course_id`)) AS `antal_registrerade`,
(SELECT COUNT(0) from `database`.`users_courses` where ((`database`.`users_courses`.`status` = 1) and (`database`.`users_courses`.`course_id` = `database`.`users_courses`.`course_id`))) AS `antal_aktiva`,
(SELECT COUNT(0) from `database`.`users_courses` where ((`database`.`users_courses`.`status` = 3) and (`database`.`users_courses`.`course_id` = `database`.`users_courses`.`course_id`))) AS `antal_avklarade`
FROM `database`.`users_courses`
GROUP BY `database`.`users_courses`.`course_id`
This is much simpler to express using conditional aggregation:
SELECT course_id AS course, NOW() as datum,
COUNT(*) as antal_registrerade,
SUM(status = 1) as antal_aktiva,
SUM(status = 3) AS antal_avklarade
FROM users_courses
GROUP BY course_id;
This should fix the problem with your results.
For some reason, the saved code for the view has the correlation clause incorrect. My guess is that you don't have two columns in the table for course and course_id, so your first query isn't exactly what is going into the view. In any case, fix this using a simpler query.