I have a reports table which looks like something as follows:
Reports is monthly reports of users putting their efforts. It's many to many relationship table.
id
user_id
project_id
total_effort
created_at
1
5
232
40
2023-01-23
I want to get all users with their contributions projects-wise so that I can create an excel something like this.
I am able to group users by group and get their collective data, but unable to group by project id as well.
So far here is my query
$data = Report::select('user_id')
->selectRaw("SUM(total_effort) as total, DATE_FORMAT(report_for_date, '%b,%Y') new_date")
->groupBy('user_id')->with('userDetail:id,first_name,last_name')->get();
And this query return
[0] => Array
(
[user_id] => 2
[total] => 500
[new_date] => Dec,2022
[user_detail] => Array
(
[id] => 2
[first_name] => Hermione
[last_name] => Granger
)
)
But what I am actually looking for is something like this:
[0] => Array
(
[user_id] => 2
[total] => 500
[new_date] => Dec,2022
[projects]=>Array(
[0]=>Array(
[project_id]=>1,
[total]=>30,
[project_detail]=>Array(
[id]=>1
[name]=>Project 1
)
)
[1]=>Array(
[project_id]=>41,
[total]=>30,
[project_detail]=>Array(
[id]=>41
[name]=>Project 41
)
)
[2]=>Array(
[project_id]=>32,
[total]=>30,
[project_detail]=>Array(
[id]=>32
[name]=>Project 32
)
)
)
[user_detail] => Array
(
[id] => 2
[first_name] => Hermione
[last_name] => Granger
)
)
So that I can loop the data and plot them in excel.
How can this be done? Even if it is just a Raw MySQL query how to do Groupby inside a groupBy?
Some reference that I checked but without any help
sql group by sub group
Mysql Nested query and GROUP BY
using group function inside group function
You can use with() method to load the relation with groupby clause
$data = Report::select('user_id', 'project_id')
->selectRaw("SUM(total_effort) as total, DATE_FORMAT(report_for_date, '%b,%Y') new_date")
->groupBy('user_id')
->groupBy('project_id')
->with('userDetail:id,first_name,last_name')
->with('projectDetail:id,name')
->get();
I would query the active projects within the reporting period and then use that to build up the main query with conditional aggregation (pivot) -
// get list of projects within reporting period
$projects = DB::table('projects p')
->join('reports r', 'p.id', '=', 'r.project_id')
->select('p.id', 'p.name')
->whereBetween('r.created_at', ['2023-01-01', '2023-01-31'])
->groupBy('p.id')
->get();
// build conditional aggregates
$columns = ['CONCAT(u.first_name, \' \', u.last_name) AS username'];
foreach ($projects as $project) {
$columns[] = "SUM(IF(r.project_id = {$project->id}, total_effort, 0)) AS project_{$project->id}";
}
// Add total column
$columns[] = 'SUM(total_effort) AS total';
$report = DB::table('reports r')
->join('users u', 'r.user_id', '=', 'u.id')
->selectRaw(implode(',', $columns))
->whereBetween('r.created_at', ['2023-01-01', '2023-01-31'])
->groupBy('r.user_id')
->get();
The idea is to build and execute a query which looks something like this -
SELECT
CONCAT(u.first_name, ' ', u.last_name) AS username,
SUM(IF(r.project_id = 232, total_effort, 0)) AS project_232,
SUM(IF(r.project_id = 233, total_effort, 0)) AS project_233,
SUM(IF(r.project_id = 234, total_effort, 0)) AS project_234,
SUM(total_effort) AS total
FROM reports r
JOIN users u ON r.user_id = u.id
WHERE r.created_at BETWEEN '2023-01-01' AND '2023-01-31'
GROUP BY r.user_id
Related
So here's the thing.
I'm querying the database using Doctrine 2.5 and I'd like to use Doctrine's createQuery() method to do it. The reason is that Doctrine 2 hydrates the results in a camelCase manner, and my column names are all with underscores. And since most of the simple queries work fine with createQuery (or even with the query builder) I'd like to move all native MySQL queries to Doctrine's DQL (to get all results in camelCase so that's a standarized results). Either that or you guys can teach me how to make Doctrine 2 return the results in the same way that the columns are in the database... xD
I have the following query:
$sql = '
SELECT sp.*, so.user_id
FROM shop_order so
INNER JOIN shop_payment sp ON(so.payment_id=sp.id)
WHERE so.id = '.$orderID;
$stmt = $em->getConnection()->prepare($sql);
$stmt->execute();
$result = $stmt->fetch();
This results in the following result:
Array
(
[id] => 14
[payment_type_id] => 2
[flag] => boleto_bb
[tid] =>
[billet] => 1
[parcel_quantity] =>
[cart_name_holder] =>
[cart_date_holder] => 1999-12-01
[cart_number_holder] => 0
[datecreated] => 2017-08-30 14:51:25
[dateupdate] => 2017-08-30 16:36:18
[status] => 2
[user_id] => 16
)
But if I do it like this:
$query = $em->createQuery('
SELECT sp, so.userId
FROM ShopOrder so
INNER JOIN ShopPayment sp WITH (so.paymentId = sp.id)
WHERE so.id = ' . $orderID);
$result = $query->getArrayResult();
I get the following result:
Array
(
[0] => Array
(
[0] => Array
(
[id] => 14
[paymentTypeId] => 2
[flag] => boleto_bb
[tid] =>
[billet] => 1
[parcelQuantity] =>
[cartNameHolder] =>
[cartDateHolder] => DateTime Object
(
[date] => 1999-12-01 00:00:00.000000
)
[cartNumberHolder] => 0
[datecreated] => DateTime Object
(
[date] => 2017-08-30 14:51:25.000000
)
[dateupdate] => DateTime Object
(
[date] => 2017-08-30 16:36:18.000000
)
[status] => 2
)
[userId] => 16
)
)
How can I get the first result from the createQuery method?
I have a BD that contains people with day and month fields, like so:
person1 (day = 5; month = 3)
person2 (day = 2; month = 12)
I have to perform a find between 2 dates, for example, I need all people between 01/03 and 01/06 (day/month) but I don't know how to perform that.
I tried using separate conditions, like this:
$conditions['People.day >='] = dayA;
$conditions['People.month >='] = monthA;
$conditions['People.day <='] = dayB;
$conditions['People.month <='] = monthB;
But, that's not correct because it finds day and month, I mean, it finds People between monthA and monthB and People between dayA and dayB, instead, what I need is people between dayA/monthA and dayB/monthB
I suppose I must do some kind of a JOIN, but I'm lost here, I looked some information but I don't know where to start.
updated info:
ok, I'm using this
Array
(
[People.month_day BETWEEN ? AND ?] => Array
(
[0] => 01/02
[1] => 28/02
)
)
but I get people like this:
1/1
2/1
10/1
11/1
12/1
13/1
20/1
21/1
1/2
what's wrong? do you need more code? I'm using this to retrieve results:
A paginate:
public $paginate = array('People'=>array(
'limit' => 16,
'order' => 'People.month, People.day ASC'
));
In your People model
public $virtualFields = array(
'month_day' => 'CONCAT(People.month, "-", People.day)'
// 'month_day' => 'CONCAT(People.day, "/", People.month)'
);
then add in your controller
$options = array(
'conditions' => array(
'Post.month_day BETWEEN ? AND ?' => array('01-03','01-06')
// 'Post.month_day BETWEEN ? AND ?' => array('03/01','06/01')
)
);
$posts = $this->Post->find('all',$options);
It would be best to alter your table. Use DATE field and your SQL query would be:
SELECT *
FROM dbname.People
WHERE People.date BETWEEN '1999-03-01' AND '1999-06-01';
I am using cakephp in one of my project. What i need is to handle complex query using single model and single array out.Since I am new to cakephp i got stucked really very bad here :
$rs = $this->User->query("
SELECT (wd.wajebaat_amt) as commited,
SUM(pd.sila_waje) as paid,
(wd.wajebaat_amt-sum(pd.sila_waje)) as balance,
FROM wajebaat_details as wd
LEFT JOIN waje_pay_details as pd ON (pd.waje_id=wd.waje_id)
WHERE wd.hof_id="123" and wd.year="2010"
GROUP BY wd.waje_id");
print_r($rs); exit();
// it displays output as
Array
(
[0] => Array
(
[wd] => Array
(
[commited] => 252000
)
[0] => Array
(
[paid] => 253829
[balance] => -1829
)
)
)
//however i need it following format
Array
(
[0] => Array
(
[wd] => Array
(
[commited] => 252000
[paid] => 200000
[balance] => 52000
)
)
)
You can use Hash (utility) method format() in cakephp 2.5 to convert the nested array into string,in previous version of cakephp the method is set(),
Hash::format(array $data, array $paths, $format)
Example :
$result = Hash::format($rs,array('{n}.wd.commited','{n}.wd.0.paid','{n}.wd.0.balance'),'%1$d,%2$d,%$d');
Output:
252000,2000000,52000
For More formating option refere cook book of cakephp
$conditions = Array
(
[table] => products_pages
[alias] => ProductsPage
[type] => inner
[foreignKey] =>
[conditions] => Array
(
[0] => ProductsPage.product_id = Product.id
)
)
I'm trying to set up NOT EXISTS conditions, like the following SQL statement:
SELECT * FROM products_pages,products
WHERE NOT EXISTS (SELECT id
from products_pages
where products_pages.product_id = products.id)
So basically select any product that doesn't exist in the products_pages table.
What is the proper way to format that SQL statement for CakePHP and replace it here:
[conditions] => Array
(
[0] => (What's the proper way to insert above SQL here?
)
Would really appreciate your help guys, I've been trying to figure this out for about 5 hours with no luck. Thanks!
You can always use query if you don't find the way to do it with CakePHP:
http://book.cakephp.org/2.0/en/models/retrieving-your-data.html#model-query
In this case security wouldn't be compromised as you are not using any input.
Anyway, something simple would be just to do it in more than one step:
//selecting the products in the productcs_pages table
$productsWithPages = /* query to get them*/
//getting an array of IDs
$productsWidthPagesIds = Hash::extract($productsWithPages, '{n}.Product.id');
//doing the NOT IN to select products without pages
$productsWithoutPages= $this->Product->find('all',
array('conditions' =>
array( 'NOT' => array('Product.id' => $productsWidthPagesIds )
)
);
In cake php is how we can get order of query result according to 'IN' clause in the query
$array = array(8,6); // order in 'In' clause
$condition = array('Video.id' => $array);
$videos = $this->Video->find('all', array('conditions' => $conditions));
//The query will be like below
SELECT * FROM `videos` AS `Video` WHERE `Video`.`id` IN (8,6);
Currently it will give result as
Array
(
[0] => Array
(
[Video] => Array
(
[id] => 6
)
)
[1] => Array
(
[Video] => Array
(
[id] => 8
)
)
)
I need it like
Array
(
[0] => Array
(
[Video] => Array
(
[id] => 8
)
)
[1] => Array
(
[Video] => Array
(
[id] => 6
)
)
)
order Desc or asc will not retreive actual result in order. How it can retreved using cake php ?
I am using cake php, whether this can be done in mysql ?
ORDER is an option in CakePHP?
$this->Video->find('all', array('conditions' => $conditions, 'order' => array('Video.id DESC')));
In response to comment:
$this->Video->find('all', array('conditions' => $conditions, 'order' => array('FIELD(Video.id, 7, 4, 9)')));
ORDER BY FIELD("videos"."id",8,6)
i'm quote sure you can use it in cake's find
You can even use the ELT function in this way:
ORDER BY ELT(videos.id, 8,1,6,2,n,3,.....)
This works fine for me
$order = array("FIND_IN_SET(Video.id, '8,6')");
$result = $this->Video->find('all', array('conditions' => $conditions,'order' => $order);
SELECT * FROM table WHERE id IN (1,2,3,4,5)
The above query can be converted to CakePHP like so:
<?php
$ids = array(1,2,3,4,5);
$this->Model->find('all', array('conditions' => array('Model.id' => $ids)));
?>
Why not write
SELECT * FROM `videos` as `Videos` WHERE `Videos`.`id` IN (8,6) ORDER BY `Videos`.`id` DESC
It is not a best practice to use * in your SELECT statement.
Let me know if that helps.
EDIT
If needed we can also sort it using another method
Order By Case `Video`.`id`
When 8 Then 1
When 1 Then 2
When 3 Then 3
END