MySQL matching phone calls from two tables - mysql

I've been trying to solve an apparently simple problem for a couple of days now, and I can't seem to figure it out flawlessly, no matter which attempt I take.
I've two simple Tables, each with 3 cols: call_date, call_time, caller_id all of them are self-explanatory.
The first table contains inbound calls, the second one contains outbound calls.
After receiving an inbound call, the corresponding outbound call should be made within 30 minutes. So far so good, no problem here, but this is where it gets a little bit tricky:
There can be multiple inbound calls from the same caller_id in the inbound table.
Only calls that have a minimum of 30 Minutes distance to the previous relevant call are regarded as valid calls. So when someone calls four times, let's say at 08:00, at 08:15, at 08:31 and at 09:31, there are three valid calls: 08:00, 08:31 and 09:31. So after each valid call you have to wait 30 minutes again in order for the next call to be valid.
The outbound table can also contain none, one, or multiple calls to the same number, all of which must be matched as closely as possible to the incoming calls.
I tried various approaches to this problem, but all of them are not 100% accurately, always leaving one or more calls unmatched or not marked as valid etc, and my head is starting to spin a lil bit.
Right now I build a huge table from my inbound call table, adding numerous columns for the previous_calltime and next_calltime so I can calculate the minutes between the time stamps. A second approach I took, was to create seperate tables with the first and last calls each day of each number etc, but something always does not add up.
Here's a sample of my data tables, in- and outbound are the same.
+----+-----------+----------+----------+
| id | call_date | call_time|caller_id
+----+-----------+----------+----------+
| 1 | 2013-06-01| 08:00 | 12345
+----+-----------+----------+----------+
| 2 | 2013-06-01| 08:20 | 12345
+----+-----------+----------+----------+
| 3 | 2013-06-01| 08:30 | 12345
+----+-----------+----------+----------+
| 4 | 2013-06-01| 08:32 | 555-999
+----+-----------+----------+----------+
| 5 | 2013-06-01| 08:47 | 555-999
+----+-----------+----------+----------+
Maybe someone can point me in a rough direction which approach I should take.
Any suggestions are appreciated.
P.s. I am trying a 100% MySQL based approach here, but theoretically I could also make use of PHP, just in case someone thinks this is a pain to do in pure SQL and it could be much more easy using PHP.

