Calculate Average between MySQL results - php

I need to calculate the avg days between date of sales:
My DB is like this:
id | customer | creation_date | payment_date
1 | 234 | 2017/07/6 | 2017/07/8
34 | 234 | 2017/08/4 | 2017/08/10
53 | 234 | 2017/09/15 | 2017/09/17
67 | 234 | 2017/10/1 | 2017/07/6
So I need to calculate de difference of days (creation_date) between Order 1 and Order 34, Order 34 and Order 53, Order 53 and Order 67, etc...
and calculate an AVG of days depending the number of results.
So I know how to calculate the difference of days between 2 dates using this small script:
$seconds=strtotime($date1) - strtotime($date2);
$difference=intval($seconds/60/60/24);
$positive = $difference * -1;
but I donĀ“t know how to take the date of the las result and compare it with the next result.
Please someone who can help me with this enigma. Thanks!

I could be misunderstanding what you are looking for, but I would think something like this should work
(TO_DAYS(MAX(creation_date))-TO_DAYS(MIN(creation_date))) / (COUNT(1)-1)
This will get you the total days between the first and last; and divide by the number of "spaces" between orders.
Edit: ....and if you wanted to treat orders on the same date as a single order, you can just change COUNT(1) to COUNT(DISTINCT creation_date).
...all this assumes the db designer was sane and actually used DATE data types for date values.
To summarize, the average of the span sizes should be the same as the total span divided by the number of spans.

You can keep track of the previous result using a variable outside of the loop to get your MySQL table and then run the loop through the rows of the table:
$last_positive = 0;
while ($row = $result->fetch_assoc()){
$date1 = $row['creation_date'];
$date2 = $row['payment_date'];
$seconds=strtotime($date1) - strtotime($date2);
$difference=intval($seconds/60/60/24);
$positive = abs($difference);
//DO SOME COMPARISON HERE
echo($last_positive >= $positive);
$last_positive = $positive;
}
I'd also suggest using abs to get the absolute value instead of multiplying by -1.

SOLVED WITH THIS:
SELECT DATEDIFF(MAX(creation_date), MIN(creation_date)) / (COUNT(creation_date) - 1) AS SaleAverage FROM table WHERE customer = '$customer'

Related

laravel check amount if in between the records

I have a weight calculated, and a database table that contains the range of amounts, now I want to check if the calculated weight is between the ranges in my table.
I want to check the amount if it's between in one of the records in the table.
For example the calculated weight is 505kg it will find it on the table if it's between weight_from & weight_to, so it will return the 120 amount because 505kg is between 500 and 1000
Shipping Fee Table
+-----+---------------+--------------+-------------+
| id | weight_from | weight_to | amount |
+-----+---------------+--------------+-------------+
| 1 | 0.5 | 100 | 100 |
| 2 | 500 | 1000 | 120 |
| 3 | 1000 | 3000 | 180 |
+-----+---------------+--------------+-------------+
I found something like this, but it is not like what I'm trying to do, because it pass a number, and not getting the value of database table.
Code for reference only
$fee = Fee::whereBetween('column', [1, 150])->first();
whereBetween() is for finding values in a range within a single column. Since you are checking between two different columns, the checks needs to be explicit.
$weight = 505;
$fee = Fee::where('weight_from', '<=', $weight)
->where('weight_to', '>=', $weight)
->orderBy('weight_from')
->first();
if ($fee) {
// Do something with $fee->amount;
}
You shouldn't use <, the ranges should be inclusive. The reason for this, contrary to the other answer that states "use > for the upper-bound if the next range starts from the same value", is that the range may not always have the same upper-limit as the start-limit of your next batch (for example, the sample data provided has a range from 0.5 to 100, but none from 100-500). If it doesn't, then suddenly you're missing a value. Its better to use orderBy() to find the correct record. Combine that with first() and you only get one record.
I got it now, I want to share this simple yet helpful solution. This kind of logic is so confusing.
Code
$weight = 505;
$fee = Fee::where('weight_from', '<=', $weight)
->where('weight_to', '>', $weight)
->first();
if ($fee) {
// Do something with $fee->amount;
}
The weight_from should be less (or equal) to the calculated weight, and weight_to should be greater than the calculated weight
Also, use > for the upper-bound if the next range starts from the same value.
Thanks to tykus and MichalOravec of Laracasts for helping me

