MySQL filter by GROUP BY result - mysql

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

Related

MySQL: Get everyday incremental data

I want to fetch the data from Table based on date but in an incremental way.
Suppose I have data like this which is grouped by date
| DATE | Count |
| 2015-06-23 | 10 |
| 2015-06-24 | 8 |
| 2015-06-25 | 6 |
| 2015-06-26 | 3 |
| 2015-06-27 | 2 |
| 2015-06-29 | 2 |
| 2015-06-30 | 3 |
| 2015-07-01 | 1 |
| 2015-07-02 | 3 |
| 2015-07-03 | 4 |
So the result should come like this
| DATE | Count| Sum|
| 2015-06-23 | 10 | 10 |
| 2015-06-24 | 8 | 18 |
| 2015-06-25 | 6 | 24 |
| 2015-06-26 | 3 | 27 |
| 2015-06-27 | 2 | 29 |
| 2015-06-29 | 2 | 31 |
| 2015-06-30 | 3 | 34 |
| 2015-07-01 | 1 | 35 |
| 2015-07-02 | 3 | 38 |
| 2015-07-03 | 4 | 42 |
You would join every other previous date on that date, and then sum the count on that
If you give me your table structure, I can make it run.
id, name, date_joined
SELECT counts.theCount, sum(counts.theCount), table.date_joined
FROM yourTable
LEFT JOIN
(SELECT count(*) as theCount, table.date_joined
FROM yourTable
GROUP BY table.date_joined
) as counts
ON
yourTable.date_joined> counts.date_joined
GROUP BY yourTable.date_joined

get time difference from childs

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

MySQL query to meet specific needs

My table is as follow:
-------------------------------------------
| rec_id | A_id | B_id |Date(YYYY-MM-DD)|
-------------------------------------------
| 1 | 1 | 6 | 2014-01-01 |
| 2 | 5 | 1 | 2014-01-02 |
| 3 | 2 | 6 | 2015-01-03 |
| 4 | 6 | 1 | 2014-01-04 |
| 5 | 7 | 1 | 2014-01-05 |
| 6 | 3 | 6 | 2014-01-06 |
| 7 | 8 | 1 | 2014-01-07 |
| 8 | 4 | 6 | 2014-01-08 |
| 9 | 9 | 1 | 2014-01-09 |
| 10 | 10 | 21 | 2014-01-10 |
| 11 | 12 | 21 | 2014-01-11 |
| 12 | 11 | 2 | 2014-01-12 |
| 13 | 1 | 1 | 2014-12-31 |
| 14 | 2 | 2 | 2014-12-31 |
| 15 | 1 | 1 | 2015-01-31 |
| 16 | 10 | 21 | 2015-01-31 |
| 17 | 1 | 21 | 2014-10-31 |
This table represents the possession of various "A_id" to a specific "B_id" with a date when it is possessed. The possession of each "A_id" can be changed later on at any time. That means the only the latest possession is considered.
I want to find out all the "A_id" that are currently (possessed in latest date) in possession of a specific "B_id". For example, for "B_id" = 6 the possessed "A_id" at present are as follows:
---------------------------
| A_id | Date(YYYY-MM-DD) |
---------------------------
| 2 | 2015-01-03 |
| 3 | 2014-01-06 |
| 4 | 2014-01-08 |
Similarly, for "B_id" = 21 the possessed "A_id" at present are as follows:
---------------------------
| A_id | Date(YYYY-MM-DD) |
---------------------------
| 10 | 2015-01-31 |
| 12 | 2014-01-11 |
I would highly appreciate your kind help in this regard.
One way to accomplish this is to use a correlated not exists predicate that makes sure that there doesn't exists any later possession for each A_ID with another B_ID.
SELECT A_ID, MAX(PDATE) AS DATE
FROM YOUR_TABLE T
WHERE B_ID = 6
AND NOT EXISTS (
SELECT 1
FROM YOUR_TABLE
WHERE A_ID = T.A_ID
AND PDATE > T.PDATE
AND B_ID <> T.B_ID
)
GROUP BY A_ID