Initial code to play with.
This is ignoring dates (for now) but should give you a list of the valid calls. Not sure it will be that quick, but it might be an idea to use it to populate a list somewhere of valid calls (or to mark the inbound calls as valid or not).
Using you test data above it gives the results I would expect:-
SELECT Sub1.*,
#valid := if(#PrevCallerId != Sub1.caller_id OR (TIME_TO_SEC(call_time) - #PrevValidCall) >= 1800, "valid", "invalid") AS valid_call,
#PrevValidCall := if(#valid = "valid", TIME_TO_SEC(Sub1.call_time), #PrevValidCall),
#PrevCallerId := Sub1.caller_id
FROM
(
SELECT *
FROM Inbound
ORDER BY caller_id
) Sub1
CROSS JOIN (SELECT #PrevValidCall:= 0, #PrevCallerId := "", #valid := "") Sub2

Related

Joining and selecting multiple tables and creating new column names

I have very limited experience with MySQL past standard queries, but when it comes to joins and relations between multiple tables I have a bit of an issue.
I've been tasked with creating a job that will pull a few values from a mysql database every 15 minutes but the info it needs to display is pulled from multiple tables.
I have worked with it for a while to figure out the relationships between everything for the phone system and I have discovered how I need to pull everything out but I'm trying to find the right way to create the job to do the joins.
I'm thinking of creating a new table for the info I need, with columns named as:
Extension | Total Talk Time | Total Calls | Outbound Calls | Inbound Calls | Missed Calls
I know that I need to start with the extension ID from my 'user' table and match it with 'extensionID' in my 'callSession'. There may be multiple instances of each extensionID but each instance creates a new 'UniqueCallID'.
The 'UniqueCallID' field then matches to 'UniqueCallID' in my 'CallSum' table. At that point, I just need to be able to say "For each 'uniqueCallID' that is associated with the same 'extensionID', get the sum of all instances in each column or a count of those instances".
Here is an example of what I need it to do:
callSession Table
UniqueCallID | extensionID |
----------------------------
A 123
B 123
C 123
callSum table
UniqueCallID | Duration | Answered |
------------------------------------
A 10 1
B 5 1
C 15 0
newReport table
Extension | Total Talk Time | Total Calls | Missed Calls
--------------------------------------------------------
123 30 3 1
Hopefully that conveys my idea properly.
If I create a table to hold these values, I need to know how I would select, join and insert those things based on that diagram but I'm unable to construct the right query/statement.
You simply JOIN the two tables, and do a group by on the extensionID. Also, add formulas to summarize and gather the info.
SELECT
`extensionID` AS `Extension`,
SUM(`Duration`) AS `Total Talk Time`,
COUNT(DISTINCT `UniqueCallID`) as `Total Calls`,
SUM(IF(`Answered` = 1,0,1)) AS `Missed Calls`
FROM `callSession` a
JOIN `callSum` b
ON a.`UniqueCallID` = b.`UniqueCallID`
GROUP BY a.`extensionID`
ORDER BY a.`extensionID`
You can use a join and group by
select
a.extensionID
, sum(b.Duration) as Total_Talk_Time
, count(b.Answered) as Total_Calls
, count(b.Answered) -sum(b.Answered) as Missed_calls
from callSession as a
inner join callSum as b on a.UniqueCallID = b.UniqueCallID
group by a.extensionID
This should do the trick. What you are being asked to do is to aggregate the number of and duration of calls. Unless explicitly requested, you do not need to create a new table to do this. The right combination of JOINs and AGGREGATEs will get the information you need. This should be pretty straightforward... the only semi-interesting part is calculating the number of missed calls, which is accomplished here using a "CASE" statement as a conditional check on whether each call was answered or not.
Pardon my syntax... My experience is with SQL Server.
SELECT CS.Extension, SUM(CA.Duration) [Total Talk Time], COUNT(CS.UniqueCallID) [Total Calls], SUM(CASE CS.Answered WHEN '0' THEN SELECT 1 ELSE SELECT 0 END CASE) [Missed Calls]
FROM callSession CS
INNER JOIN callSum CA ON CA.UniqueCallID = CS.UniqueCallID
GROUP BY CS.Extension

How can I create a timeline in Access?

In Access I have an MachinesList and ActionsList. A Machine can be set to Active and set to Inactive several times per year. Each change of status has its own ActionID and ActionDate.
In VBA I added some code to get the first and last date the Machine is Active. As this can happen more than once I now can create a list with start- and enddates each time the Machine is Active.
Two questions:
1) Can this be done with a query instead of VBA?
2) Is it possible to display these dates in some sort of timeline in Access?
This is what I have to create my list of dates:
SELECT DISTINCT Requests.RequestNumber, Requests.MachineID, Actions.Assignee, Actions.Action, Actions.TRDate FROM SelectedIDs LEFT JOIN (Requests LEFT JOIN Actions ON Requests.RequestNumber = Actions.RequestNumber) ON SelectedIDs.MachineID = Requests.MachineID ORDER BY Requests.MachineID, Actions.TRDate;
I do need the RequestNumber and the Assignee (in case of Activation) for further use. And since the RequestNumber for Activation and Deactivation differ I cannot use the MIN(date) and MAX(date) functionality because of the GROUP BY clause.
The list produced in VBA looks somewhat like this:
2325 ID1234 29-11-2016 16-3-2017
2323 ID1234 28-3-2017 27-4-2017
2203 ID9999 25-1-2017 27-2-2017
This list I want to see in some sort of timeline in Access.
Something like this:
ID | wk01 | wk02 | wk03 | etc
88 | N | N | Y | Y | Y | N
99 | N | Y | Y | N | N | Y
But any timeline is fine. Suggestions anyone?
Thanks, Karin
It is possible to do, yes. Your resulting recordset will not be tabular, it will be more like { Week, MachineID, Active } and it will be up to you to format it for reporting purposes. It may take a few subqueries, for example an obvious one is max of activation date before the week's end, and min de-activation date after that activation date. If that deactivation date is before the week's start the machine was not active. The actual SQL depends on how your Week table is defined. If you don't have a week table things get a bit more complicated as you also need a sub-query that replaces the week table.

How to balance out row mode and column mode in cygnus?

I have a weather-station that transmits data each hour. During that hour it makes four recordings (one every 15 minutes). In my current situation I am using attr_persistence=row to store data in my MySql database.
With row mode I get the default generated columns:
recvTimeTs | recvTime | entityId | entityType | attrName | attrType | attrValue | attrMd
But my weather station sends me the following data:
| attrName | attrValue
timeRecorded 14:30:0,22.5.2015
measurement1 18.799
measurement2 94.0
measurement3 1.19
These attrValue are represented in the database as string.
Is there a way to leave the three measurements in row mode and switch the timeRecorded to column mode? And if not, then what is my alternative?
The point of all this is to query the time recorded value, but I cannot query date as long as it is string.
As a side note: having the weather station send the data as soon as it is recorded (every 15 minutes) is out of the question, firstly because I need to conserve battery power and more importantly because in the case of a problem with the post, it will send all of the recordings at once.
So if an entire day went without sending any data, the weather station will send all 24*4 readings at once...
The proposed solution is to use the STR_TO_DATE function of MySQL in order to translate the stored string-based "timeRecorded" attribute into a real MySQL Timestamp type.
Nevertheless, "timeRecorded" attribute appears every 4 rows in the table due to the "row" attribute persistence mode of OrionMySQLSink. In this case I think you can use the ROWNUM keyword from MySQL in order to get only every 4 rows, something like (not an expert on MySQL):
SELECT STR_TO_DATE( attrValue, '%m/%d/%Y' ) FROM def_servpath_0004_weatherstation where (ROWNUM / 4 = 0);
The alternative is to move to "column" mode (in this case you have to provision de table by yourself). By using this mode you will have a single row with all the 4 attributes, being one of these attributes the "timeRecorded" one. In this case, you can provision the table by directly specifying the type of the "timeRecorded" column as Timestamp, instead of Text. That way, you will avoid the STR_TO-DATE part.

