select different MAX(values) depending on array - php

I want to select different MAX(values) of different sensornodes. My Problem is that the WHERE...IN... clause works like many logical "OR", but I need the MAX($measure) from each sensornode. I know how to do it with a loop, but I think there is a better solution to do this.
This is the table 'measurement' of the database:
ID humidity temperature date time sensornode
---------- ---------- ----------- ---------- ---------- ----------
1 22.00% 18.00C 06/03/2017 13:07:18 WSN1
2 22.00% 18.00C 06/03/2017 13:08:19 WSN2
3 34.00% 21.00C 06/03/2017 13:09:19 WSN3
4 21.00% 20.00C 06/03/2017 13:10:19 WSN4
The query should be somthing like this
$measure //is either 'temperature' or 'humidity', depends on the users input.
$sensornode // is a string which is converted with the implode-function from an array which includes the selected 'sensornodes' of the users input
$sql_query = "SELECT MAX($measure) AS $measure
FROM measurement
WHERE sensornode IN ('$sensornode')";
$data = executeQuery($sql_query, $measure);

To get the maximum value for each sensor node, use grouping:
SELECT sensornode,
max(temperature)
FROM measurement
GROUP BY sensornode;
To restrict that to certain sensor nodes, just add a WHERE:
SELECT sensornode,
max(temperature)
FROM measurement
WHERE sensornode IN ('WSN1', 'WSN2', ...)
GROUP BY sensornode;

Related

MySQL/PHP - Need to be able to produce query results with certain columns having more weight than others

