How to write this query MYSQL - mysql

I have this database:
| id | name | email | control_number | created | | | | | |
|:--:|-------|-----------------|----------------|------------|---|---|---|---|---|
| 1 | john | john#gmail.com | 1 | 14/09/2016 | | | | | |
| 2 | carl | carl#gmail.com | 1 | 13/08/2016 | | | | | |
| 3 | frank | frank#gmail.com | 2 | 12/08/2016 | | | | | |
And i want to get the COUNT in the last 12 months by the control_number.
basicly is a COUNT where control_number = 1 but by month.
So if the query is done today, its september, it should start from september to October 2015 and display the count of records for each month.
Result should be:
09/2016 = 50
08/2016 = 35
07/2016 = 20
06/2016 = 50
05/2016 = 21
04/2016 = 33
03/2016 = 60
02/2016 = 36
01/2016 = 11
12/2015 = 0
11/2015 = 0
10/2015 = 0

Hmmm. Getting the 0 values can be tricky. Assuming that you have some data each month (even if not for "1"), th en you can do:
select extract(year_month from created) as yyyymm,
sum(control_number = 1)
from t
where created >= date_sub(curdate(), interval 12 month)
group by extract(year_month from created)
order by yyyymm;
If you don't have at least one record for each month, then you'll need a left join and a table with one row per month.

