get time difference from childs - mysql

I have the following structure
----------
presences
----------
id
started
ended
user_id
---------
breaks
---------
id
presence_id
started
ended
I need to create an SQL query that returns me the following information
presence_id user_id presence_time breaks_time
where presence time is (presence.ended - presence-started) - sum of (break.ended - break.started) of all the breaks related to the presence
Is there an efficient way to get this information with an sql query?
If you know how to do it in eloquent it would be even better :D
Thank you so much!

http://sqlfiddle.com/#!9/650a2/3
SELECT p.id presence_id,
p.user_id,
(p.ended-p.started) presence_time ,
SUM(b.ended-b.started) breaks_time
FROM presences p
LEFT JOIN breaks b
ON p.id = b.presence_id
GROUP BY p.id
UPDATE Same query grouped by user_id:
http://sqlfiddle.com/#!9/1ce21/1
SELECT
sub_total.user_id,
SUM(sub_total.presence_time) ,
SUM(sub_total.breaks_time)
FROM (
SELECT p.id presence_id,
p.user_id,
(p.ended-p.started) presence_time ,
SUM(b.ended-b.started) breaks_time
FROM presences p
LEFT JOIN breaks b
ON p.id = b.presence_id
GROUP BY p.id) sub_total
GROUP BY sub_total.user_id

If your started and ended are stored as datetime or timestamp, then you can easily do the calculation and find the data in minutes.
The following example will be useful when someone taking multiple short breaks through out the working hours.
Later in the application level you can convert the minutes to hour. Here is how you can do in mysql
mysql> select * from presence ;
+------+---------------------+---------------------+---------+
| id | started | ended | user_id |
+------+---------------------+---------------------+---------+
| 1 | 2015-01-01 09:00:00 | 2015-01-01 18:00:00 | 10 |
| 2 | 2015-01-01 09:20:00 | 2015-01-01 18:04:00 | 11 |
| 3 | 2015-01-01 09:10:00 | 2015-01-01 18:30:00 | 12 |
| 4 | 2015-01-02 09:23:10 | 2015-01-02 18:10:00 | 10 |
| 5 | 2015-01-02 09:50:00 | 2015-01-02 19:00:00 | 11 |
| 6 | 2015-01-02 09:10:00 | 2015-01-02 18:36:30 | 12 |
+------+---------------------+---------------------+---------+
6 rows in set (0.00 sec)
mysql> select * from breaks ;
+------+-------------+---------------------+---------------------+
| id | presence_id | started | ended |
+------+-------------+---------------------+---------------------+
| 1 | 1 | 2015-01-01 12:00:00 | 2015-01-01 12:20:30 |
| 2 | 1 | 2015-01-01 15:46:30 | 2015-01-01 15:54:26 |
| 3 | 2 | 2015-01-01 11:26:30 | 2015-01-01 11:34:23 |
| 4 | 2 | 2015-01-01 14:06:45 | 2015-01-01 14:10:20 |
| 5 | 2 | 2015-01-01 16:01:10 | 2015-01-01 16:14:57 |
| 6 | 3 | 2015-01-01 12:11:20 | 2015-01-01 12:40:05 |
| 7 | 3 | 2015-01-01 17:01:10 | 2015-01-01 17:24:21 |
| 8 | 4 | 2015-01-02 12:50:00 | 2015-01-02 13:40:00 |
| 9 | 5 | 2015-01-02 12:20:00 | 2015-01-02 13:05:30 |
| 10 | 5 | 2015-01-02 17:03:00 | 2015-01-02 17:20:00 |
| 11 | 6 | 2015-01-02 12:16:50 | 2015-01-02 12:58:30 |
+------+-------------+---------------------+---------------------+
11 rows in set (0.00 sec)
select
p.id as presence_id,
p.user_id,
timestampdiff(minute,started,ended) - b.break_time as presence_time,
b.break_time from presence p
left join (
select
presence_id,
coalesce(sum( timestampdiff(minute,started,ended ) ),0) as break_time
from breaks
group by presence_id
)b
on b.presence_id = p.id
+-------------+---------+---------------+------------+
| presence_id | user_id | presence_time | break_time |
+-------------+---------+---------------+------------+
| 1 | 10 | 513 | 27 |
| 2 | 11 | 501 | 23 |
| 3 | 12 | 509 | 51 |
| 4 | 10 | 476 | 50 |
| 5 | 11 | 488 | 62 |
| 6 | 12 | 525 | 41 |
+-------------+---------+---------------+------------+
6 rows in set (0.00 sec)

