I have a model Reporter.
public function relations() {
return array(
'video' => array(self::HAS_MANY, 'Video', 'reporter_id'),
);
}
I have a mysql query for Reporter->search() method:
SELECT t.*, COUNT(t1.id) AS 'reports'
FROM reporter AS t
LEFT JOIN video AS t1 ON t.id = t1.reporter_id
GROUP BY t.id
ORDER BY t.name
How can it be written in terms of CDbCriteria to use with CActiveDataProvider?
To write that query into the criteria for a CActiveDataProvider you'll need to set the following properties from CDbCriteria:
select,
join,
group, and
order.
You can then set them while creating a new CActiveDataProvider object like so:
$dataProvider=new CActiveDataProvider('Reporter', array(
'criteria'=>array(
'select'=>'`t`.*, count(`t1`.`id`) as `reports`',
'join'=>'LEFT JOIN `video` AS `t1` ON `t`.`id` = `t1`.`reporter_id`',
'group'=>'`t`.`id`',
'order'=>'`t`.`name`',
),
));
The whole list of properties and methods that you can use with CDbCriteria can be found here: CDbCriteria Class Reference
Related
I'm currently having a problem with my query
TableA::find()
->select('*')
->joinWith(['TableB'])
->joinWith(['TableC'])
->joinWith(['TableD'])
->where([TableA.attribute1=1 or TableA.attribute2=1])
->andWhere([(further possible conditions on the other Tables)])
->all()
The usual execution order for SQL Queries is From (with joins) and then where.
Is there a way to execute the first where condition before the joins, to reduce the amount of joined lines? Something like
TableA::find()
->where([TableA.attribute1=1 or TableA.attribute2=1])
->select('*')
->joinWith(['TableB'])
->joinWith(['TableC'])
->joinWith(['TableD'])
->andWhere([(further possible conditions on the other Tables)])
->all()
You can modify the condition that is used for joining the tables.
In SQL it would look like this:
SELECT
*
FROM
`tableA`
JOIN `tableB` ON (
`tableA`.`FK` = `tableB`.`PK`
AND `tableA`.`attr1` = 'someValue'
)
JOIN tableC ON (`tableB`.`FK` = `tableC`.`PK`)
To do that in Yii you can use ActiveQuery::onCondition() method. If you want to apply this condition only for this one query you can use callback in joinWith() method to modify the query used for join.
$query = TableA::find()
->joinWith([
'tableB' => function(\yii\db\ActiveQuery $query) {
$query->onCondition(['tableA.attr1' => 'someValue']);
}
])
//... the rest of query
Other option would be using a subquery in FROM part of sql query like this:
SELECT
*
FROM
(
SELECT
*
FROM
`tableA`
WHERE
`attr1` = 'someValue'
) AS `tableA`
JOIN `tableB` ON (`tableA`.`FK` = `tableB`.`PK`)
In yii:
$subQuery = TableA::find()
->select('*')
->where(['attr1' => 'someValue']);
$query = TableA::find()
->select('*')
->from(['tableA' => $subQuery])
->joinWith('tableB')
// ... the rest of query
The main weakness of this approach is that temporary table from your subquery won't have any indexes so the join and other conditions will be slower. It might still be worth to use this approach if the tableA has a lot of rows and the condition you want to apply before join will reduce the number of rows significantly.
Well, I think that's the best way at all. But if you don't print data from relation TableB or TabelC (you just get them only about where condition), you can set the relation like that:
TableA::find()
->joinWith('TableB', false) //dont load data from this relational table
->joinWith('TableC', false) //dont load data from this relational table
->joinWith(['TableD']) //load data from this relational table
->where([TableA.attribute1=1 or TableA.attribute2=1])
->andWhere([(further possible conditions on the other Tables)])
->all()
Lets say Table1 references Table2 while Table2 references Table3.
Query:
x.Table1.Include(x=> x.Table2)
.Where(x=> x.something == 1).Select(x=> new{
T2Model = x.Table2.Select(y=> new{
T3Val = y.Table3.val
})
}).ToList()
Will Include(x=> x.Table2) ensure that val from Table3 would be loaded without issues?
Your query should work if you remove the Include call:
x.Table1
.Where(x=> x.something == 1).Select(x=> new{
T2Model = x.Table2.Select(y=> new{
T3Val = y.Table3.val
})
}).ToList();
Your projection is what ensures that val from Table3 will be loaded, because your Linq to Entities query is going to be translated to sql later, and due to what you are doing in your Select, the Linq provider will deduct it needs to do two inner joins to do that projection.
If you don't project using related properties and you want to load them (having lazy loading disabled), then use the Include extension method:
x.Table1.Include(x=> x.Table2.Select(e=>e.Table3));
I am working on a query in NHibernate in which user could provide a sorting order for some selected fields. I need to do a OrderBy() in QueryOver with the names of field names in entities, but when using projection list I am getting like this.
SELECT
this_.Number as y0_,
scc3_.Code as y1_,
FROM sometable
WHERE 1=1 ORDER BY this_.Number as y0_,
scc3_.Code as y1_ asc
The projection list for select columns is different from orderby projection list and I am not using .WithAlias()
sortProjectionList.Add(Projections.Property(() => stock.Number));
sortProjectionList.Add(Projections.Property(() => scc.Code));
How can I create a projectionlist without aliases or aliases with custom names?
Thanks
I expect, that you experience an issue with generated keyword "AS" inside of the ORDER BY in case, that you pass projections like this:
// NHibernate will leave few AS, except the last
query.OrderBy(sortProjectionList).Asc();
To avoid that, we can do it like this:
// NHibernate will solve each projection separately
for (var index = 0; index < sortProjectionList.Length; index++)
{
query.OrderBy(sortProjectionList[index]).Asc();
}
I have a SQL query like this
SELECT *
FROM nu.tb_class t
WHERE NOT EXISTS (SELECT st_id FROM student_class s WHERE s.st_id = t.id)
I need this to put it Cdbcriteria can any tell me how to do this ?
(somehow I need to put this data to CGridView if there is any other way I like to know it)
You can use the NotInCondition in your Criteria
http://www.yiiframework.com/doc/api/1.1/CDbCriteria#addNotInCondition-detail
Note: that it takes an array, that means you have to select only the st_id column, and pass it to the addNotInCondition function ... the easy way to do this is some thing like:
$st_ids = CHtml::listData(StudentClass::model()->findAll($criteria)), 'column1', 'column2');
And for sure you can run it as sql query, then use the CArrayDataProvider
http://www.yiiframework.com/doc/guide/1.1/en/database.dao#executing-sql-statements
http://www.yiiframework.com/doc/api/1.1/CArrayDataProvider
You can convert that query to an equivalent LEFT JOIN query:
$classes = TbClass::model()->findAll(array(
'condition' => 's.st_id IS NULL',
'join' => 'LEFT JOIN student_class s ON t.id=s.st_id',
));
Notice that you can pass the properties of a CDbCriteria to findAll().
Is it possible to code (not mapping) following query CONDITION for NHibernate join criteria for lazy child list?
...
JOIN ChildTable child ON child.ParentID = parent.ID AND <CONDITION>
NHibernate 2.1
Yes it's possible to write that query using the criterion or the QueryOver API. Here's an example with QueryOver api
Child childAlias = null;
session.QueryOver<Parent>()
.JoinAlias(w => w.Children,
() => childAlias ,
JoinType.None,
Restrictions.Where<Child>(s => s.Active)
).List();