SQL Group By Date But Maintain Records - php

I assume the php/cakephp/sql parliance.
I have a History table which contains the column created of type DATETIME.
I would show all the records of the History but arranged by day.
------------------------------------------
id created what
------------------------------------------
1 2014-01-01 08:00:00 Someone ran
2 2014-01-01 09:00:00 Someone cried
2 2014-01-02 10:00:00 Someone spoke
2 2014-01-02 18:00:00 Someone worked
And the result I am looking for
array(
'2014-01-01' => array(
0=>array('time'=>'8:00','what'=>'Someone ran'
1=>array('time'=>'9:00','what'=>'Someone cried'
),
'2014-01-02' => array(
0=>array('time'=>'10:00','what'=>'Someone spoke',
1=>array('time'=>'18:00','what'=>'Someone worked',
)
)
I am totaly stucked. Which SQL statement should I investigate? GROUP is not good...

If the other reviewers agree, I would give my answer in cakephp adapted from this SO answer
$histories = $this->History->find('all',
array(
'group'=>array('History.created')
));
foreach ($histories as $item) {
$key = date('Y-m-d',strtotime($item['History']['created']));
if (!isset($groups[$key])) {
$groups[$key] = array(
'items' => array($item),
'count' => 1,
);
} else {
$groups[$key]['items'][] = $item;
$groups[$key]['count'] += 1;
}
}

Related

Split date range into weeks and sum columns based on the week

So I'm calling in data from a DB where each record is associated with a (column) 'start_time', 'no_shows and 'cancelled'. Successful attendance is based on whether 'no_shows' && 'cancelled' == 0.
I want to go through the results of this SELECT and count the number of no shows, cancellations and attended on a WEEKLY basis (based on the start_time). How can I achieve this?
The user will provide a date range and the DB will select the records based on that date range. Now this date range will then have to be split by weeks and then get the count. I'm totally stuck on the counting by weeks part. This is what I have so far:
// Multidimensional Array with [boolean][week #]
$no_shows_weekly = [
'no_show' => [],
'week' => []
];
$cancelled_weekly = [
'cancelled' => [],
'week' => []
];
$attended_weekly = [
'attended' => [],
'week' => []
];
foreach($result as $r) {
$start_time = new DateTime($r['start_time']);
if($r['is_no_show'] == 0 && $r['is_cancelled'] == 0) {
array_push($attended_weekly['attended'], 1);
array_push($attended_weekly['week'], date_format($start_time, "W"));
}
else {
array_push($attended_weekly['attended'], 0);
array_push($attended_weekly['week'], date_format($start_time, "W"));
}
array_push($no_shows_weekly['no_show'], $r['is_no_show']);
array_push($no_shows_weekly['week'], date_format($start_time, "W"));
array_push($cancelled_weekly['cancelled'], $r['is_cancelled']);
array_push($cancelled_weekly['week'], date_format($start_time, "W"));
}
echo json_encode(array(
'success'=> 1,
'msg'=>
array(
'No Shows' => $no_shows_weekly,
'Cancellations' => $cancelled_weekly,
'Attendend' => $attended_weekly
)
));
I wasn't able to do any counting in this, rather I just pulled the data and separated into arrays with the corresponding week.
I want to pull data into something like this:
Week 50: {No_Shows: 10, Cancelled: 5, Attended: 25}
Week 52: {No_Shows: 10, Cancelled: 5, Attended: 25}
General DB Structure:
+------------+-----------+-----------+
| start_time | no_shows | cancelled |
+------------+-----------+-----------+
| 2019-12-20 | 1 | 0 |
| 2019-12-21 | 0 | 0 |
| 2019-12-22 | 0 | 1 |
I tried to do this in MySQL as well:
SELECT
WEEKOFYEAR('start_time') AS weekno,
SUM('is_no_show' = 1) AS no_shows,
SUM('is_cancelled' = 1) AS cancelled
FROM
myTable
WHERE
(
`start_time` > '2019-12-01' AND `start_time` < '2019-12-07'
) AND category LIKE 'Continued Probation'
GROUP BY
weekno
However, this statement is returning Null for me. Any suggestions is appreciated! Thank you

Raking Result of search query

Rank Result data set
i am building meta search tool which get results from different sources in response to user query. i have saved the results in array of object having information like title, description , release date etc.before showing it on interface i want to rank them, so that most relevant result should be on top just like search engines. but i am new to ranking and don't know about it. so kindly guide me in this matter which ranking algorithm i should follow or any useful link for help.
I think you need to add "weighting" column (can be null) on your array of Object and just before to show the elements you need to loop on weighting if they got one.
Then your results will be shown in first and if no weight just the normal display.
Here an example :
<?php
//Function to compare weightings
function cmp($a, $b) {
if ($a == $b) {
return 0;
}
return ($a < $b) ? -1 : 1;
}
$searches = array(
'songs'=> array(
0 => array(
'title' => 'coldplay',
'weight'=> 3
),
1 => array(
'title' => 'eminem',
'weight'=> 2
),
2 => array(
'title' => 'rihanna',
'weight'=> 2
),
3 => array(
'title' => 'shakira',
'weight'=> 1
),
4 => array(
'title' => 'nirvana',
'weight'=> null
),
5 => array(
'title' => 'acdc'
)
),
);
//this foreach is used to apply the weighting on itterations
foreach($searches['songs'] as $key => $search){
//if no weight of weight is null
if(array_key_exists('weight', $search) && $search['weight']){
$array_by_weight[$key]['weight'] = $search['weight'];
$array_by_weight[$key]['title'] = $search['title'];
}else{
$array_by_weight[$key]['weight'] = 5; //Value max of weighting
$array_by_weight[$key]['title'] = $search['title'];
}
}
//We use our function to compare and sort our array
uasort($array_by_weight, 'cmp');
//display
foreach($array_by_weight as $songs){
echo $songs['title'].' | ';
echo $songs['weight'].PHP_EOL;
}
Output :
shakira | 1
eminem | 2
rihanna | 2
coldplay | 3
acdc | 5
nirvana | 5
I hope it can help you.

Possible to break statement in foreach loop?

I have two tables (user,user_group). Where user table contains
------------+-------------+------------------+
| user_id | user_name | user_group(id) |
------------+-------------+------------------+
and user_group table contains
+-----------------+-------------------+
| user_group_id | user_group_name |
+-----------------+-------------------+
Now I try to join them without using JOIN query. To solve this problem I have used double foreach() loop, but no result returns. I can't use break statement. How can I get the desired result using a loop? The desired result would be:
+-----------+-------------+-------------------+
| user_id | user_name | user_group_name |
+-----------+-------------+-------------------+
What I have so far:
public function user_get_item()
{
$this->db->select('*');
$this->db->from('users');
$results=$this->db->get()->result_array();
$this->db->select('*');
$this->db->from('user_group');
$group_data=$this->db->get()->result_array();
foreach($results as $v_results)
{
foreach($group_data as $v_group_data)
{
if($v_results['user_group']==$v_group_data['user_group_id'])
{
$v_results['user_group']=$v_group_data['user_group_name'];
}
}
}
return $results;
}
I think what you are trying to do is changing the group id identifier by the group name in the current row if they match. So, replace:
$results['user_group']=$v_group_data['user_group_name'];
by:
$v_results['user_group']=$v_group_data['user_group_name'];
Anyway, what's wrong with joins? Making SQL queries will be always faster than a polynomic loop.
Presumably you would have this:
Array(
0 => Array(
[user_id] => 1,
[user_name] => John Doe,
[user_group] => 200
),
1 => Array(
[user_id] => 2,
[user_name] => Jane Doe,
[user_group] => 100
)
)
And:
Array(
0 => Array(
[user_group_id] => 100,
[user_group_name] => Admin
),
1 => Array(
[user_group_id] => 200,
[user_group_name] => Web User
)
)
So make a new array for the group so it looks like this (ID as key, Name as value):
Array(
[100] => Admin,
[200] => Web User
)
To do that:
public function getUserGroups()
{
$rows = array();
$this->db->select('*');
$this->db->from('user_group');
$group_data=$this->db->get()->result_array();
foreach($group_data as $results)
{
$rows[$results['user_group_id']] = $results['user_group_name'];
}
return $rows;
}
Then you just assign in the other method:
public function userGetItem()
{
# Get all the user groups
$groupData = $this->getUserGroups();
# Get your original user list
$this->db->select('*');
$this->db->from('users');
$results=$this->db->get()->result_array();
$row = array();
foreach($results as $users) {
$row[] = array(
'user_id' => $users['user_id'],
'user_name' => $users['user_name'],
# Here you assign the group name from the user groups array
'user_group_name' => $groupData[$users['user_group']]
);
}
return $row;
}
Anyway, I think JOIN would be faster/better, but this should get you the results you are looking for, though I have not tested it, it should be close.

Pivoting mysql result with dates and sums

I have some data in mysql that I need to create some reports from.
My data are coming from the following query :
SELECT StoreNbr,StoreName,Date, SUM(`Sales`) FROM sales_tbl GROUP BY StoreNbr,StoreName,Date;
This results in the following data (just a small subset for my example):
+-----------+---------------------------+----------------------------+
| StoreNbr | StoreName | Date | SUM(Sales) |
+-----------+---------------------------+----------------------------+
| 1112 | Store1 | 2016-01-16 | 115.09 |
| 1112 | Store1 | 2016-01-17 | 81.00 |
| 1113 | Store2 | 2016-01-16 | 112.44 |
| 1113 | Store2 | 2016-01-17 | 56.61 |
I would like to transform my data to be this way :
+-----------+---------------------------+----------------------------+
| StoreNbr | StoreName | 2016-01-16 | 2016-01-17 |
+-----------+---------------------------+----------------------------+
| 1112 | Store1 | 115.09 | 81.00 |
| 1113 | Store2 | 112.44 | 56.61 |
Obviously there might be thousands of rows (stores) and unknown number of dates to be returned in the query as my query might be run like this (this will need to return 120+ number of columns for dates):
SELECT StoreNbr,StoreName,Date, SUM(`Sales`) FROM sales_tbl WHERE (Date BETWEEN '2016-01-10' AND '2016-05-10') GROUP BY StoreNbr,StoreName,Date;
There are a few ways to do this, none very simple. I did some research and there are some that mention that mysql does not support pivoting. I am running mariadb though, and saw that mariadb supports pivoting through the connect engine. I was unable to make it work though (adjust their official examples on my data).
Another way is lots of IFs and Cases, but most of the answers I am finding are very difficult to adapt or are tailored only to the data the guy that asks provides.
Another approach would be to process the data as they come out on my array as I have a json response in the end that feeds a datatable. - This is another think I have not managed to figure out yet.
I am looking for a way to get the desired output independent on the amount of dates (and I guess dates could be replaced by weeks or whatever else). Can anyone help?
Select all distinct dates
SELECT DISTINCT Date
FROM sales_tbl
WHERE (Date BETWEEN '2016-01-10' AND '2016-05-10')
ORDER BY Date;
and initialize an array which is indexed by that dates storing zeros:
$dateIndexedArray = array();
while($row = $stmt1->fetch(PDO::FETCH_ASSOC) {
$dateIndexedArray[$row['Date']] = 0;
}
The arry will look like
[
'2016-01-16' => 0,
'2016-01-17' => 0
]
Then execute your query
SELECT StoreNbr, StoreName,Date, SUM(`Sales`) AS Sales
FROM sales_tbl
WHERE (Date BETWEEN '2016-01-10' AND '2016-05-10')
GROUP BY StoreNbr,StoreName,Date;
And store the "Sales" in a date indexed array per store
$report = array();
while($row = $stmt2->fetch(PDO::FETCH_ASSOC)){
$storeIndex = $row['StoreNbr'] . ':' . $row['StoreName'];
if (!isset($report[$storeIndex])) {
$report[$storeIndex] = array(
'StoreNbr' => $row['StoreNbr'],
'StoreName' => $row['StoreName'],
'Sales' => $dateIndexedArray
);
}
$report[$storeIndex]['Sales'][$row['Date']] = $row['Sales'];
}
The $report array will look like:
[
'1112:Store1' => [
'StoreNbr' => 1112,
'StoreName' => 'Store1',
'Sales' => [
'2016-01-16' => 115.09,
'2016-01-17' => 81.00
]
],
'1113:Store2' => [
'StoreNbr' => 1113,
'StoreName' => 'Store2',
'Sales' => [
'2016-01-16' => 112.44,
'2016-01-17' => 56.61
]
]
]
Update:
If you need all data to be in one row for each store you can change the code to:
$report = array();
while($row = $stmt2->fetch(PDO::FETCH_ASSOC)){
$storeIndex = $row['StoreNbr'] . ':' . $row['StoreName'];
if (!isset($report[$storeIndex])) {
$report[$storeIndex] = $dateIndexedArray;
$report[$storeIndex]['StoreNbr'] = $row['StoreNbr'];
$report[$storeIndex]['StoreName'] = $row['StoreName'];
}
$report[$storeIndex][$row['Date']] = $row['Sales'];
}
The resulting array will look like:
[
'1112:Store1' => [
'StoreNbr' => 1112,
'StoreName' => 'Store1'
'2016-01-16' => 115.09,
'2016-01-17' => 81.
],
'1113:Store2' => [
'StoreNbr' => 1113,
'StoreName' => 'Store2',
'2016-01-16' => 112.44,
'2016-01-17' => 56.61
]
]
Update 2: To get the total sales per store you can use WITH ROLLUP
SELECT StoreNbr, StoreName,Date, SUM(`Sales`) AS Sales
FROM sales_tbl
WHERE (Date BETWEEN '2016-01-10' AND '2016-05-10')
GROUP BY StoreNbr,StoreName,Date WITH ROLLUP;
$report = array();
while($row = $stmt2->fetch(PDO::FETCH_ASSOC)){
if ($row['StoreName'] === null) {
// Skip this row.
// It contains total sales grouped by StoreNbr
// (or not grouped if StoreNbr === null).
continue;
}
$storeIndex = $row['StoreNbr'] . ':' . $row['StoreName'];
if (!isset($report[$storeIndex])) {
$report[$storeIndex] = $dateIndexedArray;
$report[$storeIndex]['StoreNbr'] = $row['StoreNbr'];
$report[$storeIndex]['StoreName'] = $row['StoreName'];
}
if ($row['Date'] === null) {
// This row contains total sales grouped by StoreNbr & StoreName
$report[$storeIndex]['TotalSales'] = $row['Sales']
} else {
$report[$storeIndex][$row['Date']] = $row['Sales'];
}
}
Please note that i've never used WITH ROLLUP. So you might need to adjust the code.

Create array from mysql query

I have something like this in my MySQL DB.
Date product
2015-01-01 1
2015-01-01 2
2015-02-03 3
2015-02-04 1
2015-02-04 1
2015-02-04 3
What i need in PHP is to know which product was how often sold on which day.
So: 01.01.2015 Product 1 one time, product 2 one time, on 04.02.2015 product 1 two times, product 3 one time ...
Like this:
Date product 1 product 2 product 3
2015-01-01 1 1 0 //how many times
2015-02-03 0 0 1
2015-02-04 2 0 1
So i did a normal query: SELECT date from table order by date.
I get the object as stdClass in a array back, but now i am stuck with how to get the information out i need.
I tried something like
$array=array();
foreach ($result as $key => $value) {
$time = ($result[$key]->date);
$temp[] = array(
'date' => $time,
'product 1' => '2', //equals times
'product 2' => '3',
'product 3' => '4',
'product 4' => '4'
);
$arrayBig[] = $temp;
}
And also used array_count_values to filter the days to know which days appears, but i can not find out how to connect the product to the days.
EDIT: DWright's solution:
SELECT product, date, COUNT(*)
FROM tablename
GROUP BY product, date.
Date product count(*)
2015-01-01 1 1
2015-01-01 2 2
2015-02-03 3 1
Worked fine, now i have in each row which product was sold in which date how often.
The problem i encounter now is that if i want use this data to populate google stacked charts as seen below each row in the results represents on column in the google charts graph. So for the example above i will have two entries on 01.01.2015 in my charts (one bar for product 1 and one for product 2) but i want the amount of products on each day to be stacked. So there should be only one entry for 01.01.2015 where the amount of product 1 sold on that day and the amount of product 2 sold that day is stacked onto each other,
$result = $sql statement
foreach ($result as $key => $value ) {
$typeOfProduct = $value->product;
$amount = $value->anzahl;
$time = $value->Date;
switch ($typeOfProduct) {
case 1:
$produt1 = $amount;
break;
case 2: //twitter
$product2 = $amount;
break;
case 3:
$product3 = $amount;
break;
default:
break;
}
$rows = array();
$table = array();
$table['cols'] = array(
array('label' => 'Datum', 'type' => 'date'),
array('label' => 'product 1', 'type' => 'number'),
array('label' => 'product 2', 'type' => 'number'),
array('label' => 'product 3', 'type' => 'number')
);
$day = date('d', strtotime( $time ) );
$month = date('m', strtotime( $time ) );
$monthNew = $month - 1;
$year = date('Y', strtotime( $time ) );
$temp[] = array('v' => "Date( $year, $monthNew, $day )");
$temp[] = array('v' => $product1 );
$temp[] = array('v' => $product2);
$temp[] = array('v' => $product3 );
$rows[] = array('c' => $temp);
$table['rows'] = $rows;
}
This would result in something like this:https://jsfiddle.net/api/post/library/pure/
But i would need the values to be stacked onto each other , like this:
https://jsfiddle.net/api/post/library/pure/
This will be a lot easier for you to do in SQL. Something like:
SELECT product, date, COUNT(*)
FROM tablename
GROUP BY product, date.
Then each row is for one product by date, with the count sold that date.

Categories