MYSQL query closest match from a second table - php

Hi I have 2 tables structured as follows
cdr
src | bill sec | clean_dst
------------------------------
100 | 10 | 18006927753
100 | 22 | 18006927753
100 | 9 | 441138973356
dialing_codes
id | dial_code | tele2id
-----------------------------
1 | 1 | 1422
2 | 1800 | 1433
3 | 441 | 1024
4 | 4413 | 1086
I need to get the tele2id for the closest match in dial_code against clean_dst my best effort so far is
$query = "SELECT tele2id, dial_code FROM dialing_codes ORDER by dial_code DESC";
$result = $mysqli->query($query) or die($mysqli->error.__LINE__);
while($row = $result->fetch_assoc()) {
$tele2id = $row['tele2id'];
$dialcode = $row['dial_code'];
$query2 = "SELECT clean_dst FROM cdr WHERE clean_dst LIKE '".$dialcode."%'";
$result2 = $mysqli->query($query2) or die($mysqli->error.__LINE__);
while($row2 = $result2->fetch_assoc()) {
Which I thought was working but on closer inspection it only returns the correct result the first time if a clean_dst is repeated
eg
clean_dst dial_code tele2id
18006927753 1800 1433
18006927753 1 1422
What am i doing wrong? Thanks
If it helps I need the result with the most matching digits?

Although not in php, this one sql can handle your first and secondary query all in one... AND properly handle returning the longest matching entry per dial.
select
PQ.clean_dst,
PQ.dial_code,
PQ.tele2id,
#Rank := if( #lastDst = PQ.clean_dst, #Rank +1, 1 ) as dialRank,
#lastDst := PQ.clean_dst as ForNextRowCompare
from
( SELECT distinct
cdr.clean_dst,
dc.dial_code,
dc.tele2id,
length( trim( dc.dial_code )) as Longest
from
cdr
JOIN dialing_codes dc
on cdr.clean_dst like concat( dc.dial_code, '%' )
order by
cdr.clean_dst,
Longest DESC ) PQ,
( select #lastDst := '',
#Rank := 0 ) sqlvars
having
dialRank = 1
The first part is the inner query resulting in alias "PQ" (preQuery). It is getting a list of distinct combinations for any call data record to its matching POSSIBLE dial codes. Critical component is to put the order by each phone number dialed, THEN based on the longest dial code in descending order. This will put your "1800" at the top of the list per phone number using it.
Next comes the outer query where the MySQL #variables are applied. These work like in-line programming loop for you and goes for every record in the "PQ" result set. It starts the variables with blank and zero respectively.
Every record compares its dialed number to the last dialed number record (in cases like your 1800 and 1 multiple return sets). If they ARE the same phone, add 1 to the existing #Rank, otherwise, it is a change in phone numbers... always start a phone number change back to rank 1. THEN, it assigns the #lastDst to the phone number it just processed so it can be the basis of the next phone record being tested.
At the end is a HAVING clause to only include those of DialRank = 1
So, per your record set samples, the query would result in records looking something like...
Dial Number Dial_Code Tele2ID Longest DialRank ForNextRowCompare
18006927753 1800 1433 4 1 18006927753 <-- Keep this
18006927753 1 1422 1 2 18006927753
441138973356 441 1024 3 1 441138973356 <-- Keep this
Feedback per comment. TO handle your update, you can just wrap it up
update cdr,
( full query ) as FromThisQuery
where cdr.clean_dst = FromThisQuery.clean_dst
set tele2id = FromThisQuery.tele2id

Please try this query:
select dial_code, clean_dst from cdr c, dialing_codes d where c.clean_dst
like concat(d.dial_code, '%');
You don't need to code all that logic in php. MySQL gives you the functions and comparisons to do it natively in SQL, which is simpler and much more concise.
Hope this helps.

Related

How to add not existing record and return it with zero value in Mysqli

QUERY:
SELECT month(date_created), count(a.ticket_num)
FROM ticket as a
LEFT JOIN user_management as b on b.engineer_id = a.ticket_engineer
WHERE b.tl_id = 'sample_id'
AND year(date_created) = '2019'
GROUP BY extract(year from date_created), extract(month from date_created)
SAMPLE OUTPUT:
month | ticket_num
----------------------
2 | 12
4 | 24
6 | 78
EXPECTED SAMPLE OUTPUT:
month | ticket_num
----------------------
1 | 0
2 | 12
3 | 0
4 | 24
5 | 0
6 | 78
As you can see the above expected output, i'm trying to place all existing month in the first column and set all the count to zero if not existed in the second column. As of now, i only have the query for sorting the ticket count by month that is existed when the ticket is created.
There are different approaches to this problem. One is pure SQL for example.
But I would say a PHP based solution is simpler. Basically you need to get your data into array, then create a loop that outputs the desired months order, and have a condition that sees whether we have a corresponding row in our array and outputs ether the actual data or a zero accordingly.
The only tricky part is to have such an array that would let us check the data availability. For this we have to index it with month numbers. Not a big deal actually
$sql = "SELECT month(date_created), count(a.ticket_num) ...";
$res = $mysqli($sql);
$data = [];
while($row = mysqli_fetch_row($res)) {
$data[$row[0]] = $row[1];
}
Now $data is an array indexed by the month number. The rest is a primitive loop
foreach (range(1,12) as $month) {
echo $data[$month] ?: 0;
}
On a side note I would like to advertise using PDO as opposed to mysqli for your database interactions as this case clearly displays the superiority of the former. Using PDO we can get the indexed array right away, without an explicit loop, thanks to a special fetch mode:
$sql = "SELECT month(date_created), count(a.ticket_num) ...";
$data = $data = $pdo->query($sql)->fetchAll(PDO::FETCH_KEY_PAIR);
That's all!

PHP function to find the median of a column in MySQL

I have a database, db and in it a table, Table.
It looks somewhat like:
id | val
--------
1 | 45
2 | 35
3 | 23
4 | 49
5 | 67
6 | 12
7 | 0
8 | 87
9 | 46
(This is just an example data set. Actual data set is huge. And I need to work in least time possible.)
I need to find the median of the column val. Actually I need a php function to be used multiple times.
A similar question does exist: Simple way to calculate median with MySQL
I tried a few answers in this question, none of them worked for me. The accepted answer doesn't work since it used to work with an older version of SQL only.
PS: It should also work in the case of many duplicates.
just for fun i thought i try and do it all in MySQL, here's the sqlFiddle
SELECT
CASE
WHEN MOD((select count(*) as count from t),2)=1 THEN
(select val from
(select #row:=#row+1 as row,val
from t,(select #row:=0)r
order by val)t1
where t1.row = CEIL((select count(*) as count from t)/2)
)
ELSE
((select val from
(select #row:=#row+1 as row,val
from t,(select #row:=0)r
order by val)t1
where t1.row = (select count(*) as count from t)/2)+
(select val from
(select #row:=#row+1 as row,val
from t,(select #row:=0)r
order by val)t1
where t1.row = ((select count(*) as count from t)/2)+1))/2
END AS median
Just replace occurences of t with your table name, don't change t1.
Also if the table has no rows, it'll return NULL as median.
This query can be further reduced to the below (sqlFiddle)
SELECT #rowCount:=(select count(*) as count from t) AS rowcount,
(select AVG(val) from
(select #row:=#row+1 as row,val
from t,(select #row:=0)r
order by val)t1
where t1.row IN (FLOOR((#rowCount+1)/2),
CEIL((#rowCount+1)/2)
)
) as Median
It'll return 2 columns, a rowcount column and a median column. I put the rowcount column there because i didn't want to count from t multiple times like previous query.

How to get the next row in sql

I have a table that is something like this
id | names | value
1 Vicky 43
2 Erica 23
3 Rueben 33
4 Bob 54
5 Chris 60
Then I set them in order according to their value. Now the table looks like this.
id | names | value
5 Chris 60
4 Bob 54
1 Vicky 43
3 Rueben 33
2 Erica 23
Now the starting point is id 5 which has a name of Chris and a value of 60. My goal is, to get the next row which has an id of 4 and name of Bob and a value of 54.
You just need to limit the resultset:
SELECT * from table
ORDER BY value DESC
LIMIT 1, 1
Output:
| ID | NAMES | VALUE |
|----|-------|-------|
| 4 | Bob | 54 |
Fiddle here.
The LIMIT basically works this way: the first number sets the starting point (being 0 the minimal value) and the second number the amount of items to fetch (in this case only one).
Edit:
A different way of understanding the question would be: Given a value for a particular field (EG: id field with value of 5)... what would be the previous record? As we have the id 4 in the data we should return that one.
That could be accomplished this way:
SELECT * from t
WHERE id < 5
ORDER BY id DESC
LIMIT 1
Fiddle here.
This way you can traverse the results in both orders (ASC and DESC) and also get both the next or previous (> or <) rows.
If your current ID is for example 4 then
Next:
select * from foo where id = (select min(id) from foo where id > 4)
previous:
select * from foo where id = (select max(id) from foo where id < 4)
sql server:
with temp as
(
SELECT ROW_NUMBER() OVER (ORDER BY value desc) AS ROWID, * FROM table_name
)
SELECT * from temp where ROWID=2
mysql:
SELECT * from table
ORDER BY value DESC
LIMIT 1, 1
I get the feeling that this is a PHP related question?
If that's so, then you can use PHP's mysql or mysqli_fetch functions to get what you want... along with a loop
This is your basic loop-through-a-mysql-query
$sql = mysql_query( "SELECT * from table ORDER BY value DESC" );
while ( $r = mysql_fetch_array( $sql ) ) {
echo $r['value'] . "<br />\n";
}
If you want to have them all at your disposal and be able to call either one of them at will, you will need to store the data in an accessible array, like so
$sql = mysql_query( "SELECT * from table ORDER BY value DESC" );
$theData = array();
while ( $r = mysql_fetch_array( $sql ) ) {
$theData[] = $r['value'];
}
Then, to access the SECOND value, use this
echo $theData[1];

MSSQL Aggregated time query with multiple columns

In this example, I am collecting some engine data on a car.
Variables
--------------------------------------
id | name
--------------------------------------
1 Headlights On
2 Tire Pressure
3 Speed
4 Engine Runtime in Seconds
...
Values
--------------------------------------
id | var_id | value | time
--------------------------------------
1 1 1 2013-05-28 16:42:00.100
2 1 0 2013-05-28 16:42:22.150
3 2 32.0 2013-05-28 16:42:22.153
4 3 65 2013-05-28 16:42:22.155
...
I want to write a query that returns a result set something like the following:
Input: 1,2,3
Time | Headlights On | Tire Pressure | Speed
---------------------------------------------------------------
2013-05-28 16:42:00 1
2013-05-28 16:42:22 0 32 65
Being able to modify the query to include only results for a given set of variables and at a specified interval say (1 second, 1 minute or 5 minutes) are also really important for my use case.
How do you write a query in T-SQL that will return a time-aggregated multi column result set at a specific interval?
1 minute aggregate:
SELECT {edit: aggregate functions over fields here} FROM Values WHERE {blah} GROUP BY DATEPART (minute, time);
5 minute aggregate:
SELECT {edit: aggregate functions over fields here} FROM Values WHERE {blah} GROUP BY
DATEPART(YEAR, time),
DATEPART(MONTH, time),
DATEPART(DAY, time),
DATEPART(HOUR, time),
(DATEPART(MINUTE, time) / 5);
For the reason this latter part is so convoluded, please see the SO post here: How to group time by hour or by 10 minutes .
Edit 1:
For the part "include only results for a given set of variables", my interpretation is that you want to to isolate Values with var_id being within a specified set. If you can rely on the variable numbers/meanings not changing, the common SQL solution is the IN keyword (http://msdn.microsoft.com/en-us/library/ms177682.aspx).
This is what you would put into the WHERE clause above, e.g.
... WHERE var_id IN (2, 4) ...
If you can't rely on knowing the variable numbers but are certain about their names, you can replace the set by a sub-query, e.g.:
... WHERE var_id IN (SELECT id FROM Variables WHERE name IN ('Tire Pressure','Headlights On')) ...
The alternative interpretation is that you actually want to aggregate based on the variable ids as well. In this case, you'll have to include the var_id in your GROUP BY clause.
To make the results more crosstab-like, I guess you'll want to order by time aggregate that you're using. Hope that helps more.
Try
SELECT
VehicleID
, Case WHEN Name = 'Headlights on' THEN 1
Else 0 END ' as [Headlights on]
, Case WHEN Name = 'Tyre pressure' THEN Value
Else CAST( NULL AS REAL) END ' as [Tyre pressure]
, DateName(Year, DateField) [year ]
FROM
Table
ETC
Then agrregate as required
SELECT
VehicleID
, SUM([Headlights on]) SUM([Headlights on],
FROM
(
QUery above
) S
GROUP BY
VehicleID
, [Year]

How to Select 1 Row From Each 10 Rows in MySQL

I am recording a real time change of a given signal into my database table.
Then I draw line graph to visualize the change of the signal level.
I want to get (10n+1)th rows in the table to make a rough graph. 10 is also arbitrary. User may change it to another value.
Does someone know how to make this just using a MySQL Query
If no, I will go with PHP after selecting all the data.
Here my table structure is:
|id |signal1 |signal2 | signal 3 |
+----------+----------+----------+------------+
|1 |0.41452 | 1.32135 | 0.31231 |
...
If you have an auto_incrememt id column, you can select rows that are divisible by n
SELECT * FROM tableName1 WHERE MOD(id,10)=0;
// id divided by 10 with a remainder equal to 0 (exact)
or without sequential column id's
SELECT * FROM (
SELECT
#row := #row +1 AS rowNum, colName1
FROM (
SELECT #row :=0) r, tableName1
) ranked
WHERE rowNum % 10 = 1
If your table has auto increment id (intID), then you should try this code.There are only 26 records in my table.
select * from tablename where intId
in (select case (intId%10=0) when 1 then intId else 0 end as x from tablename )
Output :-
10 sdf KK201300010 123456 Regular
20 sdf KK201300020 123456 Regular
It will displaying each record in every 10 record.
Hope it will help you.

Categories