MySQL - Counting transactions related to the most recent visit - mysql

Suppose I have two tables in the same MySQL DB:
The first is the inbound_campaign_visit table. It looks like this.
+----+-----------+------------------+---------------------+
| id | user_id | inbound_campaign | date |
+----+-----------+------------------+---------------------+
| 1 | 1 | 1 | 2013-02-18 13:00:00 |
| 2 | 1 | 2 | 2013-02-24 13:00:00 |
| 3 | 2 | 3 | 2013-01-01 01:00:00 |
| 4 | 2 | 2 | 2013-02-24 19:00:00 |
+----+-----------+------------------+---------------------+
A row on this table is generated every time a user visits my site as a result of clicking on a promotional campaign. The "date" column represents the time when they came to the site.
The second table is my transaction table.
+--------+---------+---------------------+
| id | user_id | creation_date |
+--------+---------+---------------------+
| 321639 | 1 | 2013-02-18 14:00:00 |
| 321640 | 1 | 2013-02-24 15:00:00 |
| 321641 | 1 | 2013-02-25 13:00:00 |
| 321642 | 1 | 2013-04-05 12:00:00 |
| 321643 | 2 | 2013-01-01 12:00:00 |
| 321644 | 2 | 2013-02-23 12:00:00 |
+--------+---------+---------------------+
A row on this table is created whenever a transaction happens. The "creation_date" column represents the time the transaction occured.
I want to create a report that will count the number of transactions per inbound campaign. The following rules must apply:
A transaction is considered related to an inbound campaign if the user_id values match that of the transaction and the transaction occurred within 30 days of an inbound_campaign_visit row being created.
A transaction can only apply to the most recent inbound campaign_visit for the given user.
The result table should look something like this:
+------------------+-------------------+
| inbound_campaign | transaction_count |
+------------------+-------------------+
| 1 | 1 |
| 2 | 2 |
| 3 | 1 |
+------------------+-------------------+
Notice that transactions 321644 and 321642 are not counted as they fail rule 1. Also notice how transaction 321641 only applies to inbound_campaign 2 and not inbound_campaign 1 (even though both campaigns fall within the 30 day restriction defined in rule 1).
I have been struggling with this for some time so any help would be appreciated. Of course I could do this in code but there must be a way to do this in SQL. TIA.

SELECT a.inbound_campaign,
COUNT(b.user_ID) TotalCount
FROM inbound_campaign_visit a
LEFT JOIN transaction b
ON a.user_ID = b.user_ID AND
DATEDIFF(CURDATE(), b.creation_date) > 30
GROUP BY a.inbound_campaign
SQLFiddle Demo

Related

Need an SQL query to calculate the sum of user activity time in desired period

I'm looking for a way to find out how much time in total has each user been active during, for example, the month of February 2020. Is there a way this can be done by querying the MySQL database?
I have a "user_activity" table in my databese, which contains all changes that the user is making to his account.
It has 4 columns that can be important for this calculation:
"field" - this column gathers data on what type of change the user is making to his account, the value of this column is "active" for all cases where the user is activating/deactivating his account
"old_value" - this is the value of what is inside of "field" that was present before the change
"new_value - this is the new value of what is inside of "field"
"activity_time" - this is the time of the change
EXAMPLE TABLE: user_activity
| user_activity_id | user_id | field | old_value | new_value | activity_time |
-------------------------------------------------------------------------------------
| 1 | 1 | active | 1 | 0 | 2020-01-01 15:45:00 |
| 2 | 1 | active | 0 | 1 | 2020-01-02 10:31:00 |
| 3 | 3 | active | 0 | 1 | 2020-01-02 16:22:00 |
| 4 | 4 | active | 0 | 1 | 2020-01-03 03:25:00 |
| 5 | 4 | active | 1 | 0 | 2020-01-06 19:59:00 |
So each time the user activates his account a line is entered in "user_activity" table with new activity_time and values where field = "active" and old_value = 0 and new_value = 1.
A single user can activate or deactivate his account multiple times during 1 month and I'm working on a table with tens of thousands of entries like this.
EXAMPLE DESIRED OUTPUT:
| user_id | active_hours_feb_2020 |
-----------------------------------
| 1 | 500 |
| 2 | 0 |
| 3 | 700 |
| 4 | 250 |
You can get the past activity time and calculate the difference. Think the query will work if you track the activity all the time.
query using lag - https://dbfiddle.uk/?rdbms=mysql_8.0&fiddle=9f6fea01dd9fdc3e91d1e446ec027927