Thanks to #Alex I got it working, my final query is the following (using timestamps)
SELECT *,
TIME_TO_SEC(TIMEDIFF(p.ended,p.started)) as presence_time,
sum(TIME_TO_SEC(TIMEDIFF(b.ended,b.started))) as breaks_time
FROM presences p
left join presences_breaks b on b.presence_id = p.id
group by p.id

Related

Mysql - JOIN two tables group by 2 dates

I have two tables that are not linked by a foreign key but that I would like to join.
Here are the two functional SQL queries I would like to join:
SELECT DATE(added) as 'day', COUNT(*) as 'TopicSub'
FROM user_subscription
WHERE topic_id = 39
GROUP BY DATE(added)
SELECT DATE(date) as 'day', COUNT(*) as 'QSub'
FROM user_submitted_q
WHERE question_id IN (SELECT id FROM questions WHERE topic_id = 39)
GROUP BY DATE(date)
These two queries return a result including the total number of entries grouped by days.
First concern the days of the two queries do not necessarily match (There are no entries for all the days of the period), I would like to merge the results and have 0 if the other column does not include an equivalent date.
After a lot of unsuccessful attempts with all possible joints, I found this query that might work on PostgreSQL:
SELECT *
FROM
(SELECT DATE(date) as 'day', COUNT(*) as 'QSub'
FROM user_submitted_q
WHERE question_id IN (SELECT id FROM questions WHERE topic_id = 39)
GROUP BY DATE(date)) AS q1
FULL OUTER JOIN
(SELECT DATE(added) as 'day', COUNT(*) as 'TopicSub'
FROM user_subscription
WHERE topic_id = 39
GROUP BY DATE(added)) AS q2 ON q1.day = q2.day
ORDER BY
day
But unfortunately I use Mysql5.7
Here is the desired result:
Date | QSub | TopicSub
-----------+------+---------
2018-02-09 | 5 | 1
2018-02-19 | 19 | 13
2018-02-21 | 12 | 1
2018-02-22 | 43 | 0
2018-02-25 | 0 | 1
Sample user_submitted_q data:
+----+---------+-------------+--------+---------------------+----------------+
| id | user_id | question_id | result | date | ip |
+----+---------+-------------+--------+---------------------+----------------+
| 1 | 2 | 436 | good | 2018-02-09 00:13:15 | 176.159.30.253 |
| 2 | 2 | 409 | good | 2018-02-09 00:13:15 | 176.159.30.253 |
| 3 | 3 | 651 | wrong | 2018-02-09 00:13:53 | 77.136.14.187 |
| 4 | 3 | 651 | wrong | 2018-02-09 00:13:53 | 77.136.14.187 |
| 5 | 1 | 96 | wrong | 2018-02-09 00:21:51 | 77.69.200.124 |
| 6 | 1 | 24 | good | 2018-02-09 00:21:51 | 77.69.200.124 |
| 7 | 1 | 25 | good | 2018-02-09 00:21:51 | 77.69.200.124 |
| 8 | 1 | 96 | wrong | 2018-02-09 00:26:52 | 77.69.200.124 |
| 9 | 1 | 24 | good | 2018-02-09 00:26:52 | 77.69.200.124 |
| 10 | 1 | 25 | good | 2018-02-09 00:26:52 | 77.69.200.124 |
+----+---------+-------------+--------+---------------------+----------------+
Sample user_subscription data:
+----+---------+----------+---------------------+
| id | user_id | topic_id | added |
+----+---------+----------+---------------------+
| 8 | 1 | 39 | 2018-02-09 00:27:30 |
| 9 | 4 | 47 | 2018-02-09 00:42:34 |
| 10 | 4 | 19 | 2018-02-09 00:42:34 |
| 11 | 5 | 47 | 2018-02-09 00:54:14 |
| 13 | 6 | 47 | 2018-02-09 01:00:23 |
| 14 | 6 | 19 | 2018-02-09 01:00:23 |
| 17 | 8 | 47 | 2018-02-09 01:06:50 |
| 18 | 8 | 19 | 2018-02-09 01:06:50 |
| 19 | 9 | 47 | 2018-02-09 01:08:33 |
| 20 | 9 | 19 | 2018-02-09 01:08:33 |
+----+---------+----------+---------------------+
Thank you in advance for your help
Given
MariaDB [sandbox]> select * from t order by dt;
+------------+
| dt |
+------------+
| 2018-01-01 |
| 2018-01-02 |
| 2018-01-03 |
| 2018-01-03 |
| 2018-01-04 |
| 2018-01-04 |
+------------+
6 rows in set (0.00 sec)
MariaDB [sandbox]> select * from t1 order by dt;
+------------+
| dt |
+------------+
| 2018-01-02 |
| 2018-01-03 |
| 2018-01-04 |
| 2018-01-05 |
| 2018-01-05 |
| 2018-01-06 |
| 2018-01-06 |
| 2018-01-07 |
| 2018-01-07 |
+------------+
9 rows in set (0.00 sec)
select tu.dt,(select count(*) from t where t.dt = tu.dt) tdt,
(Select count(*) from t1 where t1.dt = tu.dt) t1dt
from
(
select dt from t
union
select dt from t1
) tu;
Will establish all dates spanning both tables then the sub queries will count them.
+------------+------+------+
| dt | tdt | t1dt |
+------------+------+------+
| 2018-01-01 | 1 | 0 |
| 2018-01-02 | 1 | 1 |
| 2018-01-03 | 2 | 1 |
| 2018-01-04 | 2 | 1 |
| 2018-01-05 | 0 | 2 |
| 2018-01-06 | 0 | 2 |
| 2018-01-07 | 0 | 2 |
+------------+------+------+
7 rows in set (0.00 sec)
My SQL does not support full outer joins but you can simulate that behavior as follows:
Select Col1, Col2, ...
From Table1
Left Join Table 2 on Table1.col = Table2.col
Union
Select Col1, Col2, ...
From Table 1
Right Join Table 2 on Table1.col = Table2.col
If you care to preserve duplicate rows you would change the Union to a Union All

