How to find average number of days between orders? [duplicate] - mysql

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

Ignore empty values on average math, but show it as zero on result

I need to calculate the Average value of fields, but two things needs to happen:
1- The empty values should NOT be counted for the average math.
2- If the field is empty it still must be shown in the result (with avg === 0)
Imagine that I have this dataset:
-----------------------
Code | valField | Date
-----------------------
A | | 2020-09-08
B | 12 | 2020-09-09
A | 10 | 2020-09-08
B | 15 | 2020-09-09
B | | 2020-09-09
C | | 2020-09-09
So I need the average of the day. As you can see, we have:
A = { empty, 10 }
B = { 12, 15, empty }
C = { empty }
I need to make the average like this:
Average of A = 10
Average of B = (12+15)/2 (because we have 2 non-empty values)
Average of C = 0 (It has not a single value, but I need it to show on result as 0)
So far I could accomplish both of the requirements, but not in the same time.
This query will show empty values BUT will also count empty fields on average math
SELECT AVG(valField) FROM myTable;
So Average of B would be = (12+15+0)/3 - wrong!
Now this will ignore empty values, the AVG math will be correct, but C would NOT be shown.
SELECT AVG(valFIeld) FROM myTable WHERE valField <> ''
How may I accomplish both requirements?
From your comment I understood, you have valField defined as varchar, so you can use next trick:
select
Code,
coalesce(avg(nullif(valField, '')), 0) as avg_value
from tbl
group by Code;
Test the query on SQLize.online
Here I used NULLIF function for convert empty values to null before calculate the average
I think you want:
SELECT code, COALESCE(AVG(valField), 0) FROM myTable GROUP BY code
This assumes valField is of a numeric datatype, and that by empty you mean null.
Here is what happens behind the hood:
avg(), as most other aggregate functions, ignores null values
if all values are null, then avg() does return null; you can replace that with 0 using coalesce()
That should be easy just create two queries one that calculates the average using non null values and the other one calculating the codes having no value in the data.
select round(avg(valField)) as avg, code from new where valField is not null group by Code
union all
select 0 as avg, code from new group by Code having avg(valField) is null;

MySQL group by date smaller then multiple dates

this is a hard one(i think).
i need to group a table by multiple dates . the tricky part is that i dont need the group to be the specific date , i need it to be all values that smaller then the date. so if it was only for one date my query will looks like this.
select count(*) from users where created_at <= '2016-01-01'
so my current and rely bad solution is to just do as many query's as many dates that i have. for example if i have and array of 10 dates that mean my code will look like this
for(let i = 0; i< dates.length;i++){
db.query(`select count(*) from users where created_at <= '${dates[i]}'`)
}
desired output:
dates = [ '2016-06-01' ,' 2016-04-01' ,'2016-02-01']
table users:
id | created_at
1 | 2016-01-01
2 | 2016-02-01
3 | 2016-03-01
4 | 2016-04-01
5 | 2016-05-01
6 | 2016-06-01
outpout:
date | count
'2016-06-01' | 6
'2016-04-01' | 4
'2016-02-01' | 2
is there a way to achieve this with 1 query?
//explained logic using java but hope this will help:)
you can achieve this using union,
first concat all dates and make query using union,
String sqlPart="";
String union="";
for(int i = 0; i< dates.length;i++){
sqlPart=sqlPart+union;
sqlPart=sqlPart + "SELECT created_at , count(*) FROM users WHERE created_at <= "+ dates[i];
union="union";
}
so your final query will form in 'sqlPart'
then run this query
There is way to get it in one database hit, yes some logical things need to do:
Dynamically generate query as like below :
query = "SELECT "
for(let i = 0; i< dates.length;i++){
if(i>0) query +=",";
query += "SUM(if(created_at <='${dates[i]}',1,0))";
}
query += " from users ";
if you know max date in advance then add below part also to get it faster :
query += " WHERE created_at<= 'Maxdate' ";
And finally hit database.
db.query(query);
Here you will get the result like this :
6,4,2
Now here each column value will give the count for date in same sequence of array.
Example :
First value 6 is count for 1st array value which is 2016-06-01 and so on, which can be formatted in any format base on requirement.
Still you can improve this better by applying your logical things.
This is not exact compiled code, take it as reference to achieve the same.
Check this please:
SELECT created_at , count(*)
FROM users
WHERE created_at < '2016-01-01'
GROUP BY created_at;
Query will return grouped dates with count of rows for each date.