MySQL, how to repeat same line x times

I have a query that outputs address order data:
SELECT ordernumber
, article_description
, article_size_description
, concat(NumberPerBox,' pieces') as contents
, NumberOrdered
FROM customerorder
WHERE customerorder.id = 1;
I would like the above line to be outputted NumberOrders (e.g. 50,000) divided by NumberPerBox e.g. 2,000 = 25 times.
Is there a SQL query that can do this, I'm not against using temporary tables to join against if that's what it takes.
I checked out the previous questions, however the nearest one:
is to be posible in mysql repeat the same result
Only gave answers that give a fixed number of rows, and I need it to be dynamic depending on the value of (NumberOrdered div NumberPerBox).
The result I want is:
Boxnr Ordernr as_description contents NumberOrdered
------+--------------+----------------+-----------+---------------
1 | CORDO1245 | Carrying bags | 2,000 pcs | 50,000
2 | CORDO1245 | Carrying bags | 2,000 pcs | 50,000
....
25 | CORDO1245 | Carrying bags | 2,000 pcs | 50,000
First, let me say that I am more familiar with SQL Server so my answer has a bit of a bias.
Second, I did not test my code sample and it should probably be used as a reference point to start from.
It would appear to me that this situation is a prime candidate for a numbers table. Simply put, it is a table (usually called "Numbers") that is nothing more than a single PK column of integers from 1 to n. Once you've used a Numbers table and aware of how it's used, you'll start finding many uses for it - such as querying for time intervals, string splitting, etc.
That said, here is my untested response to your question:
SELECT
IV.number as Boxnr
,ordernumber
,article_description
,article_size_description
,concat(NumberPerBox,' pieces') as contents
,NumberOrdered
FROM
customerorder
INNER JOIN (
SELECT
Numbers.number
,customerorder.ordernumber
,customerorder.NumberPerBox
FROM
Numbers
INNER JOIN customerorder
ON Numbers.number BETWEEN 1 AND customerorder.NumberOrdered / customerorder.NumberPerBox
WHERE
customerorder.id = 1
) AS IV
ON customerorder.ordernumber = IV.ordernumber
As I said, most of my experience is in SQL Server. I reference http://www.sqlservercentral.com/articles/Advanced+Querying/2547/ (registration required). However, there appears to be quite a few resources available when I search for "SQL numbers table".

Grouping timestamps in MySQL with PHP

I want to log certain activities in MySql with a timecode using time(). Now I'm accumulating thousands of records, I want to output the data by sets of hours/days/months etc.
What would be the suggested method for grouping time codes in MySQL?
Example data:
1248651289
1248651299
1248651386
1248651588
1248651647
1248651700
1248651707
1248651737
1248651808
1248652269
Example code:
$sql = "SELECT COUNT(timecode) FROM timecodeTable";
//GROUP BY round(timecode/3600, 1) //group by hour??
Edit:
There's two groupings that can be made so I should make that clearer: The 24 hours in the day can be grouped but I'm more interested in grouping over time so returning 365 results for each year the tracking is in place, so total's for each day passed, then being able to select a range of dates and see more details on hours/minutes accessed over those times selected.
This is why I've titled it as using PHP, as I'd expect this might be easier with a PHP loop to generate the hours/days etc?
Peter
SELECT COUNT(*), HOUR(timecode)
FROM timecodeTable
GROUP BY HOUR(timecode);
Your result set, given the above data, would look as such:
+----------+----------------+
| COUNT(*) | HOUR(timecode) |
+----------+----------------+
| 10 | 18 |
+----------+----------------+
Many more related functions can be found here.
Edit
After doing some tests of my own based on the output of your comment I determined that your database is in a state of epic fail. :) You're using INT's as TIMESTAMPs. This is never a good idea. There's no justifiable reason to use an INT in place of TIMESTAMP/DATETIME.
That said, you'd have to modify my above example as follows:
SELECT COUNT(*), HOUR(FROM_UNIXTIME(timecode))
FROM timecodeTable
GROUP BY HOUR(FROM_UNIXTIME(timecode));
Edit 2
You can use additional GROUP BY clauses to achieve this:
SELECT
COUNT(*),
YEAR(timecode),
DAYOFYEAR(timecode),
HOUR(timecode)
FROM timecodeTable
GROUP BY YEAR(timecode), DAYOFYEAR(timecode), HOUR(timecode);
Note, I omitted the FROM_UNIXTIME() for brevity.