SQL Query: find lots that belong to current auction

I have a table auctions and a table lots:
mysql> select id, auction_name, auction_startdate, auction_planned_closedate from auctions;
+----+--------------+---------------------+---------------------------+
| id | auction_name | auction_startdate | auction_planned_closedate |
+----+--------------+---------------------+---------------------------+
| 1 | Auction 1 | 2016-05-01 00:00:00 | 2016-06-30 00:00:00 |
| 2 | Auction 2 | 2016-06-01 00:00:00 | 2016-07-30 00:00:00 |
| 3 | Auction 3 | 2016-07-01 00:00:00 | 2016-08-30 00:00:00 |
| 4 | Auction 4 | 2016-09-01 00:00:00 | 2016-10-30 00:00:00 |
+----+--------------+---------------------+---------------------------+
mysql> select id, auction_id, lot_name from lots;
+----+------------+----------+
| id | auction_id | lot_name |
+----+------------+----------+
| 1 | 1 | Lot 1 |
| 2 | 1 | Lot 2 |
| 3 | 1 | Lot 3 |
| 4 | 1 | Lot 4 |
| 5 | 1 | Lot 5 |
| 6 | 1 | Lot 6 |
| 7 | 1 | Lot 7 |
| 8 | 2 | Lot 8 |
| 9 | 2 | Lot 9 |
| 10 | 2 | Lot 10 |
| 11 | 3 | Lot 11 |
| 12 | 3 | Lot 12 |
| 13 | 3 | Lot 13 |
| 14 | 3 | Lot 14 |
| 15 | 4 | Lot 15 |
| 16 | 4 | Lot 16 |
+----+------------+----------+
I want to display only the lots for current auctions (which are auctions 1 and 2 in the example), in other words for which the current time is between the 'auction_startdate' and 'auction_planned_closedate'.
So here is what I want to achieve:
+--------------+---------------------+---------------------------+---------+
| auction_name | auction_startdate | auction_planned_closedate | lots_id |
+--------------+---------------------+---------------------------+---------+
| Auction 1 | 2016-05-01 00:00:00 | 2016-06-30 00:00:00 | 1 |
| Auction 1 | 2016-05-01 00:00:00 | 2016-06-30 00:00:00 | 2 |
| Auction 1 | 2016-05-01 00:00:00 | 2016-06-30 00:00:00 | 3 |
| Auction 1 | 2016-05-01 00:00:00 | 2016-06-30 00:00:00 | 4 |
| Auction 1 | 2016-05-01 00:00:00 | 2016-06-30 00:00:00 | 5 |
| Auction 1 | 2016-05-01 00:00:00 | 2016-06-30 00:00:00 | 6 |
| Auction 1 | 2016-05-01 00:00:00 | 2016-06-30 00:00:00 | 7 |
| Auction 2 | 2016-06-01 00:00:00 | 2016-07-30 00:00:00 | 8 |
| Auction 2 | 2016-06-01 00:00:00 | 2016-07-30 00:00:00 | 9 |
| Auction 2 | 2016-06-01 00:00:00 | 2016-07-30 00:00:00 | 10 |
+--------------+---------------------+---------------------------+---------+
The following query gets me the current auctions:
mysql> select auction_name, auction_startdate, auction_planned_closedate from auctions where now() >= auction_startdate and now() <= auction_planned_closedate;
+--------------+---------------------+---------------------------+
| auction_name | auction_startdate | auction_planned_closedate |
+--------------+---------------------+---------------------------+
| Auction 1 | 2016-05-01 00:00:00 | 2016-06-30 00:00:00 |
| Auction 2 | 2016-06-01 00:00:00 | 2016-07-30 00:00:00 |
+--------------+---------------------+---------------------------+
and then I do an inner join with the 'lots' table:
select auction_name, auction_startdate, auction_planned_closedate, lots.id
from auctions
where now() >= auction_startdate
and now() <= auction_planned_closedate
inner join lots on auctions.id = lots.auction_id;
ERROR 1064 (42000): You have an error in your SQL syntax; check the
manual that corresponds to your MySQL server version for the right
syntax to use near 'inner join lots on auctions.id=lots.auction_id' at
line 1
I'm getting a syntax error which I'm staring blind at for a while.
Wrong order, put the WHERE clause after the JOIN:
select auction_name, auction_startdate, auction_planned_closedate, lots.id
from auctions
inner join lots on auctions.id = lots.auction_id
where now() >= auction_startdate
and now() <= auction_planned_closedate
Your join is in the wrong order:
select a.auction_name, a.auction_startdate, a.auction_planned_closedate, l.id
from auctions a inner join
lots l
on a.id = l.auction_id
where now() >= a.auction_startdate and now() <= a.auction_planned_closedate ;
Notes:
where goes after the from clause. join is not a separate clause; it is an operator in the from clause.
Table aliases make a query easier to write and to read.
Qualify all the column names. This makes it clear where the columns are coming from.