MySQL timestampdiff of records on same column

I have a table in which I am trying to sum times on the same column. I have a column where I log all time entries of any event. So, I would like to group them by gateway ID and have the value in hours, minutes and seconds. I have tried to used timestampdiff, but it takes three arguments and since I have only one row, I couldn't find a way. Is that possible? My table looks like this:
+----+---------+---------------------+
| id | gateway | timestamp |
+----+---------+---------------------+
| 1 | 1 | 2018-03-21 08:52:51 |
| 2 | 1 | 2018-03-21 08:52:54 |
| 3 | 1 | 2018-03-21 08:52:58 |
| 4 | 1 | 2018-03-21 08:53:11 |
| 5 | 2 | 2018-03-21 08:53:51 |
| 6 | 1 | 2018-03-21 08:54:21 |
| 7 | 2 | 2018-03-21 08:54:32 |
| 8 | 2 | 2018-03-21 08:54:44 |
| 9 | 2 | 2018-03-21 08:54:53 |
| 10 | 2 | 2018-03-21 08:55:01 |
+----+---------+---------------------+
basically I would like to group all records by their gateway and then have the sum of time there.
Thanks in advance!
I think you want:
select gateway,
timestampdiff(seconds, min(timestamp), max(timestamp))
from t
group by gateway;
I'm a little confused by your question, because timestampdiff() takes three arguments, not two.

mysql return two minimum values