If more than 10% of results are over X in mysql

I have a database table with lists of temperature readings from many locations in a number of buildings. I need a query that will give me a true or false if more than 10% of the readings in a building, taken on a date, are greater than X
I am not looking for a average. If there are 100 measurements taken in a building on a date, and 10 of them are over X (say 80 degrees) then create a flag.
The table is laid out as
Building # location # date temperature
| 123 | 555 |2016-04-08 | 68.5 |
| 123 | 556 |2016-04-08 | 70.2 |
| 123 | 557 |2016-04-08 | 65.4 |
| 888 | 999 |2013-03 22 | 80.4 |
Typically a building would have over 100 readings. There are many hundreds of building/date entries in the table
Can this be done with a single mysql query and can you share that query with me?
I obviously haven't made my question clear.
The result I am looking for is a single True or False.
If more than 10% of the results for a building/date combination were over X (say 80%) then show true, or some flag equal to true.
The known fields will be building and date. The location is not relevant, and can be ignored. So given the input of building (123) and date (2016-04-08) are more than 10% of the entries in the table that have that building number and date greater than X (e.g. 80). The only data to be tested are those for that building and date. So the query would end in:
where building_id=`123` AND date =`2016-04-08`
I am NOT looking for an average or a median. I am NOT looking to see a list of the data for that 10%. I am just looking for true or false.
You can use conditional aggregation, something like this:
select building, date,
(case when avg(temperature > x) > 0.1 then 'Y' else 'N' end) as flag
from t
group by building, date;
To return building and date, and "create a flag" for rows where more than 10% of the readings for that building on that date are over a given value X ...
SELECT r.building
, DATE(r.date)
, ( SUM(r.reading > X ) > SUM(.10) ) AS _flag
FROM myreadings r
GROUP BY r.building, DATE(r.date)
Absent more specification about the actual resultset you want to return, we're just guessing at what result set you want to return.
FOLLOWUP
Based on the update to the question... to return a row for a single building and a single date, add the WHERE clause as shown in the question. And remove expressions from the SELECT list.
SELECT ( SUM(r.reading > X ) > SUM(.10) ) AS _flag
FROM myreadings r
WHERE r.building = '123'
AND r.date >= '2016-04-08'
AND r.date < '2016-04-08' + INTERVAL 1 DAY
If there are no rows for the given building and given date, the query will return zero rows. If there is at least one row, and the number of rows that have a reading greater than X is more than 10% of the total number of rows, the query will return a single row, with _flag having a value of 1 (TRUE). Otherwise, the query will return a single row with _flag having a value of 0 (FALSE).
If you want the query to return a row even when there are no matching rows in the table, that can be accomplished with a more complex SQL statement.
If you want the query to return string values 'TRUE' or 'FALSE', that can be accomplished as well.
Again, absent an example of the resultset you are expecting to have returned, (without an actual specification which we can compare a resultset to), we're just guessing.

Calculate percentage in mysql from the same column