Print out per item for each status it reached, the oldest datetime of that statuschange in a different column on the same row

I've got one table (Items) with items and another table (ItemStatusHistory) where the status history per item is recorded. Now I want to print out per item for each status it reached, the oldest datetime of that statuschange in a different column on the same row.
What is the best way to query the desired output table using MySQL? Please provide example to query this.
Table Items
+----+------+
| ID | Item |
+----+------+
| 1 | a |
| 2 | b |
| 3 | c |
| 4 | d |
| 5 | e |
+----+------+
Table ItemStatusHistory
+----+--------+----------------+------------+
| ID | ItemID | StatusDateTime | StatusName |
+----+--------+----------------+------------+
| 1 | 1 | 2015-01-01 | statusA |
| 2 | 2 | 2015-01-01 | statusA |
| 3 | 3 | 2015-01-02 | statusA |
| 4 | 1 | 2015-01-03 | statusB |
| 5 | 1 | 2015-01-04 | statusC |
| 6 | 2 | 2015-01-05 | statusB |
| 7 | 3 | 2015-01-06 | statusB |
| 8 | 1 | 2015-01-06 | statusA |
| 9 | 2 | 2015-01-07 | statusA |
| 10 | 3 | 2015-01-08 | statusC |
+----+--------+----------------+------------+
Desired Output Table DateFirstReachedStatus
+------+---------------------+---------------------+---------------------+
| Item | FirstReachedStatusA | FirstReachedStatusB | FirstReachedStatusC |
+------+---------------------+---------------------+---------------------+
| a | 2015-01-01 | 2015-01-03 | 2015-01-04 |
| b | 2015-01-01 | 2015-01-05 | |
| c | 2015-01-02 | 2015-01-06 | 2015-01-08 |
+------+---------------------+---------------------+---------------------+
I tried following query, but I don't got the output I want.
select
i.Item,
Min(status_A.StatusDateTime)[status_A],
Min(status_B.StatusDateTime)[status_B],
Min(status_C.StatusDateTime)[status_C]
from Items i
inner join ItemStatusHistory status_A on i.ID = status_A.ItemID
inner join ItemStatusHistory status_B on i.ID = status_B.ItemID
inner join ItemStatusHistory status_C on i.ID = status_C.ItemID
where
status_A.StatusName = 'statusA'
and status_B.StatusName = 'statusB'
and status_V.StatusName = 'statusC'
order by i.Item asc