I have a table named: workers and a table named: schedule with the following format:
workers:
| id | name | vacationA | vacationB | workhistory |
| 1 | Florin | 2017-05-05 | 2017-05-25 | 2010-01-01 |
| 2 | Andrei | 2017-06-05 | 2017-06-25 | 2010-01-01 |
| 3 | Alexandra | 2017-07-05 | 2017-07-25 | 2010-01-01 |
| 4 | Emilia | 2017-08-05 | 2017-08-25 | 2010-01-01 |
| 5 | Nicoleta | 2017-09-05 | 2017-09-25 | 2010-01-01 |
+----+-----------+------------+------------+-------------+
schedule:
| day | month | name | shifts |
+-----+-------+-----------+--------+
| 1 | 6 | Florin | 0 |
| 1 | 6 | Andrei | 1 |
| 1 | 6 | Alexandra | 2 |
| 1 | 6 | Emilia | 3 |
| 1 | 6 | Nicoleta | 4 |
+-----+-------+-----------+--------+
I need to interrogate table "workers" to give me 2 random names, with minimum shifts number, and workers should not be in vacation period. Also work history must be greater than 18 MONTHS.
In this case, the query i need should return Florin and Andrei.
This is what I've got so far, but it doesn't work as supposed:
SELECT name FROM workers WHERE (CURDATE() NOT BETWEEN vacationA AND vacationB) AND workhistory > (DATE_SUB(CURDATE(), INTERVAL 18 MONTH)) AND name IN (SELECT name FROM schedule ORDER BY shifts LIMIT 2) ORDER BY RAND() LIMIT 2;
This query returns
1235 - This version of MySQL doesn't yet support 'LIMIT & IN/ALL/ANY/SOME subquery'.
Thank you!
As you have got name column in schedule table already (although it's not a good design), you don't need a join. You can just use ORDER BY with LIMIT,.e.g.
SELECT name
FROM schedule
WHERE day ? AND month = ? --Remove this if there is no crriteria
ORDER BY shifts
LIMIT 2;
The obvious answer is just to sort the table by the number of shifts and grab the first two entries:
SELECT name FROM schedule ORDER BY shifts ASC LIMIT 2
I notice, however, that you already have an ORDER BY clause, so it seems you want the results in random order.
If you need the random order as well, then wrap the whole thing in a subquery like this:
SELECT name FROM (SELECT name FROM schedule ORDER BY shifts ASC LIMIT 2) ORDER BY RAND()

MySQL monthly report inaccuracies

My company has introduced an on-call rota for the IT department. I created a MySQL table which details who takes on-call, when they take it and when it's taken by the next individual on completion of each shift.
Below is a sample (with names removed) taken from late May - early June:
|seq__num | date_taken | date_relinquished | user |
|-----------|---------------|-----------------------|-----------|
| 1 | 2015-05-29 | 2015-06-05 | A |
| 2 | 2015-06-05 | 2015-06-06 | B |
| 3 | 2015-06-06 | 2015-06-07 | C |
| 4 | 2015-06-07 | 2015-06-10 | B |
| 5 | 2015-06-10 | 2015-06-10 | A |
| 6 | 2015-06-10 | 2015-06-12 | B |
| 7 | 2015-06-12 | 2015-06-19 | C |
| 8 | 2015-06-19 | 2015-07-03 | D |
The next step is to produce an automated monthly report which queries the table and outputs how many days each user held on-call for so Finance know how much they need paying. Currently this is counted manually.
The query I've got is:
SELECT user, SUM(DATEDIFF(date_relinquished, date_taken))
AS duration
FROM on-call_log
WHERE YEAR(date_relinquished) = YEAR(CURRENT_DATE - INTERVAL 1 MONTH)
AND MONTH(date_relinquished) = MONTH(CURRENT_DATE - INTERVAL 1 MONTH)
GROUP BY user
While this does work if on-call is held perfectly within a month. If someone is on-call from the one month into the next, it reports the full period, which produces inaccuracies. Instead of reporting as if June actually has 30 days, like so:
A 4
B 6
C 8
D 12
It takes into account how person A took on-call from the previous month and person D took it into the following month, like so:
A 7
B 6
C 8
D 14
I'm a bit of a loss as to how to make it report accurately. Does anyone have any suggestions or ideas? Thanks in advance.
One solution is to use a calendar table - even a calendar table holding all plausible dates into the future is depressingly small!
Then your query might look like this - I've assumed that on-calls are only counted once per day per user (DISTINCT)...
SELECT user
, DATE_FORMAT(dt,'%Y-%m') month
, COUNT(DISTINCT dt) total
FROM calendar x
JOIN my_table y
ON x.dt BETWEEN y.date_taken AND y.date_relinquished
GROUP
BY month
, user;
+------+---------+-------+
| user | month | total |
+------+---------+-------+
| A | 2015-05 | 3 |
| A | 2015-06 | 6 |
| B | 2015-06 | 8 |
| C | 2015-06 | 10 |
| D | 2015-06 | 12 |
| D | 2015-07 | 3 |
+------+---------+-------+

Mysql how to find cumulative total by group

Can anyone help me to sort this out pleaase. i have a episode table and for an episode there will be following appointments . Episode table will be like
+-------------+------------+------------+------------+----------------+------+
| Episode_id | Patientid | St_date | End_date | Status | ... |
+-------------+------------+------------+------------+----------------+------+
| 61112345 | 100001 | 12-01-2010 | | Active | |
| 61112346 | xxxxxx | 20-01-2010 | 10-10-2011 | Withdrawn | |
| ......... | xxxxxxxx | 30-01-2010 | 10-05-2011 | Lost to follow | |
| ......... | xxxxxxxx | 01-02-2011 | Active | Active | |
+-------------+------------+------------+------------+----------------+------+
Status field holds the status of each episode.A episode has 6 appointments , 3 months per appointment. so totally an episode has 18 months . some patient may complete all 6 appointment , some may withdraw in the middle, or some will be lost to follow up. i need to create a dashboard .
Appointment table will have fields for
Appointment_id
PatientId
...
Stats // Completed or pending, which is used for reporting
For example if a patient complete 2 appointment and if he is marked as Withdrawn on episdode which means that he has withdrawn from 3rd visit and active for 2 visits, if we lost to follow him on 5th app, then he will be active for 4app and then he will be added to lost to follow up on 5th visit. if he completes all then he will added to active for all 6 visits. and the report should be like
Report from 01-01-2010 to 31-12-2010
+--------+--------+-------------+----------------+---------+
| | Active | Withdrawn | Lost to follow | Revised |
+------- +--------+-------------+----------------+---------+
| visit1 | 1500 | 30 | 5 | 5 |
| Visit2 | 1800 | 20 | 4 | 3 |
| Visit3 | 1900 | 45 | 3 | 2 |
| Visit4 | 1800 | 34 | 0 | 1 |
| Visit5 | 1900 | 30 | 0 | 1 |
| Visit6 | 1200 | 20 | 0 | 5 |
+--------+--------+-------------+----------------+---------+
Currently we are fetching the query and using loop only we are generating reports like this, but it is taking time to process, is there any way i can achieve using query itself.
It isn't really clear what you want to group by, but I can give you a general answer. After your where clause you can add "group by fieldname order by fieldname" where fieldname is the element you want to count or sum. You can then count(fieldname) or sum(fieldname) to either add or count.
This may be helpful: http://www.artfulsoftware.com/infotree/qrytip.php?id=105