Mysql - Counting different values of the same field

I have the following table:
Table games
+----+----------+------+---------------------+
| id | name | win | date |
+----+----------+------+---------------------+
| 1 | Fulano | Y | 2014-01-01 00:00:00 |
| 2 | Fulano | Y | 2014-01-01 00:00:00 |
| 3 | Fulano | Y | 2014-01-02 00:00:00 |
| 4 | Fulano | Y | 2014-01-03 00:00:00 |
| 5 | Fulano | N | 2014-01-03 00:00:00 |
| 6 | Beltrano | N | 2014-01-01 00:00:00 |
| 7 | Beltrano | N | 2014-01-01 00:00:00 |
| 8 | Beltrano | N | 2014-01-02 00:00:00 |
| 9 | Beltrano | Y | 2014-01-03 00:00:00 |
| 10 | Cicrano | Y | 2014-01-03 00:00:00 |
| 11 | Cicrano | N | 2014-01-03 00:00:00 |
+----+----------+------+---------------------+
I would like to do something like:
SELECT name, count(win='Y') AS wins, count(win='N') AS losses FROM games GROUP BY name;
Obviously I get an unwanted response due to the "count" does not recognize the command that I invented:
+----------+----------+----------+
| name | wins | losses |
+----------+----------+----------+
| Beltrano | 4 | 4 |
| Cicrano | 2 | 2 |
| Fulano | 5 | 5 |
+----------+----------+----------+
Could someone help get the correct answer?
SELECT
name,
SUM( win = 'Y' ) AS wins,
SUM( win = 'N' ) AS losses
FROM games
GROUP BY name;

SELECT * FROM combination in MySQL

This is my MySQL databse:
+-----+------------+----------+----------------+--------------+-----------------+
| id | data | hora | conexoes_total | conexoes_sim | conexoes_lentas |
+-----+------------+----------+----------------+--------------+-----------------+
| 1 | 2013-11-18 | 00:00:02 | 200 | 1 | 30 |
| 2 | 2013-11-18 | 10:15:03 | 14 | 2 | 25 |
| 3 | 2013-11-18 | 10:30:03 | 53 | 3 | 25 |
| 4 | 2013-11-18 | 10:45:02 | 60 | 5 | 26 |
| 5 | 2013-11-18 | 11:00:02 | 66 | 3 | 27 |
| 6 | 2013-11-18 | 11:15:03 | 74 | 5 | 27 |
.
.
.
I want select * from every 00:00:% data and the last value inserted..
I already know how to select the midnight values: SELECT * FROM audiencia WHERE hora LIKE '00:00:%';, and this what i got:
+-----+------------+----------+----------------+--------------+-----------------+
| id | data | hora | conexoes_total | conexoes_sim | conexoes_lentas |
+-----+------------+----------+----------------+--------------+-----------------+
| 1 | 2013-11-18 | 00:00:02 | 200 | 1 | 30 |
| 57 | 2013-11-19 | 00:00:02 | 446 | 1 | 97 |
| 153 | 2013-11-20 | 00:00:02 | 300 | 3 | 173 |
| 349 | 2013-11-21 | 00:00:02 | 42 | 2 | 94 |
+-----+------------+----------+----------------+--------------+-----------------+
But now i want to show the last value inserted in table together with the above result..
How can i manage a command to do that to me?
(SELECT * FROM audiencia WHERE hora LIKE '00:00:%')
UNION
(SELECT * FROM audiencia ORDER BY id DESC LIMIT 1);
Do you want to fetch the last value inserted for column hora of table audiencia which starts from 00:00: ?
If Answer to the above question is YES, then the below SQL will solve your problem.
SELECT * FROM audiencia WHERE hora LIKE '00:00:%' ORDER BY id DESC LIMIT 1;
In case the answer to the above question is NO, then I might have not understood the requirement properly. Kindly, please explain the requirement.
Thanks,
Abhijit