How to select specific data from a table in MySQL and subtract it from the next date available in a subsequent row?

This question evolved from How to create a query in MySQL to subtract consecutive rows based on the date and a distinctive field?; I have the following table:
+--------+------------+----------+
| animal | date | quantity |
+--------+------------+----------+
| dog | 2015-01-01 | 400 |
| cat | 2015-01-01 | 300 |
| dog | 2015-01-02 | 402 |
| rabbit | 2015-01-01 | 500 |
| cat | 2015-01-02 | 304 |
| rabbit | 2015-01-02 | 508 |
| rabbit | 2015-01-03 | 524 |
| rabbit | 2015-01-04 | 556 |
| rabbit | 2015-01-05 | 620 |
| rabbit | 2015-01-06 | 748 |
+--------+------------+----------+
Thanks to the users that contributed (in special https://stackoverflow.com/users/1816093/drew) now I am able to run the following query:
select
animal,
date,
quantity,
quantity_diff
from
( SELECT
a.animal,
a.Date AS actual_date,
past_date.Date AS date,
a.quantity AS quantity,
(a.quantity - past_date.quantity) AS quantity_diff,
1 as drewOrder
FROM inventory a
JOIN
(SELECT b.animal, b.date AS date1,
(SELECT MAX(c.date)
FROM inventory c
WHERE c.date < b.date AND c.animal = b.animal
GROUP BY c.animal)
AS date2
FROM inventory b)
AS original_date ON original_date.animal = a.animal
AND original_date.date1 = a.date
JOIN
inventory past_date
ON past_date.animal = a.animal
AND past_date.date = original_date.date2
union
select distinct null,animal,null,null,null,2 as drewOrder from inventory
) x
where x.animal='rabbit' group by quantity_diff
order by x.animal,x.drewOrder,x.actual_date;
This is what I get:
+--------+------------+----------+---------------+
| animal | date | quantity | quantity_diff |
+--------+------------+----------+---------------+
| rabbit | 2015-01-01 | 508 | 8 |
| rabbit | 2015-01-02 | 524 | 16 |
| rabbit | 2015-01-03 | 556 | 32 |
| rabbit | 2015-01-04 | 620 | 64 |
| rabbit | 2015-01-05 | 748 | 128 |
+--------+------------+----------+---------------+
http://sqlfiddle.com/#!9/c77d8/121
...I am pretty close but i just can't get it like I want it; which is like this:
+--------+------------+----------+---------------+
| animal | date | quantity | quantity_diff |
+--------+------------+----------+---------------+
| rabbit | 2015-01-01 | 500 | 8 |
| rabbit | 2015-01-02 | 508 | 16 |
| rabbit | 2015-01-03 | 524 | 32 |
| rabbit | 2015-01-04 | 656 | 64 |
| rabbit | 2015-01-05 | 620 | 128 |
| rabbit | 2015-01-06 | 748 | null |
+--------+------------+----------+---------------+
I also should be able to change the "animal" and get its respective output:
cat:
+--------+------------+----------+---------------+
| animal | date | quantity | quantity_diff |
+--------+------------+----------+---------------+
| cat | 2015-01-01 | 300 | 4 |
| cat | 2015-01-02 | 304 | null |
+--------+------------+----------+---------------+
dog:
+--------+------------+----------+---------------+
| animal | date | quantity | quantity_diff |
+--------+------------+----------+---------------+
| dog | 2015-01-01 | 400 | 2 |
| dog | 2015-01-02 | 402 | null |
+--------+------------+----------+---------------+
Well there is a simpler way to do it without that big query with joins and unions. You need an Window Function for your specific problem it would be a LEAD function.
LEAD(`quantity`) over (Partition By `date` order by `date`)
But unfortunately MySql doesn't support those functions so, you have to mimic it with query variables.
The solution as you want would be:
select animal,
`date`,
quantity,
lead-quantity quantity_diff
from ( select i.animal,
i.`date`,
#qt as lead,
#qt := i.quantity as quantity
from inventory i
where i.animal = 'rabbit'
order by `date` desc
) tab
order by `date`;
You can check it working here: http://sqlfiddle.com/#!9/c77d8/182
And for the cat: http://sqlfiddle.com/#!9/c77d8/183
And for the dog: http://sqlfiddle.com/#!9/c77d8/184
I was frustrated to know that MySql doesn't support the Window Functions until I find this article: Analytic functions: FIRST_VALUE, LAST_VALUE, LEAD, LAG which lead me to learn how to mimic it. I suggest you to favorite it as I did!
Hope it helps!

MySQL filter by GROUP BY result

I don't know if this is something I can achieve effectively with sub queries, or how to even build a query for this. I have to extract some knowledge about people no longer using our system. Imagine we have 3 users with user id 1024, 1234, and 5678; and User 1024 and 1234 are using theme A, and 5678 is using theme B:
$ SELECT * FROM user; | $ SELECT * FROM user_theme;
+------+------+ | +------+-------+
| id | name | | | user | theme |
+------+------+ | +------+-------+
| 1024 | John | | | 1024 | A |
| 1234 | Jane | | | 1234 | A |
| 5678 | Jeff | | | 5678 | B |
+------+------+ | +------+-------+
The usage tracking table appears as this:
$ SELECT * FROM user_usage;
+----+------+---------------------+------+
| id | user | date | uses |
+----+------+---------------------+------+
| 1 | 1234 | 2014-08-02 00:00:00 | 5 |
| 2 | 1234 | 2014-08-03 00:00:00 | 5 |
| 3 | 1234 | 2014-08-04 00:00:00 | 3 |
| 4 | 1234 | 2014-08-05 00:00:00 | 6 |
| 5 | 1024 | 2014-08-02 00:00:00 | 8 |
| 6 | 1024 | 2014-08-03 00:00:00 | 7 |
| 7 | 1024 | 2014-08-04 00:00:00 | 4 |
| 8 | 1024 | 2014-08-05 00:00:00 | 6 |
| 9 | 1024 | 2014-09-02 00:00:00 | 1 |
| 10 | 1024 | 2014-09-03 00:00:00 | 2 |
| 11 | 1024 | 2014-09-04 00:00:00 | 3 |
| 12 | 1024 | 2014-09-05 00:00:00 | 4 |
| 13 | 5678 | 2014-08-02 00:00:00 | 8 |
| 14 | 5678 | 2014-08-03 00:00:00 | 7 |
| 15 | 5678 | 2014-08-04 00:00:00 | 4 |
| 16 | 5678 | 2014-08-05 00:00:00 | 6 |
| 17 | 5678 | 2014-09-02 00:00:00 | 1 |
| 18 | 5678 | 2014-09-03 00:00:00 | 2 |
| 19 | 5678 | 2014-09-04 00:00:00 | 3 |
| 20 | 5678 | 2014-09-05 00:00:00 | 4 |
+----+------+---------------------+------+
I want to find out the break down to see how many usage have dropped from our system in 2014-09 (aka: have usage data in 2014-08, but no longer in 2014-09), grouped by the theme. So I want to write something like:
SELECT
user_theme.theme,
SUM(user_usage.users) 'uses lost'
FROM
user_theme
LEFT JOIN user_usage
ON user_theme.user = user_usage.user
WHERE
...
GROUP BY
user_theme.theme
# HAVING ...?
And get result such as:
+-------+-----------+
| theme | uses lost |
+-------+-----------+
| A | 19 |
| B | 0 |
+-------+-----------+
Where the 19 comes from SUM(uses) for WHERE user = 1234 AND YEAR(date) = 2014 AND MONTH(date) = 8.
I don't know I care about the SUM(uses) from user = 1234 in advance, because I only know I need to include user 1234 in the SUM(uses)'s WHERE clause because SUM(uses) for WHERE user = 1234 AND YEAR(date) = 2014 AND MONTH(date) = 9 is 0.
There's actually a lot of users, and a handful of themes (around 20K users, and about 10 themes), so ideally, I think I'd like to avoid doing the filtering in code as opposed to directly in the database. Is there a way to do this effectively in MySQL using raw SQL queries?
Here is query which compares current month with previous one:
set #current_month = now();
set #previous_month = date_sub(#current_month, interval 1 month);
set #current_month = concat(year(#current_month), month(#current_month));
set #previous_month = concat(year(#previous_month), month(#previous_month));
select a.`theme`, sum(ifnull(b.uses_lost,0)) as uses_lost
from
`user_theme` as a
left outer join
(
select `user`, sum(uses) as uses_lost
from `user_usage`
where concat(year(`date`), month(`date`)) = #previous_month
and `user` not in (
select `user`
from `user_usage`
where concat(year(`date`), month(`date`)) = #current_month)
group by `user`
) as b
on (a.`user`=b.`user`)
group by a.`theme`;
fiddle for play
main idea is to find all users who used system during last month and has no rows during current month