SQL WeekOfYear() stops at end of the year. Alternatives?

I have a query that gets executed based on the date range the user chooses. For example: 12-12-2019 to 1-13-2020.
// Retrieve count of attendance, no shows and cancellations per user selected category and sort by week number
$q = "SELECT YEARWEEK(`start_time`, 0) AS weekno,
SUM(`is_no_show` = 0 AND `is_cancelled` = 0) as attended,
SUM(`is_no_show` = 1) AS no_shows,
SUM(`is_cancelled` = 1)AS cancelled
FROM `myTable`
WHERE (`start_time` > :start_date AND `start_time` < :end_date)
AND category LIKE :cohort_type
GROUP BY weekno";
My issue is that this query stops pulling in data after 12-23-2019. It seems to stop at the last week of the year and not go into 2020 as week 1. How do I account for this? Any suggestions or tips is greatly appreciated!
Thank you.
General DB Structure:
+------------+-----------+-----------+
| start_time | no_shows | cancelled |
+------------+-----------+-----------+
| 2019-12-20 | 1 | 0 |
| 2019-12-21 | 0 | 0 |
| 2019-12-22 | 0 | 1 |
GOAL: I want to SUM the data on a weekly basis
EDIT: YEARWEEK() skips the first week of 2020 and goes straight to week 2.
Your query is missing a GROUP BY criteria on the year; as of now, it will mix weeks belonging to different years, which I assume is not what you want. I would suggest using YEARWEEK(), which takes the year in account.
Also, your ilter on the date seems akward, as it is mixing parameters and string concatenation; you can use half-open intervals instead (and proper parameter bindings).
Consider:
SELECT
YEARWEEK(`start_time`) AS weekno,
SUM(`is_no_show` = 0 AND `is_cancelled` = 0) attended,
SUM(`is_no_show` = 1) no_shows,
SUM(`is_cancelled` = 1) cancelled
FROM `attendee_categories_appts_joined`
WHERE
`start_time` > :start_date
AND `start_time` < :end_date
AND category LIKE :cohort_type
GROUP BY weekno

PHP SQL SELECT sum(value) WHERE x LIKE '%$month%' (foreach)

I have a table:
user_id | month | value
1 | firstname_1_2017 | 5
1 | secondname_1_2017 | 4
1 | firstname_2_2017 | 7
1 | secondname_2_2017 | 8
1 | firstname_3_2017 | 5
1 | secondname_3_2017 | 4
1 | firstname_4_2017 | 7
1 | secondname_4_2017 | 8
I need sum "value" for each month with a foreach.
firstname_1_2017 + secondname_1_2017 = 9
firstname_2_2017 + secondname_2_2017 = 15
I have an array for the months:
$array_months = array( '1','2','3','4','5','6','7','8','9','10','11','12' );
So I wrote (in php):
SELECT sum(value)
FROM table
WHERE user_id = $user_id
AND month LIKE '%_$array_months_2017%'
When I write a foreach, the result is the same sum of all values repeat for each month (Example: Jan 48 points, Feb 48 points, Mar 48 points...).
I'd like this result with a foreach:
You have "9" points in January
You have "15" points in February
etc...
You should think about changing your DB design to separate Month and Year from user's name.
Not a good approach, but a solution will be to extract month year from month string and group it to calculate sum for each month.
SELECT SUBSTR(month, LOCATE('_', month) + 1) AS month_year, SUM(value)
FROM users
WHERE user_id = 1
GROUP BY month_year;
Working Fiddle
You can extract the numbers from your month column values with the SUBSTRING_INDEX() function like this, giving 3_2017.
SUBSTRING_INDEX('firstname_3_2017', '_', -2)
You can then transform that to a date-ish string with CONCAT(), yielding 1_3_2017.
CONCAT('1_',SUBSTRING_INDEX('firstname_3_2017', '_', -2))
Then you can turn that into an actual DATE value with STR_TO_DATE().
STR_TO_DATE(CONCAT('1_',SUBSTRING_INDEX('firstname_3_2017', '_', -2)),'%d_%m_%Y')
Cool, now you have a datatype you can actually work with. You can now use an aggregate query to get good stuff. Leveraging Samir's fiddle: http://sqlfiddle.com/#!9/8aabd/6/0
SELECT
SUM(value) value,
user_id,
STR_TO_DATE(CONCAT('1_',SUBSTRING_INDEX(month, '_', -2)),'%d_%m_%Y') month_begin
FROM table
GROUP BY user_id, STR_TO_DATE(CONCAT('1_',SUBSTRING_INDEX(month, '_', -2)),'%d_%m_%Y')
Summary: simple transformations of your month column can give you usable information.
Pro tip: when designing your tables try not to stuff values with different meanings into single columns. Read about normalization.
Pro tip 2: use actual DATE, TIMESTAMP, or DATETIME values for storing time-based information whenever possible. Date arithmetic and date-based indexing are powerful parts of DBMS servers.

