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.
Related
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'
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;
SELECT product.productID
, product.Name
, product.date
, product.status
FROM product
INNER JOIN shelf ON product.sheldID=shelf.shelfID
WHERE product.weekID = $ID
AND product.date < '$day'
OR (product.date = '$day' AND shelf.expire <= '$time' )
ORDER BY concat(product.date,shelf.expire)
I am trying to stop the SQL statement at a specific value e.g. bad.
I have tried using max-date, but am finding it hard as am making the time stamp in the query. (Combining date/time)
This example table shows that 3 results should be returned and if the status "bad" was the first result than no results should be returned. (They are ordered by date and time).
ProductID Date status
1 2017-03-27 Good
2 2017-03-27 Good
3 2017-03-26 Good
4 2017-03-25 Bad
5 2017-03-25 Good
Think I may have fixed it, I added this to my while loop.
The query gives the results in order by present to past using date and time, this while loop checks if the column of that row is equal to 'bad' if it is does something (might be able to use an array to fill it up with data). If not than the loop is broken.
I know it doesn't seem ideal but it works lol
while ($row = mysqli_fetch_assoc($result)) {
if ($row['status'] == "bad") {
$counter += 1;
}
else{
break;}
I will provide an answer just with your output as if it was just one table. It will give you the main ideia in how to solve your problem.
Basically I created a column called ord that will work as a row_number (MySql doesn't support it yet AFAIK). Then I got the minimum ord value for a bad status then I get everything from the data where ord is less than that.
select y.*
from (select ProductID, dt, status, #rw:=#rw+1 ord
from product, (select #rw:=0) a
order by dt desc) y
where y.ord < (select min(ord) ord
from (select ProductID, status, #rin:=#rin+1 ord
from product, (select #rin:=0) a
order by dt desc) x
where status = 'Bad');
Result will be:
ProductID dt status ord
-------------------------------------
1 2017-03-27 Good 1
2 2017-03-27 Good 2
3 2017-03-26 Good 3
Also tested with the use case where the Bad status is the first result, no results will be returned.
See it working here: http://sqlfiddle.com/#!9/28dda/1
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]
;
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;