CakePHP3 query object returns wrong datatype - cakephp

Query object return avg rating is 3 instead of 3.5, when I have 4 and 3 values for ratings in the table
$query = $Comments->find()
->select(['rating'=>'AVG(rating)'])
->first();

Related

CakePHP how to get the running total in an entity using a virtual field?

I have field called deposit, I am trying to create a virtual field called balance. Below my desire output, It's like chaining sum.
deposit balance
100 100
300 400
10 410
I have tried below code in entity
public $balance = 0;
protected function _getBalance()
{
$this->balance = $this->balance + $this->deposit;
return $this->balance;
}
I have got all 0 in balance.
I am getting result like below
deposit balance
100 0
300 0
10 0
How can I get desire result ?
An entity has no idea about other entities, but that would be required in order for it to be able to sum up the balance.
The two solutions that come to my mind here are a) iterating over all the results and modifying the data, or b) in case your DBMS supports them, using window functions to create the running total on SQL level.
If you iterate over all results you can access the previous result's balance and calculate the sum and populate the balance field accordingly, for example in a result formatter:
$query->formatResults(function (\Cake\Collection\CollectionInterface $results) {
$previous = null;
return $results->map(function ($row) use (&$previous) {
if ($previous === null) {
$row['balance'] = $row['deposit'];
} else {
$row['balance'] = $previous['balance'] + $row['deposit'];
}
$previous = $row;
return $row;
});
});
On SQL level window functions would allow you sum up previous rows:
$query->select(function (\Cake\ORM\Query $query) {
return [
'deposit',
'balance' => $query
->func()
->sum('deposit')
->over()
->order('id')
->rows(null)
];
});
This would create a SELECT clause like this:
SELECT
deposit,
(
SUM(deposit) OVER (
ORDER BY id ROWS BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW
)
) AS balance
where the sum is calculated over all previous rows up to and including the current row.
It should be noted that window functions on the builder are only supported as of CakePHP 4.1, in previous version you'd have to create custom expressions or pass raw SQL:
$query->select([
'deposit',
'balance' => 'SUM(deposit) OVER (
ORDER BY id ROWS BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW
)'
]);
See also
Cookbook > Database Access & ORM > Query Builder > Adding Calculated Fields
Cookbook > Database Access & ORM > Query Builder > Window Functions

How to convert SQL Query into LinQ

I'm new to SQL, please Help me how to convert this query into LinQ
This is My Table Dept:
Id Name Sal Department
1 John 40000 Dotnet
2 mick 45000 DotNet
3 Pillay 777 Sql
Here I want to display Salary Based On Department Name, like:
DepartmentName ToalSal
Dotnet 85000
Sql 777
select DeprtmentName,sum(sal) from Dept_Emp Group by DeprtmentName
I wrote some Part of query
public IEnumerable<Dept_Emp> GetJam()
{
var x = from n in db.Dept_Emp
group n by n.Sal into g
select new
{
DeprtmentName = g.Key
};
// what I mention Here;
}
You are missing calculating sum of sal fields of grouped entities. Also you are grouping by wrong field. You should use department name for grouping
from de in db.Dept_Emp
group de by de.DeprtmentName into g
select new {
DepartmentName = g.Key,
TotalSalary = g.Sum(x => x.Sal) // aggregation here
}
What you have as output is anonymous objects. You cannot return them directly from method. You have several options here
Create custom class like DepartmentTotals with name and total salary fields, and return instances of this class instead of anonymous objects. Then return type will be IEnumerable<DepartmentTotals>.
Create Tuple<string, int> (or whatever type of salary). And return such tuples.
Use C# 7 tuples.

CakePHP count query gives different result when run in phpmyadmin

I am running the following query on my database:-
SELECT COUNT(*) AS COUNT, `Doctor`.`device_type` FROM `doctors` AS `Doctor` WHERE 1 = 1 GROUP BY `Doctor`.`device_type`
and it gives the result:-
count device_type
47 Android
23 iPhone
Whereas when running this query as a CakePHP query it gives the result as '2':-
$this->Doctor->find('count',array('group'=>'Doctor.device_type'));
Can anyone please suggest why this is happening?
The CakePHP result is correct as it is returning a count of the number of results returned by your query. In your case you have 2 rows: 'Android' and 'iPhone'.
find('count') always returns an integer, not an array. What Cake is doing is basically this:-
$data = $this->Doctor->find('all', array('group' => 'Doctor.device_type'));
$count = count($data); // This is what find('count') will return.
You need to do something like the following instead:-
$data = $this->Doctor->find('all', array(
'fields' => array(
'Doctor.device_type',
'COUNT(Doctor.*) AS count'
)
'group' => 'Doctor.device_type'
));

Zend query sort by multiplication of columns

I am trying to query some rows from a MySQL database with Zend Framework 1. I need all columns, but want to sort them by a multiplication of columns:
$select = $this
->select()
->where('start_date < ' . $currentTime)
->where('end_date >' . $currentTime)
->order('columnA * columnB DESC');
This obviously isn't working.
With the Zend documentation, I'm getting to this:
$select = $this->select()
->from(array('p' => 'products'),
array('product_id',
'order_column' =>
new Zend_Db_Expr('columnA * columnB'))
)
->order('order_column DESC);
However, this only returns the product_id and new order_column, but I need all columns.
How to get there? How to return all columns of the selected rows, ordered by columnA * columnB?
Too quick. I found the solution by trying:
$select = $this
->select()
->where('start_date < ' . $currentTime)
->where('end_date >' . $currentTime)
->order(new Zend_Db_Expr('columnA * columnB DESC'));
This gives the desired result.

Linq Query Condition for multiple row

I tried to solve one query from last 2 days but didn't.
It looks easy to understand but can't made.
There are two column in Table for example:
ResourceId || MappingId
1 2
1 3
2 2
2 4
3 2
3 4
4 2
4 5
5 2
5 4
This is one table which have two fields ResourceId and MappingId.
Now I want resourceId which have Mappingid {2,4}
Means answer must be ResourceId {2,3,5}
How can I get this answer in Linq Query?
Use Contains of collection. This method can be translated by Entity Framework into SQL IN operator:
int[] mappingIds = { 2, 4 };
var resources = from t in table
where mappingIds.Contains(t.MappingId)
select t.ResourceId;
Lambda syntax:
var resources = table.Where(t => mappingIds.Contains(t.MappingId))
.Select(t => t.ResourceId);
Generated SQL will look like:
SELECT [Extent1].[ResourceId]
FROM [dbo].[TableName] AS [Extent1]
WHERE [Extent1].[MappingId] IN (2,4)
UPDATE: If you want to get resources which have ALL provided mapping ids, then
var resources = from t in table
group t by t.ResourceId into g
where mappingIds.All(id => g.Any(t => t.Id == id))
select g.Key;
Entity Framework is able to translate this query into SQL, but it will not be that beautiful as query above.
IQueryable<int> resourceIds = table
// groups items by ResourceId
.GroupBy(item => item.ResourceId)
// consider only group where: an item has 2 as MappingId value
.Where(group => group.Select(item => item.MappingId).Contains(2))
// AND where: an item has 4 as MappingId value
.Where(group => group.Select(item => item.MappingId).Contains(4))
// select Key (= ResourceId) of filtered groups
.Select(group => group.Key);

Resources