I have a table called 'orders' and it contains; id, order_total and time fields. 'time' is an integer and stores a unix timestamp...
orders
| id | order_total | time |
-------------------------------------
| 1 | 42.00 | 1443355834 |
| 2 | 13.00 | 1443460326 |
| 3 | 51.00 | 1443468094 |
| 4 | 16.00 | 1443477442 |
| 5 | 10.00 | 1443606966 |
| 6 | 53.00 | 1443608256 |
I want to able to display in a table using php the sum, of 'order_total' for each day for the previous 'x' amount of days (or weeks or months) so it will look something like this:
| Date | Order Total |
---------------------------
| 27/09/15 | 42.00 |
| 28/09/15 | 80.00 |
| 30/09/15 | 63.00 |
I have made a MYSQL query and a php loop that kind of works but being new to MYSQL I am probably over-complicating things and there must be an easier way to do this ? When I say kind of works, it will correctly sum and show the order_totals up until the current day but for some reason will combine the current day with the previous day.
Here is what I currently have:
$x = $interval;
$y = $x - 1;
while ($x > 0) {
$sql10 = "
SELECT id,
time,
SUM(order_total) as sum,
date_format(DATE_SUB(FROM_UNIXTIME($now_time), INTERVAL $x DAY), '%Y-%m-%d') as thedate
FROM $ordersTABLE
WHERE FROM_UNIXTIME(time) BETWEEN date_format(DATE_SUB(FROM_UNIXTIME($now_time), INTERVAL $x DAY),'%Y-%m-%d')
AND date_format(DATE_SUB(FROM_UNIXTIME($now_time), INTERVAL $y DAY),'%Y-%m-%d')";
$result10 = mysql_query ( $sql10, $cid ) or die ( "Couldn't execute query." );
while ( $row = mysql_fetch_array ( $result10) ) {
$order_total = $row ["order_total"];
$thedate = $row ["thedate"];
$sum = $row ["sum"];
$sum = number_format($sum,2);
$thedate = strtotime($thedate);
$thedate = date("d/m/y",$thedate);
print "<tr><td width=\"120\">$thedate</td><td>\$$sum</td></tr>";
}
$x--;
$y--;
}
(The string $now_time contains the current time as a Unix Timestamp hence the converting as the system time can not be changed and this contains the correct local time for the user)
Is there better way to do this ?
You can convert the timestamps into YYYY MM DD using FROM_UNIXTIME function and then select only the ones which are older enough thanks to the DATEDIFF function. Today's date is provided by CURDATE function.
First of all, the query which retrieves the totals for the orders older then the interval and reformats the date fields:
$q1 = "SELECT " . $ordersTABLE . ".order_total AS total, FROM_UNIXTIME(" . $ordersTABLE . ".time, '%Y-%m-%d') AS short_date FROM " . $ordersTABLE . " WHERE DATEDIFF(CURDATE(), short_date) > " . $intervalInDAYS;
Then, the one that sums up the totals of the day:
$q2 = "SELECT short_date AS day, SUM(total) AS total FROM (" . $q1 . ") GROUP BY short_date";
And then you perform your query stored in $q2 and all other operations you need to display the result.
Result from the query should be in form:
| day | total |
===========================
| 25/09/15 | 34.00 |
| 16/09/15 | 100.00 |
| 31/07/14 | 3.20 |
| ... | ... |
Related
I am working with laravel and i have a table with all the users attendances.
each row has a flag stating if the user logs in or out. I want to calculate the total working hours of user.
this is my table and sample data
id | user_id | date | timestamp | status |
1 | 1 | 2018-05-10 | 17:15:31 | out |
1 | 1 | 2018-05-10 | 13:15:31 | in |
1 | 1 | 2018-05-10 | 12:15:31 | out |
1 | 1 | 2018-05-10 | 08:01:31 | in |
I want to calculate the total working hours
$logs = DB::table('attendances as at')
->join('infos as in','in.user_id','=','at.user_id')
->select('in.name','in.avatar','at.*')
->orderBy('id','desc')
->where('at.user_id',$id)
->get();
$total_hours = [];
for($i=0; $i < count($logs)-1; $i++ ){
if($logs[$i]->status == 'out'){
$dattime1 = new DateTime($logs[$i]->dt.' '. $logs[$i]->time);
$dattime2 = new DateTime($logs[$i+1]->dt.' '. $logs[$i+1]->time);
$total_hours[] = $dattime1 ->diff($dattime2);
}
}
$working_hours = array_sum($total_hours);
Is this the best way to achieve accurate results? Please help.
Thanks
Can you try like this?
$time1 = "17:15:00";
$time2 = "00:30:00";
$strtotime1 = strtotime($time1);
$strtotime2 = strtotime($time2);
$o = ($strtotime1) + ($strtotime2);
echo $time = date("h:i:s A T",$o);
Output will be like this:
05:45:00 PM UTC
I'm developing a pharmacy store project, but I have a problem of determining the total number of drugs that expired. From DB I have:
+----+----------+--------+------------+
| id | drug_nam | amount | exp |
+----+----------+--------+------------+
| 1 | M and T | 200 | 04/15/2016 |
| 2 | VIT C | 20 | 05/25/2016 |
| 3 | Pana | 10 | 01/03/2016 |
| 4 | Lonat | 1200 | 08/25/2017 |
| 5 | ProC | 100 | 05/25/2017 |
+----+----------+--------+------------+
what I need here is a line of PHP script that will count the numbers of expired drugs from DB. using <?php $d = date('m/d/Y'); ?> to determine it from DB.
I used the code below but it count only 2
<?php
$d = date('m/d/Y');
$result = mysqli_query($conn, "SELECT count(exp) FROM products where exp < $d ");
while($row = mysqli_fetch_array($result))
{
echo $row['count(exp)'];
}
You should convert your string date representation to date value if you want to filter by date but not by string.
This query should work:
SELECT count(exp) FROM products where STR_TO_DATE(exp, '%d/%m/%Y') < $d
The main drawback is mysql can't use index in this case. One of the solution is to convert your column from varchar(50) to DATETIME. In this case you can use your original query.
From your table it seems you used some sort of text or varchar for you exp column. Cause mysql date format should be like yyyy-mm-dd. Please change your exp column to date and change the below line
$d = date('m/d/Y');
to
$d = date('Y-m-d');
That should be good.
I have the following table structure and using Yii2 ActiveRecord methods I'd like to extract the number of bookings (OrderLine) a supplier has for each day for the next week (0 entries also required). So some way of getting a row per day per supplier, with num_bookings or potentially 0 depending on the supplier.
/--------------------\ /------------\
| OrderLine |------------------|Availability|
|--------------------| 0..n 1 |------------|
|ID {PK} | |ID {PK} |
|availabilityID {FK} | |start |
|line_status | \------------/
|supplierID {FK} |
\--------------------/
| 1
|
|
| 1
/----------\
| Supplier |
|----------|
|ID {PK} |
\----------/
Querying the database directly, using DAO, with the following SQL gives me (almost) the desired result,
select count(ol.ID) as num_bookings,
day(from_unixtime(a.start)) as order_day,
ol.supplierID
from order_line ol left join
availability a on ol.availabilityID = a.ID
where ol.line_status = "booked"
and a.start >= 1451952000 //magic number for midnight today
and a.start <= 1452556800 //magic number for seven days from now
group by order_day, ol.supplierID;
something along the lines of
------------------------------------
| num_bookings|order_day|supplierID|
------------------------------------
| 1 | 5 | 3 |
| 2 | 5 | 7 |
| 1 | 6 | 7 |
| 1 | 7 | 7 |
------------------------------------
So there should be entries of 0 for the days the given Supplier has no bookings, like so
------------------------------------
| num_bookings|order_day|supplierID|
------------------------------------
| 1 | 5 | 3 |
| 0 | 6 | 3 |
| 0 | 7 | 3 |
| 2 | 5 | 7 |
| 1 | 6 | 7 |
| 1 | 7 | 7 |
------------------------------------
[days 8+ omitted for brevity...]
I've got some php/Yii code which will [eventually] give me something similar but involves multiple queries and database connections as follows,
$suppliers = Supplier::find()->all(); // get all suppliers
$start = strtotime('tomorrow');
$end = strtotime('+7 days', $start); // init times
// create empty assoc array with key for each of next 7 days
$booking_counts[date('D j', $start)] = 0;
for ($i=1; $i<7; ++$i) {
$next = strtotime('+'.$i." days", $start);
$booking_counts[date('D j', $next)] = 0;
}
foreach ($suppliers as $supplier) {
$bookings = OrderLine::find()
->joinWith('availability')
->where(['order_line.supplierID' => $supplier->ID])
->andWhere(['>=', 'availability.start', $start])
->andWhere(['<=', 'availability.start', $end])
->andWhere(['order_line.line_status' => 'booked'])
->orderBy(['availability.start' => SORT_ASC])
->all();
$booking_count = $booking_counts;
foreach ($bookings as $booking) {
$booking_count[date('D j', $booking->availability->start)] += 1;
}
}
This gives me an array for each supplier with the count stored under the appropriate day's index but that feels quite inefficient.
Can I refactor this code to return the desired data with fewer database calls and less 'scaffold' code?
This could be is the trasposition of your firt select
$results = OrderLine::find()
->select('count(order_line.ID) as num_bookings, day(from_unixtime(availability.start)) as order_day', order_line.supplierID )
->from('order_line')
->leftjoin('availability', 'order_line.availabilityID = availability.ID')
->where( 'order_line.line_status = "booked"
and a.start >= 1451952000
and a.start <= 1452556800')
->groupBy(order_day, order_line.supplierID)
->orderBy(['availability.start' => SORT_ASC])
->all();
in this way you should obtain a row for supplierID (and order_day) avoinding the foreach on supplier
For getting the data in $results->num_bookings and order_day you need add
public $num_bookings;
public $order_day;
in your OrderLine model
I hope this is what you are looking for.
I want to select the next upcoming birthdays in MYSQL.
My date is stored as: 02/19/1981 and not in a date field. I think it has to sort by day and month and not year but i can not find out how.
How can i do this? This is the query till now:
$sql = "SELECT * FROM wp_postmeta WHERE meta_key='_web_date' ORDER BY ....";
If it's possible for you change the date column to type date.
Otherwise try this:
SELECT month(str_to_date(birthdayColumn, "%m/%d/%Y")) as month, day(str_to_date(birthdayColumn, "%m/%d/%Y")) as day FROM yourTable order by month, day;
Result:
+-------+------+
| month | day |
+-------+------+
| 1 | 12 |
| 2 | 19 |
| 9 | 10 |
| 12 | 15 |
+-------+------+
You can use the php date() function. For example ate('Y-m-d',strtotime("+7 day")); then create a sql query which selects dates which are in the upcoming 7 days
This is a test environment.
CREATE TEMPORARY TABLE `birthdays` (
`id` int(4),
`name` VARCHAR(50),
`dob` CHAR(10)
) ENGINE=MEMORY;
INSERT INTO birthdays VALUES (1,'Alice', '02/19/1951'), (2,'Bob', '09/10/2015'), (3,'Carol', '12/15/2000'), (4,'Doug', '01/12/2011');
I created this function to get the next birthday. The logic may throw some interesting results over 29th Feb / 1st March.
DELIMITER $$
CREATE FUNCTION `next_birth_day`(d_dob DATE) RETURNS DATE
DETERMINISTIC
BEGIN
/* NOTE: this logic ignores the handling of leap years */
/* MySQL will happily construct invalid leap years and they are ordered
between 29/2 & 1/3 in this code. */
DECLARE d_today DATE;
DECLARE d_this_year_bday DATE;
DECLARE d_next_year_bday DATE;
SET d_today = DATE(NOW());
SET d_this_year_bday = CONCAT(YEAR(d_today), '-', MONTH(d_dob), '-', DAY(d_dob));
SET d_next_year_bday = CONCAT(YEAR(d_today)+1, '-', MONTH(d_dob), '-', DAY(d_dob));
RETURN IF( d_this_year_bday < d_today, d_next_year_bday, d_this_year_bday);
END
$$
DELIMITER ;
Then you can do a query and order by next_birth_day:
SELECT *, str_to_date(dob, "%m/%d/%Y") AS dob_dt,
next_birth_day(str_to_date(dob, "%m/%d/%Y")) AS next_bday
FROM birthdays
ORDER BY next_birth_day(str_to_date(dob, "%m/%d/%Y")) ASC
giving results like this:
+------+-------+------------+------------+------------+
| id | name | dob | dob_dt | next_bday |
+------+-------+------------+------------+------------+
| 3 | Carol | 12/15/2000 | 2000-12-15 | 2015-12-15 |
| 4 | Doug | 01/12/2011 | 2011-01-12 | 2016-01-12 |
| 1 | Alice | 02/19/1951 | 1951-02-19 | 2016-02-19 |
| 2 | Bob | 09/10/2015 | 2015-09-10 | 2016-09-10 |
+------+-------+------------+------------+------------+
I have a few different tables that I'm trying to join aggregate results out of and group them based on different time intervals.
calendar table
+------------+
| adate |
+------------+
| 2015-09-24 |
| 2015-09-25 |
| 2015-09-26 |
| 2015-09-27 |
| 2015-09-28 |
....etc.......
plays table
+----+------------+----------+---------------------+---------------------+
| id | session_id | video_id | created_at | updated_at |
+----+------------+----------+---------------------+---------------------+
| 1 | 1 | 1 | 2015-09-25 15:49:50 | 2015-09-25 15:49:50 |
+----+------------+----------+---------------------+---------------------+
And the PHP/SQL:
switch ( TRUE ) {
case ($interval->days <= 7):
default:
$group_by = 'DATE(dates.adate)';
break;
case in_array( $interval->days, range(8, 31) ):
$group_by = 'CONCAT(YEAR(dates.adate), "/", WEEK(dates.adate))';
break;
case ($interval->days > 31):
$group_by = 'YEAR(dates.adate), MONTH(dates.adate)';
break;
}
$sql = '
SELECT calendar.adate,
IFNULL(g.event_count, 0) event_count
FROM calendar
LEFT JOIN (
SELECT DATE(events.created_at) created_at,
COUNT(*) event_count
FROM plays events
WHERE events.video_id = %d
AND events.created_at >= %s
AND events.created_at < date_add(%s, INTERVAL 1 DAY)
GROUP BY DATE(events.created_at)
) g
ON g.created_at = calendar.adate
WHERE calendar.adate BETWEEN %s AND %s
GROUP BY ' . $group_by . '
ORDER BY adate;
';
I'd like to change the grouping based on the interval length, ie. if it's a week's worth of results then group by each day, a months worth of results should group by "week of 9/28/15", a years worth of results should group by mm/yyyy
Can you show how to group the result set accordingly? I feel like I am close but missing something here.