How do i calculate the percentage from the same column, between two dates?
I have column latest.
For example: 2015-11-16 the value was 159,4 and today the value is 160,1.
And between these dates there is other values, that i´m not interested in at the moment. How to i calculate the percentage difference from that specific date, compared to "todays" date.
EDIT
SELECT curr.latest * 100 / NULLIF(prev.latest, 0) as percentage
FROM myTable AS curr, myTable AS prev
WHERE date(prev.timestamp) = '2015-11-16'
AND date(curr.timestamp) = CURDATE()
AND curr.the_row_namn = 'apple'
Percentage of column latest of a specific date ex. 2015-11-16, with the the_row_namn of apple from the table myTable.
What is the percentage difference of column latest for apple of the day 2015-11-16 to today.
+--------------+--------+------------+
| the_row_namn | latest | timestamp |
+--------------+--------+------------+
| apple | 159,40 | 2015-11-16 |
| apple | 164,1 | 2015-11-17 |
+--------------+--------+------------+
Expected output: (rounded) percentage: 0,2864 or even better if that is possible 2,8%
Hope this will clarify things.
You could do this with the following SELECT statement:
SELECT curr.name,
curr.value * 100 / NULLIF(prev.value, 0) as percentage
FROM myTable AS curr
INNER JOIN myTable AS prev
ON curr.name = prev.name
WHERE prev.latest = '2015-10-26'
AND curr.latest = CURDATE()
AND curr.name = 'apple';
If you leave out the last condition, you'll get a result per possible value of name.
The main point of this query is that you use your table twice, once to select the current record, and once to retrieve the earlier record. You should just specify the date of your choice in the first line of the where clause.
Once you have both records, it is straightforward to calculate a percentage from the value columns of these two records.
The NULLIF is there to protect you from division by zero errors. Instead you'll get a NULL result.
This is the general form such a query can take:
SELECT a.value / b.value AS abPerc
FROM the_table AS a
INNER JOIN the_table AS b ON [pairing criteria]
;

Given a date find the previous &/or current and next x# Dates in MySQL non-linear

I have a table code_prices that looks something like this:
CODE | DATE | PRICE
ABC | 25-7-2011 | 2.81
ABC | 23-7-2011 | 2.52
ABC | 22-7-2011 | 2.53
ABC | 21-7-2011 | 2.54
ABC | 20-7-2011 | 2.58
ABC | 17-7-2011 | 2.42
ABC | 16-7-2011 | 2.38
The problem with the data set is there are gaps in the dates, so I may want to look for the price of item ABC on the 18th however there is no entry because the item wasnt sold on this date. So I would like to return the most recent hisotrical entry for the price.
Say if I query on the date 19-7-2011, I would like to return the entry on the 17th then the next 10 avalaible entries.
If however I query for the price of ABC on the 20th, I would want to return the price on the 20th and the next 10 prices after that...
What is the most efficient way to go about this either in SQL statement or using a stored proc.
I can think of just writing a stored proc which takes the date as a param and then querying for all rows where DATE >= QUERY-DATE ordering by the date and then selecting the 11 items (via limit). Then basically I need to see if that set contains the current date, if it does then return, otherwise I will need to return the 10 most recent entires out of those 11 and also do another query on the table to return the previous entry by getting the max date where date < QUERY-DATE. I am thinking there might be a better way, however I'm not an expert with SQL (clearly)...
Thanks!
This is for one specific code:
SELECT code, `date`, price
FROM code_prices
WHERE code = #inputCode
AND `date` >=
( SELECT MAX(`date`)
FROM code_prices
WHERE code = #inputCode
AND `date` <= #inputDate
)
ORDER BY `date`
LIMIT 11
For ABC and 19-7-2011, the above will you give the row for 17-7-2011 and the 10 subsequent rows (20-7-2011, 21-7-2011, etc)
I'm not entirely clear on what you want to achieve, but I'll have a go anyway. This searches for the ID of the row that contains a date less than or equal to your specified date. It then uses that ID to return all rows with an ID greater than or equal to that value. It assumes that you have a column other than the date column on which the rows can be ordered. This is because you said that the dates are non-linear - I assume that you must have some other way of ordering the rows.
SELECT id, code, dt, price
FROM code_prices
WHERE id >= (
SELECT id
FROM code_prices
WHERE dt <= '2011-07-24'
ORDER BY dt DESC
LIMIT 1 )
ORDER BY id
LIMIT 11;
Alternative with code condition - thanks to #ypercube for highlighting that ;-)
SELECT id, code, dt, price
FROM code_prices
WHERE code = 'ABC'
AND id >= (
SELECT id
FROM code_prices
WHERE dt <= '2011-07-23'
AND code = 'ABC'
ORDER BY dt DESC
LIMIT 1 )
ORDER BY id
LIMIT 11;