Yii: computing aggregate by date - php

I have a simple model in Yii 1.1:
Expense
amount: int
date: string
How do I compute an aggregate by date using build-in Yii capabilities?
In other words I would like to get a result of the following query using Yii:
SELECT
date, SUM(amount) AS dateAmount
FROM
tbl_expense
GROUP BY
date
ORDER BY
date DESC

I have explored 2 ways for executing query,
First:
$list= Yii::app()->db->createCommand('SELECT date, SUM(amount) AS dateAmount
FROM tbl_expense
GROUP BY date
ORDER BY date DESC')->queryAll();
var_dump($list->getData());
Second:
(Normally used to provide dataprovider to Gridview and Listview)
$select = "SELECT date, SUM(amount) AS dateAmount
FROM tbl_expense
GROUP BY date
ORDER BY date DESC";
$dataProvider=new CSqlDataProvider($select,
array(
'pagination'=>false,
'params'=>$sqlParam
)
);
var_dump($dataProvider->getData());

You could use criteria
$criteria = new CDbCriteria();
$criteria->select = 'date, SUM(amount) dateAmount';
$criteria->group = 'date';
$model=Expense::model()->find($criteria);

You can use criteria
$criteria = new CDbCriteria();
$criteria->select = 'date, SUM(amount) dateAmount';
$criteria->group = 'date';
$model=Expense::model()->find($criteria);
AND
add property dateAmount to the model class.
class SomeClass extends CActiveRecord {
public $dateAmount;
...
}

Related

Yii CDBCriteria LEFT JOIN not working

I have a CDBCriteria here:
$criteria = new CDbCriteria;
$criteria->select = array('t.lastname', 't.name', 't.patronomic', 't.dob', 'n.nationality', 't.token', 'm.status');
$criteria->join = 'LEFT JOIN hrp_nationalities n ON (n.id = t.nationality_id) ';
$criteria->join .= 'LEFT JOIN hrp_marital_status m ON m.id = t.marital_status_id';
$criteria->condition = "t.`alive` = 1 AND t.`org_id` = ".$_SESSION['org_id'];
$criteria->order = 't.id DESC';
My question is why m.status and n.nationality are not retrieved into the resulting object?
If you are using the CDbCriteria on a model class eg $results = User::model()->findAll($criteria); then the result will contain only fields from user table, not the relations'. The reason is that when the resultant model objects are created there is no field declared for the relation columns in the model.. So you need to create fields in the model..
in your case nationality, status
Just write in your model
public $nationality;
public $status;
And if your model also has a field named status, use an alias for the relation's field status..
public $m_status;
$criteria->select = array('t.lastname', 't.name', 't.patronomic', 't.dob', 'n.nationality', 't.token', 'm.status as m_status');
I just remember I have answered an old similar question.. Read answer here: https://stackoverflow.com/a/11594530/1114536

Yii CActiveDataProvider generate SQL query

CActiveDataProvider generating auto query to count total item count:
SELECT COUNT(DISTINCT `t`.`id`) FROM `transaction` `t` LEFT OUTER JOIN `partner` `partner` ON (`t`.`partner_id`=`partner`.`id`)
This query is slow, because contain join, how I can set manualy total count, and disable this auto query ?
You may manualy set total item count for CActiveDataProvider to prevent auto calculation.
class Model extends CActiveRecord {
public function search(){
$criteria = new CDbCriteria;
// your criteria here
$data_provider = new CActiveDataProvider($this, array('criteria'=>$criteria));
// replace $this->count( $criteria ) with your own condition or another criteria
$data_provider->setTotalItemCount( $this->count( $criteria ) );
return $data_provider;
}
}
Try set use CActiveDataProvider->countCriteria.
http://www.yiiframework.com/doc/api/1.1/CActiveDataProvider#countCriteria-detail

How to count and group by in yii2

I would like to generate following query using yii2:
SELECT COUNT(*) AS cnt FROM lead WHERE approved = 1 GROUP BY promoter_location_id, lead_type_id
I have tried:
$leadsCount = Lead::find()
->where('approved = 1')
->groupBy(['promoter_location_id', 'lead_type_id'])
->count();
Which generates this query:
SELECT COUNT(*) FROM (SELECT * FROM `lead` WHERE approved = 1 GROUP BY `promoter_location_id`, `lead_type_id`) `c`
In yii 1.x I would've done the following:
$criteria = new CDbCriteria();
$criteria->select = 'COUNT(*) AS cnt';
$criteria->group = array('promoter_location_id', 'lead_type_id');
Thanks!
Solution:
$leadsCount = Lead::find()
->select(['COUNT(*) AS cnt'])
->where('approved = 1')
->groupBy(['promoter_location_id', 'lead_type_id'])
->all();
and add public $cnt to the model, in my case Lead.
As Kshitiz also stated, you could also just use yii\db\Query::createCommand().
You can get the count by using count() in the select Query
$leadCount = Lead::find()
->where(['approved'=>'1'])
->groupBy(['promoter_location_id', 'lead_type_id'])
->count();
Reference Link for various functions of select query
If you are just interested in the count, use yii\db\Query as mentioned by others. Won't require any changes to your model:
$leadsCount = (new yii\db\Query())
->from('lead')
->where('approved = 1')
->groupBy(['promoter_location_id', 'lead_type_id'])
->count();
Here's a link to the Yii2 API documentation
Without adding the $cnt property to model
$leadsCount = Lead::find()
->select(['promoter_location_id', 'lead_type_id','COUNT(*) AS cnt'])
->where('approved = 1')
->groupBy(['promoter_location_id', 'lead_type_id'])
->createCommand()->queryAll();
Just a note, in case it helps anyone, that a getter used as a property is countable (whereas if called as a function it will return 1). In this example, I have a Category class with Listings joined by listing_to_category. To get Active, Approved Listings for the Category, I return an ActiveQuery, thus:
/**
* #return \yii\db\ActiveQuery
*/
public function getListingsApprovedActive() {
return $this->hasMany(Listing::className(), ['listing_id' => 'listing_id'])
->viaTable('listing_to_category', ['category_id' => 'category_id'])
->andWhere(['active' => 1])->andWhere(['approved' => 1]);
}
Calling count on the property of the Category will return the record count:
count($oCat->listingsApprovedActive)
Calling count on the function will return 1:
count($oCat->getListingsApprovedActive())

Sorting in yii Cdbcriteria with sum(amount)

I need to show highest amount on the top in yii Cgridview.
I have two models: members and billing,
member_id is foregion key in billing model.
My Model Function:
public function getImportantMembers(){
$criteria = new CDbCriteria;
$criteria->select ='t.*,b.billing_id,b.amount,b.billing_date,b.member_id,b.billing_status , sum(b.amount) AS totalamount';
$criteria->join = 'JOIN billing AS b ON b.member_id = t.id ';
$criteria->addCondition("b.billing_date > DATE_SUB(NOW(),INTERVAL 2 MONTH) AND b.billing_status='c' AND b.amount > 150 ");
$criteria->group = 't.id';
$criteria->order = " totalamount DESC";
return new CActiveDataProvider(get_class($this),array(
'pagination'=>array(
'pageSize'=> Yii::app()->user->getState('pageSize',Yii::app()->params['defaultPageSize']),),
'criteria'=>$criteria,
));
}
but this function not showing correctly highest amount at the top or amount DESC
How I can fix this?
Define relations in your models and use them in your criteria. Then pass a CSort object to the DataProvider like below. You can define custom sort parameters as long as you define them in your model.
return new CActiveDataProvider( 'Model', array(
'criteria'=>$criteria,
'sort'=>array(
'attributes'=>array(
...........
...........
'*',
),
),
));
For details this link will help.
http://www.yiiframework.com/wiki/281/searching-and-sorting-by-related-model-in-cgridview/
If that link doesn't help post your model codes for both the tables and I'll try to provide some example code.

Sub-queries ActiveRecord Yii

Is it possible to make sub-queries in ActiveRecord in Yii?
i have a query like this:
select * from table1
where table1.field1 in (select table2.field2 from table2)
i'm currently using the fallowing code:
object1::model()->findAll(array('condition'=>'t.field1 in (select table2.field2 from table2)'))
[Edit]
i would like to know if there is a manner to construct the sub-query without using SQL, and without using joins.
Is there any solution ?
and thanks in advance.
First find doublets by db fields:
$model=new MyModel('search');
$model->unsetAttributes();
$criteria=new CDbCriteria();
$criteria->select='col1,col2,col3';
$criteria->group = 'col1,col2,col3';
$criteria->having = 'COUNT(col1) > 1 AND COUNT(col2) > 1 AND COUNT(col3) > 1';
Get the subquery:
$subQuery=$model->getCommandBuilder()->createFindCommand($model->getTableSchema(),$criteria)->getText();
Add the subquery condition:
$mainCriteria=new CDbCriteria();
$mainCriteria->condition=' (col1,col2,col3) in ('.$subQuery.') ';
$mainCriteria->order = 'col1,col2,col3';
How to use:
$result = MyModel::model()->findAll($mainCriteria);
Or:
$dataProvider = new CActiveDataProvider('MyModel', array(
'criteria'=>$mainCriteria,
));
Source: http://www.yiiframework.com/wiki/364/using-sub-query-for-doubletts/
No, there is not a way to programmatically construct a subquery using Yii's CDbCriteria and CActiveRecord. It doesn't look like the Query Builder has a way, either.
You can still do subqueries a few different ways, however:
$results = Object1::model()->findAll(array(
'condition'=>'t.field1 in (select table2.field2 from table2)')
);
You can also do a join (which will probably be faster, sub-queries can be slow):
$results = Object1::model()->findAll(array(
'join'=>'JOIN table2 ON t.field1 = table2.field2'
);
You can also do a direct SQL query with findAllBySql:
$results = Object1::model()->findAllBySql('
select * from table1 where table1.field1 in
(select table2.field2 from table2)'
);
You can, however, at least provide a nice AR style interface to these like so:
class MyModel extends CActiveRecord {
public function getResults() {
return Object1::model()->findAll(array(
'condition'=>'t.field1 in (select table2.field2 from table2)')
);
}
}
Called like so:
$model = new MyModel();
$results = $model->results;
One interesting alternative idea would be to create your subquery using the Query Builder's CDbCommand or something, and then just pass the resulting SQL query string into a CDbCritera addInCondition()? Not sure if this will work, but it might:
$sql = Yii::app()->db->createCommand()
->select('*')
->from('tbl_user')
->text;
$criteria->addInCondition('columnName',$sql);
You can always extend the base CDbCriteria class to process and build subqueries somehow as well. Might make a nice extension you could release! :)
I hope that helps!
I know this an old thread but maybe someone (like me) still needs an answer.
There is a small issues related to the previous answers. So, here is my enhancement:
$model=new SomeModel();
$criteria=new CDbCriteria();
$criteria->compare('attribute', $value);
$criteria->addCondition($condition);
// ... etc
$subQuery=$model->getCommandBuilder()->createFindCommand($model->getTableSchema(),$criteria)->getText();
$mainCriteria=new CDbCriteria();
$mainCriteria->addCondition($anotherCondition);
// ... etc
// NOW THIS IS IMPORTANT
$mainCriteria->params = array_merge($criteria->params, $mainCriteria->params);
// Now You can pass the criteria:
$result = OtherModel::model()->findAll($mainCriteria);

Categories