So I am trying to build out some nice stats displays for my app. I can do this because I keep hit stats in a table. It simply tracks the hits plus some other nice data and the time it occurs. I can query the db to show me how many hits occurred on a particular day or on each day for the past x days as in the code below. However the code below only returns dates for which there is data. I would like to show the last 30 days of hits regardless of whether a day has a hit or not. Thoughts?
SELECT DATE(time) AS theday, COUNT( * ) AS thecount
FROM stats
WHERE time <= curdate( )
AND time >= DATE_SUB( curdate(), INTERVAL 30 DAY )
GROUP BY theday ORDER BY time DESC
Produces
theday thecount
2011-11-22 5
2011-11-21 9
2011-11-18 10
2011-11-16 1
2011-11-11 2
2011-11-10 15
2011-11-09 2
2011-10-26 1
2011-10-24 6
As you can see it skips dates with no results. I understand why this is, as there are no rows with those dates in the db. I am wondering how I would produce a query that works almost like the above but has all the dates for said interval. IE: the last 30 days.
You have 3 options:
try to iterate the dates in the application logic (php)
generate a (temporary) table filled with the dates you need and left join with it
use mysql stored procedure solution like in this answer
Example for app logic implementation:
<?php
date_default_timezone_set('Europe/Paris');
$startdate = strtotime('2011-11-01 00:00:01');
$days = 60;
$found_data = array( // this is generated by 1 mysql query
array('date_field' => '2011-11-02', 'count' => 5),
array('date_field' => '2011-11-03', 'count' => 1),
array('date_field' => '2011-11-04', 'count' => 6),
array('date_field' => '2011-11-08', 'count' => 9),
array('date_field' => '2011-11-09', 'count' => 3),
array('date_field' => '2011-11-10', 'count' => 5),
array('date_field' => '2011-11-12', 'count' => 1),
array('date_field' => '2011-11-15', 'count' => 1),
array('date_field' => '2011-11-18', 'count' => 4),
array('date_field' => '2011-11-21', 'count' => 9),
array('date_field' => '2011-11-23', 'count' => 1),
array('date_field' => '2011-11-28', 'count' => 8),
array('date_field' => '2011-11-30', 'count' => 6),
);
foreach ($found_data as $counts) { // we convert the results to a usable form, you can do this in the query, too
$count_info[$counts['date_field']] = $counts['count'];
}
for ($i = 0; $i <= $days; $i++) {
$date = date('Y-m-d', $startdate+$i*60*60*24);
printf("%s\t%s\n", $date, array_key_exists($date, $count_info) ? $count_info[$date] : 0);
}
?>
Related
I want to know how or if I can use the content of a column in mysql as a key (index) to a php array within the mysql query.
Example:
Column name: period
Column contents: (one of) 'Year','Month','Fortnight','Week','Hour','Day'
My php array:
$x = array(
'Year' => 1,
'Month' => 12,
'Fortnight' => 26,
'Week' => 52,
'Hour' => 1872,
'Day' => 365
);
Another variable $y can be any number (say < 1,000,000)
I want to write a WHERE (or HAVING) condition such as...
WHERE '$x[`period`]' * '$y' > 12345
E.g. when the contents of period = 'Month', $y should be multiplied by 12 before being tested to be > 12345
What is the best (most efficient) way to do this, other than (say) a long CASE conditional?
Thank you
i have write demo query
<?php
$x = array(
'Year' => 1,
'Month' => 12,
'Fortnight' => 26,
'Week' => 52,
'Hour' => 1872,
'Day' => 365
);
$y=10;
$sql="select * from table where ".$x['Month']*$y." > 12345" ;
echo $sql;
in $x[] select your value to multiply
You can use case instead of php array:
<?php
$y = 100;
$sql = "select * from tablename
where (case `period`
when 'Year' then 1
when 'Month' then 12
when 'Fortnight' then 26
when 'Week' then 52
when 'Hour' then 1872
when 'Day' then 365
end) * {$y} > 12345";
Try this:
<?php
$y = 10;
$period = "Month";
$x = array(
'Year' => 1,
'Month' => 12,
'Fortnight' => 26,
'Week' => 52,
'Hour' => 1872,
'Day' => 365
);
$sql="select * from table where ".($x[$period] * $y)." > 12345" ;
echo $sql;
I posted a question some time ago on representing data per date horizontally on a datatable.
See here: datatables dates at the top and data cells going from left to right
With the help of that thread I was able to get the data to display how I wanted it. With the dates showing at the top, the service provided on the left and all data associated with any date between the 2 date paramters inside the main body. (If there is no data in a particular date then the < td > will display 0. See here:
http://www.phpwin.org/s/ewbAS6
After manipulating this code further I made the dates of the search dynamic by proving a form with a start date and an end date, and a dropdown with the options of:
Daily
Weekly
Monthly
Quaterly
Yearly
this allows the interval of dates at the top to become dynamic. Of course all this is doing is changing the value of the 2nd parameter inside the date while loop.
WHILE (strtotime($date) <= strtotime($end_date)) {
echo '<th>' . $date . '</th>';
$date = date('Y-m-d', strtotime($date . ' +1day'));
}
with the parameter set at Weekly, the value of +1day becomes +1week, at Monthly; the value becomes +1month and so on.
MY ISSUE:
When the interval is set to daily, the dates with their corresponding attendance counts are displayed correctly but once you try to increase the interval to +1week and above the data does not round up to the week shown. Check this:
[LINK1]Per day: http://www.phpwin.org/s/ewbAS6
[LINK2]Per month: http://www.phpwin.org/s/xRo3I6
Looking at the array (modified on the LINK2)
$result[] = array('Service Name' => 'Health', 'date' => '2017-04-04', 'Attendance' => 5);
$result[] = array('Service Name' => 'Payroll', 'date' => '2017-04-16', 'Attendance' => 5);
$result[] = array('Service Name' => 'Saturday Youth Meeting', 'date' => '2017-04-03', 'Attendance' => 1);
$result[] = array('Service Name' => 'Saturday Youth Meeting', 'date' => '2017-05-03', 'Attendance' => 3);
$result[] = array('Service Name' => 'Payroll', 'date' => '2017-05-03', 'Attendance' => 2);
$result[] = array('Service Name' => 'Payroll', 'date' => '2017-04-11', 'Attendance' => 3);
$result[] = array('Service Name' => 'Payroll', 'date' => '2018-04-03', 'Attendance' => 10);
You can see in the array that there are multiple attendance entries in April, totaling 14 Attendances in that month however during LINK2 where the interval is increased to a month instead of showing 14 for April (which would be the sum of all the dates in that particular month) it shows the value 1.
My live version takes the array from a database so I used the YEAR(), MONTH(), WEEK() and DAY() function on the date and used group by. The query executes how I want it but having issues working on the PHP end.
I have this array data structure:
$records = [];
$records[] = ['id' => 1, 'date' => '2017-03-12', 'operation' => 'sent_email'];
$records[] = ['id' => 1, 'date' => '2017-03-13', 'operation' => 'sent_email'];
$records[] = ['id' => 1, 'date' => '2017-03-13', 'operation' => 'sent_email'];
$records[] = ['id' => 1, 'date' => '2017-03-14', 'operation' => 'forgot_password'];
$records[] = ['id' => 1, 'date' => '2017-03-14', 'operation' => 'sent_email'];
$records[] = ['id' => 2, 'date' => '2017-03-14', 'operation' => 'sent_email'];
$records[] = ['id' => 2, 'date' => '2017-03-14', 'operation' => 'forgot_password'];
$records[] = ['id' => 1, 'date' => '2017-03-27', 'operation' => 'sent_email'];
$records[] = ['id' => 1, 'date' => '2017-03-29', 'operation' => 'sent_email'];
In this array I store operations that were done by website visitors. Obviously visitor can be identified by an id number.
What I want to do is to count how many times in a week (between Monday and Sunday inclusively) each visitor used 'sent_email' operation.
For example: first record with 'sent_email' operation shows that this operation happened in '2017-03-12' (Sunday), so that means this operation took place just once in that week for user with an id = 1.
Other next three 'sent_email' operations for user with an id = 1 happened three times in another week. (2017-03-13, 2017-03-13, 2017-03-14 those three dates belong to the same week).
I know that I probably need to loop through each record and somehow to check those dates if they belong to the same week, but I feel confused and stuck here, I don't understand logical steps needed to accomplish it. I would be really grateful if anyone could give me an explanation or pseudo code, whatever it is that would help me to tackle this problem, I really enjoy solving problems myself but because I'm stuck, I just need someone to get me up and running.
Thank you in advance for any help you can provide.
The DateTime object and the strtotime() functions both understand relative date strings, so you can do neat things like "last tuesday" and "3 days ago".
Some pseudo-code to get you started:
// figure out when your monday is
monday = ...
// figure out when your sunday is
sunday = ...
// loop over all records
foreach $record:
// skip records outside the date range you want
if date < monday or date > sunday then skip this record;
// skip records not of the type you want
if operation != email then skip this record;
// register a hit for this user
increment counter for user
I've a table as following-
Now I need to make report of total number of counts in every hour, week, month and year. It may have sum 0 but should be include on the result. For example I need the result as follows-
$hourlyResult = array(
'00:01' => '5',
'00:02' => '9',
'00:03' => '50',
'00:04' => '5',
..............
..............
'00:55' => '95',
'00:56' => '0',
'00:57' => '20',
'00:58' => '33',
'00:59' => '5',
);
$weeklyResult = array(
'SAT' => '500',
'SUN' => '300'
.............
.............
'FRI' => '700'
);
How can I build the query in cakephp 3? I got the following link but can't go so far.
GROUP BY WEEK with SQL
What I've done-
$this->loadModel('Searches');
$searches = $this->Searches
->find('all')
->select(['created', 'count'])
->where('DATE(Searches.created) = DATE_SUB(CURDATE(), INTERVAL 1 DAY)')
->group(WEEK(date))
->hydrate(false)
->toArray();
pr($searches);
Here is how you can do it.
Sum By Year
$query = $this->Searches->find();
$query = $this->Searches
->find()
->select([
'total_count' => $query->func()->sum('count'),
'year' => $query->func()->year(['created' => 'literal'])
])
->group(['year'])
->hydrate(false);
Or
$query = $this->Searches
->find()
->select(['total_count' => 'SUM(count)', 'year' => 'YEAR(created)'])
->group(['year'])
->hydrate(false);
Sum By Day Of Week
$query = $this->Searches->find();
$query = $this->Searches
->find()
->select([
'total_count' => $query->func()->sum('count'),
'day_of_week' => $query->func()->dayOfWeek('created')
])
->group(['day_of_week'])
->hydrate(false);
Or
$query = $this->Searches
->find()
->select(['total_count' => 'SUM(count)', 'day_of_week' => 'DAYOFWEEK(created)'])
->group(['day_of_week'])
->hydrate(false);
The same way you can get total sum by hour or month.
Here you can read about CakePHP > Using SQL Functions and date and time functions in MySQL.
I am building a small class combination to calculate the precise date of the beginning of a semester. The rules for determining the beginning of the semester goes as follow :
The monday of week number ## and after dd-mm-yyyy date
ie: for winter its week number 2 and it must be after the january 8th of that year
I am building a resource class that contain these data for all the semesters (4 in total). But now I am facing an issue based on the public holidays. Since some of those might be on a Monday, in those cases I need to get the date of the Tuesday.
The issue I am currently working on is the following :
The target semester begins on or after august 30 and must be on week 35.
I also have to take account of a public holiday which happen on the first monday of september.
The condition in PHP terms is the following
if (date('m', myDate) == 9 // if the month is september
&& date('w', myDate) == 1 // if the day of the week is monday
&& date('d', myDate) < 7 // if we are in the first 7 days of september
)
What would be the best way to "word" this as a condition and store it in an array?
EDIT
I might not have been clear enough, finding the date is not the problem here. The actual problem is storing a condition in a configuration array that looks like the following :
$_ressources = array(
1 => array(
'dateMin' => '08-01-%',
'weekNumber' => 2,
'name' => 'Winter',
'conditions' => array()
),
2 => array(
'dateMin' => '30-04-%',
'weekNumber' => 18,
'name' => 'Spring',
'conditions' => array()
),
3 => array(
'dateMin' => '02-07-%',
'weekNumber' => 27,
'name' => 'Summer',
'conditions' => array()
),
4 => array(
'dateMin' => '30-08-%',
'weekNumber' => 35,
'name' => 'Autumn',
'conditions' => array("date('m', %date%) == 9 && date('w', %date%) == 1 && date('d', %date%) < 7")
)
);
The issue I have with the way it's presented now, is that I will have to use the eval() function, which I would rather not to.
You said:
The target semester begins on or after august 30 and must be on week 35.
If that's the case you can simple check for week number.
if(date('W', myDate) == 35)
Or if your testing condition is correct then you should compare day number till 7 as it starts from 1.
if((date('m', myDate) == 9 // september
&& date('w', myDate) == 1 // monday
&& date('d', myDate) <= 7 // first 7 days of september
)
And then in the if statement, once you have found the monday which would be OK IF its not a public holiday, do this
if(...){
while(!array_search (myDate, aray_of_public_holidays))
date_add($myDate, date_interval_create_from_date_string('1 days'));
}
Here the array_of_public_holidays contains the list of public holidays.
Update with Code
Following code should work for your purposes
<?php
// array with public holidays
$public_holidays = array(/* public holidays */);
// start on 30th august
$myDate = new DateTime('August 30');
// loop till week number does not cross 35
while($myDate->format('W') <= 35){
// if its a monday
if($myDate->format('w') == 1){
// find the next date not a public holiday
while(array_search($myDate, $public_holidays))
$myDate->add(date_interval_create_from_date_string('1 days'));
// now myDate stores the valid semester start date so exit loop
break;
}
// next date
$myDate->add(date_interval_create_from_date_string('1 days'));
}
// now myDate is the semester start date
?>
Update according to updated question
Following code should work for your needs. You do not need to store the condition in your array as PHP code. The following code shows how it can be done
// semester conditions
$sem_conditions = array(
1 => array(
'dateMin' => '08-01-%',
'weekNumber' => 2,
'name' => 'Winter'
),
2 => array(
'dateMin' => '30-04-%',
'weekNumber' => 18,
'name' => 'Spring'
),
3 => array(
'dateMin' => '02-07-%',
'weekNumber' => 27,
'name' => 'Summer'
),
4 => array(
'dateMin' => '30-08-%',
'weekNumber' => 35,
'name' => 'Autumn'
)
);
// array with public holidays format (d-M)
$public_holidays = array('05-09', '10-01');
// store sem starts
$sem_starts = array();
// for each semester
foreach($sem_conditions as $sem){
// start date
$myDate = date_create_from_format('d-m', substr($sem['dateMin'], 0, -2));
// loop till week number does not cross $sem['weekNumber']
while($myDate->format('W') <= $sem['weekNumber']){
// if its a monday
if($myDate->format('w') == 1){
// find the next date not a public holiday
while(array_search($myDate->format('d-m'), $public_holidays) !== false)
$myDate->add(date_interval_create_from_date_string('1 days'));
// now myDate stores the valid semester start date so exit loop
break;
}
// next date
$myDate->add(date_interval_create_from_date_string('1 days'));
}
// add to sem starts
$sem_start[$sem['name']] = $myDate->format('d-m-Y');
}
var_dump($sem_start);
The target semester begins on or after august 30 and must be on week 35
The start of the semester is the minimal date between week 35 and August 30:
$week35 = new DateTime("January 1 + 35 weeks");
$august30 = new DateTime("August 30");
$start = min($week35, $august30);
Alternatively:
$start = min(date_create("January 1 + 52 weeks"), date_create("August 30"));