I have been tasked with creating a search function that when searched, certain fields will have more weight than others.
Here is an simplified example.
cars (table)
year, make, model, color, type (columns)
Let's say someone searches for the following:
Year: 1968
Make: Ford
Model: Mustang
Color: Red
Type: Sports Car
If the cars in the table have none of the correct fields they should not show up, but if record has some of the correct fields but not all they should still show up. But certain fields should be weighted higher than others.
For instance maybe they are weighted like this:
Column - Weight
Year - 30
Make - 100
Model - 85
Color - 10
Type - 50
So if a record matches the search in the "make" field and the "model" field, that record would be above a record that matched in the "year", "color" and "type" field, because of the weights we placed on each column.
So lets say that the query matches at least one field for two records in the database, they should be ordered by the most relevant based on the weight:
1971, Ford, Fairlane, Blue, Sports Car (weight = 185)
1968, Dodge, Charger, Red, Sports Car (weight = 90)
I have been racking my brain trying to figure out how to make this work. If anyone has done something like this please give me an idea of how to make it work.
I would like to do as much of the work in MySQL as possible via joins, I think this will be bring up the results faster than doing most of the work in PHP. But any solution to this problem would be much appreciated.
Thanks in advance
Bear with me, this is going to be a strange query, but it seems to work on my end.
SELECT SUM(
IF(year = "1968", 30, 0) +
IF(make = "Ford", 100, 0) +
IF(model = "Mustang", 85, 0) +
IF(color = "Red", 10, 0) +
IF(type = "Sports Car", 50, 0)
) AS `weight`, cars.* FROM cars
WHERE year = "1968"
OR make = "Ford"
OR model = "Mustang"
OR color = "Red"
OR type = "Sports Car"
GROUP BY cars.id
ORDER BY `weight` DESC;
Basically, this groups all results by their id (which is necessary for the SUM() function, does some calculations on the different fields and returns the weight as a total value, which is then sorted highest-lowest. Also, this will only return results where one of the columns matches a supplied value.
Since I don't have an exact copy of your database, run some tests with this on your end and let me know if there's anything that needs to be adjusted.
Expected Results:
+============================================================+
| weight | year | make | model | color | type |
|============================================================|
| 130 | 1968 | Ford | Fairlane | Blue | Roadster |
| 100 | 2014 | Ford | Taurus | Silver | Sedan |
| 60 | 2015 | Chevrolet | Corvette | Red | Sports Car |
+============================================================+
So, as you can see, the results would list the closest matches, which in this case are two Ford (+100) vehicles, one from 1968 (+30), and a Red Sports Car (10 + 50) as the closest matches (using your criteria)
One more thing, if you also want to display the rest of the results (ie results with a 0 weight match score) simply remove the WHERE ... OR ..., so it will check against all records. Cheers!
Further to the comments below, checking the weight after a LEFT JOIN on a pivot table:
SELECT SUM(
IF(cars.year = "1968", 30, 0) +
IF(cars.make = "Ford", 100, 0) +
IF(cars.model = "Mustang", 85, 0) +
IF(cars.color = "Red", 10, 0) +
IF(types.name = "Sports Car", 50, 0)
) AS `weight`, cars.*, types.* FROM cars
LEFT JOIN cars_types ON cars_types.car_id = cars.id
LEFT JOIN types ON cars_types.type_id = types.id
WHERE year = "1968"
OR cars.make = "Ford"
OR cars.model = "Mustang"
OR cars.color = "Red"
OR types.name = "Sports Car"
GROUP BY cars.id
ORDER BY `weight` DESC;
Here is a picture of the LEFT JOIN in practice:
As you can see, the Cobalt matches on color (silver) and model (Cobalt) (85 + 10) while the Caliber matches on type (Sports Car) (50). And yes, I know a Dodge Caliber isn't a Sports Car, this was for example's sake. Hope that helped!
If I understand your logic you can just do something like direct comparison in PHP between the value requested and the value returned.
The query will sound like:
SELECT Year,Make,Model,Color,Type
FROM table
WHERE year='$postedyear' OR make='$postedmake'
OR model='$postedmodel' OR color='$postedcolor'
Then in php looping between the results:
foreach($results as $result){
$score = 0;
if($result['year']==$postedyear{$score=$score+30;}
//continue with the other with the same logic.
}
After each foreach iteration $score will be the score of that selected row. If you push the score to the $result array you can also sort it by score before displaying the results.
Variation on #lelio-faieta
In php you can have a result array containing arrays of values for each item matching at least one of the search terms, the associative array of values to match and the associate array of weights, both with the same indexes. You would just get an array of matches for each index. (maybe use array_intersect_assoc()) Then you multiply by the weights and sum, add to the original data. Then you do have to sort the result array at that point.
There is a solution doing this via the mysql query directly, but that would end up with an overgrown resource thirsty query for every single search you perform.
Doing it in PHP is not much difference in resource usage, bounding to several loops in results and processing it.
I've had a very similar project and my best suggestion would be: "use SphinxSearch"
Very easy to install, needs a bit of a learning curve to setup afterwards, but very similar to mysql queries etc. With this you can apply weights to every column match and rank your results afterwards.
Also, it is a multitude of time faster that typical mysql queries.

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]

Retriving an array of grouped elements in mysql (+php)

I need i bit of help with this query, so far i have this:
SELECT * FROM coupons WHERE discount_id = '1' AND client_username = 'Zara' GROUP BY winner_id
The table is like this
id client_username winner_id bdate discount_id destroyed
72 zara 1125405534 2012-11-11 03:34:49 4 0
71 zara 1125405534 2012-11-11 03:34:43 1 0
70 zara 1125405534 2012-11-11 03:34:27 1 0
I want to group the result by winner_id (its a unique user id) where discount_id is equal to some value and order by bdate, the think is I need the id bdate and destroyed value of each ocurrence of the user and also count the number of times winner_id appear, so the result needs to be a value (count of how many times winner_id appears), and 3 arrays (discount_id,destroyed,id).. But I have no idea how to retrive this in the way I need. Thanks for any help!
Two basic methods:
aggregate in mysql and "explode" in php
aggregate in PHP
number 1 involves using some aggregate functions in your query, like COUNT() and GROUP_CONCAT():
SELECT count(*) as num_wins, GROUP_CONCAT(discount_id, ',') as discount_ids ...
then in PHP, these GROUP_CONCAT columns can be "exploded" into arrays while looping over the results:
foreach($rows as $row) {
$discount_ids = explode(',', $row['discount_ids']);
// ...
}
number 2 is easier SQL, but uglier PHP. Basically just select all your rows, and then pre-process the results yourself. (I recommend the previous solution)
foreach($rows as $row) {
$results_tree[$row['winner_id']]['num_wins']++;
$results_tree[$row['winner_id']]['discount_ids'][] = $row['discount_id'];
// ...
}

Showing statistics for mysql database

I have a database table. It contains the folowing columns:
Cathegory | Priority |
Is there a finished script or a way to display statistics for these columns?
Basically what I am trying to do is show statistics for these columns.
For example,
cathegory can have different values, as an example: (Continent,
Country, City, Street).
Priority can contain a value between 1-10.
So I would need to display how many rows there are, the and the different values for each row.
For example:
4 of the priority 8 rows have 'continent' as catheogry,
43 of the priority 8 rows have 'country' as cathegory,
329 of the priority 8 rows have 'city' as cathegory
Is this possible?
There are no built-in scripts that can do that for you but certainly you get all that kind of information using SQL, that's the basic idea of a relational database.
Number of rows in table
select count(*) from table;
The example
select cathegory, count(cathegory) nbr_of_cathegories_for_prio_8 from table where priority = 8 group by cathegory;
In you example : 329 of the priority 8 rows have 'city' as cathegory
I assume that:
8 - is the priority value.
329 - how many time the priority is repeated for that specific priority for a specific category.
the PHP implimentation will look something like:
<?php
$sql = "
SELECT Priority, COUNT(Priority) as nbr_of_Priorities, cathegory,
FROM table_Name
GROUP BY Priority, cathegory
";
$result = mysql_query($sql);
while($row = mysql_fetch_array($result))
{
echo $row['nbr_of_Priorities'].'of the priority'.$row[' Priority'];
echo 'has'.$row['cathegory'].'as catheogry';
}
?>

MYSQL : How to set to same position if row value is the same?

position | Average | gpmp
1 70.60 2.0
2 60.20 2.3
3 59.80 4.8
4 59.80 4.8
5 45.70 5.6
Hie All,
As above table, I need to arrange the position according to the lowest gpmp and the highest average. But when the both average and gmp are the same, I will need to have the position to be the same.
For example, position 3 and 4 have the same average and gpmp. How do I generate the mysql query or using php function so that after they detect the same average and gpmp and change the position 4 to 3.
Which mean after the function is generated it will become like the table below.
position | Average | gpmp
1 70.60 2.0
2 60.20 2.3
3 59.80 4.8
3 59.80 4.8
5 45.70 5.6
Here's a simple way to update the table as you described in your post - taking the sequential positions and updating them accordingly. It doesn't calculate the positions or anything, just uses the data already there:
UPDATE `table` t SET position = (
SELECT MIN(position) FROM (SELECT * FROM `table`) t2 WHERE t.Average = t2.Average AND t.gpmp = t2.gpmp
)
I'd give something like the following a try, through it does assume a primary key is on this table. Without a primary key you're going to have issues updating specific rows easily / you'll have a lot of duplicates.
So for this example I'll assume the table is as follows
someTable (
pkID (Primary Key),
position,
Average,
gpm
)
So the following INSERT would do the job I expect
INSERT INTO someTable (
pkID,
position
)
SELECT
someTable.pkID,
calcTable.position
FROM someTable
INNER JOIN (
SELECT
MIN(c.position) AS position,
c.Average,
c.gpm
FROM (
// Calculate the position for each Average/gpm combination
SELECT
#p = #p + 1 AS position,
someTable.Average,
someTable.gpm
FROM (
SELECT #p:=0
) v,someTable
ORDER BY
someTable.Average DESC,
someTable.gpmp ASC
) c
// Now regroup to get 1 position for each combination (the lowest position)
GROUP BY c.Average,c.gpm
) AS calcTable
// And then join this calculated table back onto the original
ON (calcTable.Average,calcTable.gpm) = (someTable.Average,someTable.gpm)
// And rely on the PK IDs clashing to allow update
ON DUPLICATE KEY UPDATE position = VALUES(position)
(pseudo code)
select * from table
get output into php var
foreach (php row of data)
is row equal to previous row?
yes - don't increment row counter, increment duplicate counter
no - increment row counter with # of duplicates and reset duplicate counter
save current row as 'previous row'
next
you can try something like this in php:
$d= mysql_query('select distinct gpmp from tablename order by gpmp');
pos= 1;
while($r= mysql_fetch_array($d)){
mysql_query('update tablename set position='.$pos.' where gpmp='.$r['gpmp']);
$pos++;
}
You only need to "expand" the idea to take averange in account too.

Categories