Splitting website traffic by percentage

I need to split up traffic to multiple sources based on an assigned percentage. I figure I need a log table like this:
Table:
+--------+------+----------------------+
| Source | hits | allocated percentage |
+--------+------+----------------------+
| path1 | 50 | 50 |
| path2 | 40 | 40 |
| path3 | 10 | 10 |
+--------+------+----------------------+
I figure the logic needs to loop through all the paths and calculate the current percentage and then determine which one is furthest from the "allocated percentage" and then update the table hits=hits+1. I'm having trouble with the last compare part.
$overall_hits = $db->getall('Select sum(total_hits) from table');
$source = $db->getall('Select * from table');
foreach($source as $row){
$current_percentage = ($row['total_hits']/$overall_hits)*100;
//how should I compare? what if they are equal?
if($current_percentage < $row['allocated_percentaged'])
{
$chosen_path = $row['source'];
$db->sql("Update table set total_hits=total_hits+1 where source='".$chosen_path."'");
break;
}else{
continue;
}
}
Am I even on the right track here?
Presuming I understand what you're trying to do, you can do all of the logic checks in your SQL.
Using the following data as an example:
CREATE TABLE t (
source TEXT,
hits INT,
percentage INT
);
INSERT INTO t (source, hits, percentage)
VALUES
('path1', 41, 50),
('path2', 27, 40),
('path3', 3, 10)
You can simply run a query against the entire table, to calculate what percentage each of the paths is at:
SELECT
source,
hits,
percentage,
(hits / percentage) * 100
AS current
FROM t
ORDER BY current ASC;
Which will give you the following results
SOURCE HITS PERCENTAGE CURRENT
path1 3 10 30
path2 27 40 67.5
path3 41 50 82
You can then simply add LIMIT 1 to the end of your query, to only obtain 1 result. This will give you the path with the lowest number of hits : allocated ratio.
SOURCE HITS PERCENTAGE CURRENT
path1 3 10 30
You can see it in action on SQLFiddle here.

MYSQL query for every hour

$today = mktime(0,0,0);
$yesterday = $today - 86400;
$this->view->numberyesterday=$db->fetchAll("SELECT * FROM `transactions` WHERE `siteid`=".$sid." AND `timestamp` > ".$db->quote($yesterday,'integer')." AND timestamp < ".$db->quote($today,'integer'));
I have used the above code to display all the records of yesterday. Now I need to plot the graph for each hour. How can I retrieve the records in every hour from the above query?( like, records within 12-1 AM, 1-2 AM....23-24PM etc)
Thanks!
Edit:
I wish I could store the records of each hour in each variable. So that there will be 24 variable for 24 hours. So it is easy to draw the graph.
You did not specify how the output would look like, but it seems like this construct would help you:
select Hour(timestamp) transhour, count(*) transcount
from transactions
where yourfilters = yourvalues
Group by Hour(timestamp)
The output will look like this:
transhour | transcount
======================
0 | 2
1 | 23
2 | 45
3 | 23
... | ...
22 | 34
23 | 3
Warning: if no transactions were performed in a given hour, the output will omit this hour (and not bring it back with count zero)

Categories