Try this:
select CONCAT(SUBSTRING(ym, 5, 2), '/', SUBSTRING(ym, 1, 4)) Month, Count from (
select EXTRACT(YEAR_MONTH FROM created) ym, count(*) Count
from mytable
where EXTRACT(YEAR_MONTH FROM created) > (EXTRACT(YEAR_MONTH FROM SUBDATE(NOW(), INTERVAL 1 YEAR))
group by 1
order by 1 desc) x

Try:
select concat(month(created),'/',year(created)) as period, count(*) as cnt
from mytable
where control_number=1 and TIMESTAMPDIFF(year, created, now())=0
group by (month(created));

Related

MySql Delete Data older than 1 month but leave one entry per week and id (the one with the lowest value)

This is the table I am working with:
+---------------------+-----------
| Field | Type |
+---------------------+--------------+
| ID | binary(17) |
| MiscSensor_ID | binary(17) |
| rawValue | varchar(100) |
| RawValueUnitType_ID | int |
| timestamp | timestamp |
+---------------------+--------------+
Now my goal is to implement an event which deletes all entries older than a month BUT for each week I want to leave one entry per MiscSensor_ID (the one with the lowest rawValue).
I am this far:
CREATE EVENT delete_old_miscsensordatahistory
ON SCHEDULE EVERY 1 DAY
STARTS CURRENT_DATE + INTERVAL 1 DAY
DO
DELETE
FROM history
WHERE TIMESTAMPDIFF(DAY, timestamp,NOW()) > 31;
I need to do something like: delete if (value > minvalue) and group it in by MiscSensor_ID and 7 day periods but i am stuck right now on how to do that.
Any help would be much appreciated.
You can try using the ROW_NUMBER window function to match the rows which you don't want to delete. Records having row number equal to 1 will be those rows with the minimum "rawValue" for each combination of (week, sensorId).
WITH cte AS (
SELECT *, ROW_NUMBER() OVER(
PARTITION BY MiscSensorId, WEEK(timestamp)
ORDER BY rawValue ) AS rn
FROM history
WHERE TIMESTAMPDIFF(DAY, timestamp,NOW()) > 31
)
DELETE
FROM history
INNER JOIN cte
ON history.ID = cte.ID
WHERE rn > 1;
This is how i implemented the event right now:
CREATE EVENT delete_old_miscsensordatahistory
ON SCHEDULE EVERY 1 DAY
STARTS CURRENT_DATE + INTERVAL 1 DAY
DO
WITH cte AS (
SELECT *, ROW_NUMBER() OVER(
PARTITION BY MiscSensor_ID, WEEK(timestamp)
ORDER BY CAST(rawValue AS SIGNED) ) AS rn
FROM MiscSensorDataHistory
WHERE TIMESTAMPDIFF(DAY, timestamp,NOW()) > 31
)
DELETE MiscSensorDataHistory
FROM MiscSensorDataHistory
INNER JOIN cte
ON cte.ID = MiscSensorDataHistory.ID
WHERE rn > 1
Testing my method I found out that there are still entries with the same MiscSensor_ID and less than 7 days apart:
| 0x3939333133303037343939353436393032 | 0x3439303031303031303730303030303535 | 554 | 30 | 2022-02-17 23:09:21 |
| 0x3939333133303037343939313631333039 | 0x3439303031303031303730303030303535 | 554 | 30 | 2022-02-06 16:52:48 |
| 0x3939333133303037343938383835353239 | 0x3439303031303031303730303030303535 | 553 | 30 | 2022-01-30 08:21:55 |
| 0x3939333133303037343938383639333436 | 0x3439303031303031303730303030303535 | 554 | 30 | 2022-01-29 22:48:06 |
| 0x3939333133303037343937303734353537 | 0x3439303031303031303730303030303535 | 444 | 30 | 2021-12-26 06:12:07 |
| 0x3939333133303037343937303530363738 | 0x3439303031303031303730303030303535 | 446 | 30 | 2021-12-25 21:53:03 |
| 0x3939333133303037343936333034343238 | 0x3439303031303031303730303030303535 | 0 | 30 | 2021-12-14 13:08:04 |
| 0x3939333133303037343935393934303832 | 0x3439303031303031303730303030303535 | 415 | 30 | 2021-12-08 12:56:43
Any suggestions would be much appreciated.

SQL query to count the number of rows in different columns with group by clauses

In my Mysql database, I have 2 columns that store the start and end date of the process, respectively. I need to write a query that allows me to count the number of rows for each month in each column, and presents the count separately.
Table example:
+----+------------+----------------+
| id | startData | endData |
+----+-------------+----------------+
| 1 | 02/03/2020 | 02/03/2020 |
| 2 | 02/04/2020 | 02/04/2020 |
| 3 | 02/04/2020 | 02/05/2020 |
| 4 | 02/04/2020 | 02/05/2020 |
| 5 | 02/05/2020 | 02/06/2020 |
| 6 | 02/05/2020 | 02/06/2020 |
| 7 | 02/06/2020 | 02/07/2020 |
+----+-------------+----------------+
I want as a result:
+-------+--------------------+-------------------+
| month | count_month_start | count_month_end |
+-------+--------------------+-------------------+
| 03 | 01 | 01 |
| 04 | 03 | 01 |
| 05 | 02 | 02 |
| 06 | 01 | 02 |
| 07 | 00 | 01 |
+-------+--------------------+-------------------+
Assuming your start date and end date columns are of datatype date, you can do -
Select ifnull(Tb1.mn,Tb2.mn) As mn, ifnull(count_mn_start,0) As count_mn_start, ifnull(count_mn_end,0) As count_mn_end
from
(Select Month(StartDate) as mn, count(id) as count_mn_start
from
my_table
Group by Month(StartDate))Tb1
left Join (Select Month(EndDate) as mn, count(id) as count_mn_end
from my_table
Group by Month(EndDate)) Tb2
on Tb1.mn = Tb2.mn
UNION
Select ifnull(Tb1.mn,Tb2.mn) As mn, ifnull(count_mn_start,0) As count_mn_start, ifnull(count_mn_end,0) As count_mn_end
from
(Select Month(StartDate) as mn, count(id) as count_mn_start
from
my_table
Group by Month(StartDate))Tb1
Right Join (Select Month(EndDate) as mn, count(id) as count_mn_end
from my_table
Group by Month(EndDate)) Tb2
on Tb1.mn = Tb2.mn;
DB fiddle - https://dbfiddle.uk/?rdbms=mysql_8.0&fiddle=84ecddb9f5ed431ddff6a9eaab87e5df
PS : If your dates only have one year (2020 as your example) this would work, however ideally if you have different years in the data, consider having the year in the output as well and in that case use the same syntax ie Year(datefield) and add it in the select and group by in the sub-queries (same way as month in the above query).
A pretty simple way is to expand the time periods into days using a recursive CTE. Then just aggregate:
with recursive cte as (
select id, startdate as dte, enddate
from t
union all
select id, dte + interval 1 day, enddate
from cte
where dte < enddate
)
select year(dte), month(dte),
sum( day(dte) = 1 ) as cnt_start,
sum( day(dte) = day(last_day(dte)) ) as cnt_end
from cte
group by year(dte), month(dte) ;
Here is a db<>fiddle.

MySQL - how to select id where min/max dates difference is more than 3 years

I have a table like this:
| id | date | user_id |
----------------------------------------------------
| 1 | 2008-01-01 | 10 |
| 2 | 2009-03-20 | 15 |
| 3 | 2008-06-11 | 10 |
| 4 | 2009-01-21 | 15 |
| 5 | 2010-01-01 | 10 |
| 6 | 2011-06-01 | 10 |
| 7 | 2012-01-01 | 10 |
| 8 | 2008-05-01 | 15 |
I’m looking for a solution how to select user_id where the difference between MIN and MAX dates is more than 3 yrs. For the above data I should get:
| user_id |
-----------------------
| 10 |
Anyone can help?
SELECT user_id
FROM mytable
GROUP BY user_id
HAVING MAX(`date`) > (MIN(`date`) + INTERVAL '3' YEAR);
Tested here: http://sqlize.com/MC0618Yg58
Similar to bernie's approach, I'd keep date formats native. I'd also probably list the MAX first as to avoid an ABS call (secure a positive number is always returned).
SELECT user_id
FROM my_table
WHERE DATEDIFF(MAX(date),MIN(date)) > 365
DATEDIFF just returns delta (in days) between two given date fields.
SELECT user_id
FROM (SELECT user_id, MIN(date) m0, MAX(date) m1
FROM table
GROUP by user_id)
HAVING EXTRACT(YEAR FROM m1) - EXTRACT(YEAR FROM m0) > 3
SELECT A.USER_ID FROM TABLE AS A
JOIN TABLE AS B
ON A.USER_ID = B.USER_ID
WHERE DATEDIFF(A.DATE,B.DATE) > 365

total user count monthwise

mySQL total user count
Grouping by month
I want to list the total count of registered users grouped by month
Well, the difficulty about this is that I don't want the count per month,
but the the total count of users up to (and including) the month.
User table structure
+---------------+--------------+------+-------------------+----------------+
| Field | Type | Null | Default | Extra |
+---------------+--------------+------+-------------------+----------------+
| ID | int(11) | NO | NULL | auto_increment |
| email | varchar(225) | NO | NULL | |
................................-CUT-.......................................
| registered | timestamp | NO | CURRENT_TIMESTAMP | |
+---------------+--------------+------+-------------------+----------------+
Example data
1 example1#mail 2012-04-04 xx:xx:xx
2 example2#mail 2012-05-04 xx:xx:xx
3 example3#mail 2012-05-04 xx:xx:xx
Preferred output
+------+-------+-------+
| Year | Month | Count |
+------+-------+-------+
| 2012 | 01 | 0 |
| 2012 | 02 | 0 |
| 2012 | 03 | 0 |
| 2012 | 04 | 1 |
| 2012 | 05 | 3 |
+------+-------+-------+
The NULL results aren't necessary.
How could I achieve that result in pure mySQL?
I have not tried this but something along these lines should work -
SELECT tots.*, #var := #var + tots.`count`
FROM (
SELECT
YEAR(registered) AS `year`,
MONTH(registered) AS `month`,
COUNT(*) AS `count`
FROM user
GROUP BY `year`, `month`
) AS tots, (SELECT #var := 0) AS inc
You can do it with a couple of user variables:
set #c = 0;
set #d = 0;
select y, m, #d := #d + Count as Count from
(select year(registered) as y,
month(registered) as m,
#c := #c + count(*) as Count
from user
group by y,m) as t;
gives you
+------+------+-------+
| y | m | Count |
+------+------+-------+
| 2011 | 1 | 2455 |
| 2011 | 2 | 14253 |
| 2011 | 3 | 42311 |
This approach first gets the first day of the month for all months in which any registration occurred. It then joins to every user that had a registration greater than the first day of the month, and then counts the number of users.
SELECT
YEAR(dates.first_day_of_month) AS registration_year,
MONTH(dates.first_day_of_month) AS registration_month,
COUNT(u.ID)
FROM (
SELECT DISTINCT
DATE_SUB(
DATE_ADD(
DATE_SUB(registered,INTERVAL (DAY(registered)-1) DAY),
INTERVAL 1 MONTH),
INTERVAL 1 SECOND) first_day_of_month
FROM user
) dates
LEFT JOIN user u ON u.registered <= dates.first_day_of_month
GROUP BY dates.first_day_of_month
If you want to avoid the gaps in months where no registrations occurred, you could substitute the sub-query with another that used a pre-existing "numbers" table to get a list of all possible months.

MySQL: filling empty fields with zeroes when using GROUP BY

I've got MySQL table
CREATE TABLE cms_webstat (
ID int NOT NULL auto_increment PRIMARY KEY,
TIMESTAMP_X timestamp DEFAULT CURRENT_TIMESTAMP,
# ... some other fields ...
)
which contains statistics about site visitors.
For getting visits per hour I use
SELECT
hour(TIMESTAMP_X) as HOUR
, count(*) AS HOUR_STAT
FROM cms_webstat
GROUP BY HOUR
ORDER BY HOUR DESC
which gives me
| HOUR | HOUR_STAT |
| 24 | 15 |
| 23 | 12 |
| 22 | 9 |
| 20 | 3 |
| 18 | 2 |
| 15 | 1 |
| 12 | 3 |
| 9 | 1 |
| 3 | 5 |
| 2 | 7 |
| 1 | 9 |
| 0 | 12 |
And I'd like to get following:
| HOUR | HOUR_STAT |
| 24 | 15 |
| 23 | 12 |
| 22 | 9 |
| 21 | 0 |
| 20 | 3 |
| 19 | 0 |
| 18 | 2 |
| 17 | 0 |
| 16 | 0 |
| 15 | 1 |
| 14 | 0 |
| 13 | 0 |
| 12 | 3 |
| 11 | 0 |
| 10 | 0 |
| 9 | 1 |
| 8 | 0 |
| 7 | 0 |
| 6 | 0 |
| 5 | 0 |
| 4 | 0 |
| 3 | 5 |
| 2 | 7 |
| 1 | 9 |
| 0 | 12 |
How should I modify the query to get such result (with one mysql query, without creating temporary tables)?
Is it possible to get such result with one MySQL query?
Create another table with a single column,
CREATE TABLE hours_list (
hour int NOT NULL PRIMARY KEY
)
Fill it with all 24 hours.
Then do a join on that table to fill in the zeroes.
SELECT
hs.hour as HOUR, COUNT(ws.ID) AS HOUR_STAT
FROM hours_list hs
LEFT JOIN cms_webstat ws ON hs.hour = hour(ws.TIMESTAMP_X)
GROUP BY hs.hour
ORDER BY hs.hour DESC
This is just the 'why it is not returning` part. Marcus' answer covers the 'how to' part.
The SQL
SELECT
hour(TIMESTAMP_X) as HOUR
, count(*) AS HOUR_STAT
FROM cms_webstat
GROUP BY HOUR
ORDER BY HOUR DESC
gets the count of the records per hour, for the timestamps present in the table
It does not give the details of what is not present in the table. Since there is no recors for the timestamp corresponding to the hour 8 (from your example) the SQL does not return any records.
I've finaly found the answer.
Maybe I'm insane, but this works.
SELECT HOUR, max(HOUR_STAT) as HOUR_STAT FROM (
(
SELECT HOUR(TIMESTAMP_X) as HOUR, count(*) as HOUR_STAT
FROM cms_webstat
WHERE date(TIMESTAMP_X) = date(now())
)
UNION (SELECT 0 as HOUR, 0)
UNION (SELECT 1 as HOUR, 0)
UNION (SELECT 2 as HOUR, 0)
UNION (SELECT 3 as HOUR, 0)
UNION (SELECT 4 as HOUR, 0)
UNION (SELECT 5 as HOUR, 0)
UNION (SELECT 6 as HOUR, 0)
UNION (SELECT 7 as HOUR, 0)
UNION (SELECT 8 as HOUR, 0)
UNION (SELECT 9 as HOUR, 0)
UNION (SELECT 10 as HOUR, 0)
UNION (SELECT 11 as HOUR, 0)
UNION (SELECT 12 as HOUR, 0)
UNION (SELECT 13 as HOUR, 0)
UNION (SELECT 14 as HOUR, 0)
UNION (SELECT 15 as HOUR, 0)
UNION (SELECT 16 as HOUR, 0)
UNION (SELECT 17 as HOUR, 0)
UNION (SELECT 18 as HOUR, 0)
UNION (SELECT 19 as HOUR, 0)
UNION (SELECT 20 as HOUR, 0)
UNION (SELECT 21 as HOUR, 0)
UNION (SELECT 22 as HOUR, 0)
UNION (SELECT 23 as HOUR, 0)
)
AS `combined_table`
GROUP BY HOUR
ORDER BY HOUR DESC
One MySQL query as desired.
$sql = 'SELECT g, MAX(v) AS v, MAX(c) AS c FROM (';
$sql .= '(SELECT DATE_FORMAT(viewed, \'%d.%m.%Y\') AS g, COUNT(1) AS v, 0 AS c FROM '.$this->prefix.'view WHERE campaignid IN ('.join(', ',$ids).') GROUP BY g)';
$sql .= ' UNION (SELECT DATE_FORMAT(clicked, \'%d.%m.%Y\') AS g, 0 AS v, COUNT(1) AS c FROM '.$this->prefix.'clicks WHERE campaignid IN ('.join(', ',$ids).') GROUP BY g)';
$today = strtotime("00:00:00");
for ($i=$today; $i>=time()-30*86400; $i-=86400) {
$sql .= ' UNION (SELECT \''.date('d.m.Y',$i).'\' AS g, 0 AS v, 0 AS c)';
}
$sql .= ') AS tmp GROUP BY g ORDER BY g DESC';
$chart = DB::getAll($sql);
p($chart);
Thanks! Made it! From 2 tables, clicks and views, joined.. works. ajaxel.com