Can I Recreate The Following MySQL Query In CakePHP? - mysql

I'm trying to achieve this query in CakePHP (1.3, if that's relevant):
select * from releases r join formats f on r.id = f.release_id
where r.default_upc = f.bar_code
I was hoping I could do something in the Release model like:
var $hasOne = array('Format'=>array(
'conditions' => array('Release.default_upc'=>'Format.bar_code')
));
Unfortunately this just results in a null Format; evidently 'Format.bar_code' is not yet available at the time the query is made.
What's the quickest route to getting the results I want?

Hmm, it does appear that simply changing the conditions to
'conditions' => array('Release.default_upc = Format.bar_code')
may elicit the results I seek. Is this an idiomatic Cake way of doing things?

As far as I know, using conditions with JOINS, in cakePHP, should be done as you did in the answer you provided.
Happened to me several times before
The first way should be used in the model itself and within "regular" find calls.

Related

Multi-table generic query model for CakePHP

I have approximately 25 join queries I need to build to run in a project on CakePHP.
It is much faster to just work in MySQL WorkBench or phpMyAdmin building the queries,
and then drop into PHP code than to use the screwed up cakePHP model parms that many times
have no one-to-one relationship to a MySQL query.
Is there a no-table or multi-table "take anything MySQL" example that reduces or eliminates the CakePHP complexity?
TLDR:
Use the Model's query() method to write any MySQL you'd like.
More details:
It sounds like you haven't actually read through the book. Should probably try that before bashing things that you don't understand.
For anyone else who might want to learn how to join things in CakePHP:
How to Join using CakePHP
And
How to run any MySQL query in CakePHP
CakePHP provides Model::query() for you to have more control. I'd be inclined that you use prepared statements too, if you really want to let go of CakePHP's find methods.
$db = $this->getDataSource();
$db->fetchAll(
'SELECT * from users where username = ? AND password = ?',
array('jhon', '12345')
);
$db->fetchAll(
'SELECT * from users where username = :username AND password = :password',
array('username' => 'jhon','password' => '12345')
);
Here is a note:
query() does not honor $Model->cacheQueries as its functionality is
inherently disjoint from that of the calling model. To avoid caching
calls to query, supply a second argument of false, ie: query($query,
$cachequeries = false)

Yii activerecord and pagination count() slow query

So basicly the problem is in query SELECT COUNT(*) which executed in calculateTotalItemCount function in activedataprovider. As i understood it needed for pagination for $itemcount variable. The problem is this query slow for big tables. For my ~30m table it executes 5 seconds.
So there are 2 ways to solve this problem:
1. Disable pagination ('pagination'=>'false') and write own pagination.
2. Rewrite AR count function.
I dont have enough experience/knowledge to acomplish this.
Maybe some one had same issues before and can share his solution.
Atleast for totalItemCount we can use EXPLAIN SELECT *. Its way more faster.
I appreciate any help. Thank you.
If you have a "cheaper" query in raw SQL than the one that active records create automatically, you can also query manually (e.g. through DAO) and set the totalItemCount on your data provider:
$count = Yii::app()->db->createCommand('SELECT COUNT(*)...')->queryScalar();
$provider = new CActiveDataProvider('SomeModel', array(
'totalItemCount' => $count,
'criteria' => $criteria,
...

Minimum and maximum of a field in cakephp and mysql

I am trying to build a search function for a cakephp and mysql site. Selecting different parameters like price of the product or the length triggers an ajax call which returns the number of matching results. I want to extend the returned results with the the minimum and maximum values for the lengths and prices. I tried doing this, http://bin.cakephp.org/view/1004813660 . Using the first 4 finds is too time consuming. The last one functions locally, but I get the error;
1140 - Mixing of GROUP columns (MIN(),MAX(),,...) with no GROUP columns is illegal if there is no GROUP BY clause`
remotely, due to ONLY_FULL_GROUP_BY being on.
Is it possible to use the last option with some improvements, or can I switch off ONLY_FULL_GROUP_BY?
If I understood you well, you want to get in a single request
MIN(Yacht.price) as min_price
MAX(Yacht.price) as max_price
MIN(Yacht.long) as min_length
MAX(Yacht.long) as max_length
right ?
For this, you do not need any "Group By" clause. MIN and MAX functions are already aggregations functions. But nothing prevents you from using multiple aggregations functions in a single request.
Have you tried simply doing this ?
$stats = $this->Yacht->find(array(
'conditions' => $conditions,
'fields' => array(
'MIN(Yacht.price) as min_price',
'MAX(Yacht.price) as max_price',
'MIN(Yacht.long) as min_length',
'MAX(Yacht.long) as max_length'
)
)
);
By the way, according to the documentation, there seems to be quite a lot of redundancy in your original code. "find('first', array(...))" by itself ensures you get only one result hence, there is no need to specify "'limit' => 1" in the request nor "order" clause as there would be only one field anyway :)
Hope it helps.
The way to set server modes can be found here... If you read the top of the document it will tell you how to set the server mode defaults:
http://dev.mysql.com/doc/refman/5.1/en/server-sql-mode.html
However, I'm not sure that is necessary to get to your solution. I think your query is running for a long time because you need a different group by in your code and less queries. You should be able to use a logical group by that will maximize your primary key (index):
'group' => 'Yacht.id'
So you have one query returning everything:
$this->Yacht->find('first', array(
'conditions' => $conditions,
'fields' => array('MAX(Yacht.price) as max_price', 'MIN(Yacht.price) as min_price', ...)
'group' => 'Yacht.id'
'order' => '...'));
I ended up solving the problem by changing the way I was searching. Instead of doing queries in the conditions that would lead to joins, I explicitly did the searching with where. I had things like,
$conditions = array('Brand.name LIKE'=> '%bla%');
which I replaced it with
$condtions = array('Yacht.brand_name LIKE' => '%bla%');
I had to restructure the database a bit, but the tradeoff between speed and database normalization is one I can live with.

CakePHP parsing query results alternative

I have a query that's returning a LOT of results and my code is running out of memory trying to parse the results... how can I run a query in CakePHP and just get normal results?
By parsing it I mean....
SELECT table1.*, table2.* FROM table1 INNER JOIN table2 ON table1.id = table2.table1_id
With the above query it'll return....
array(
0 => array(
'table1' => array(
'field1' => value,
'field2' => value
),
'table2' => array(
'field1' => value,
'field2' => value
)
)
)
When it parses those results into nested arrays is when it's running out of memory.... how do I avoid this?
I couldn't hate CakePHP any more than I do right now :-\ If the documentation was decent that would be one thing, but it's not decent and it's functionality is annoying.
you could do:
$list = $this->AnyModel->query("SELECT * FROM big_table");
but i dont think that will solve your problem, because if you have, for exemple, 10millon rows.. php wont be able to manage an array of 10millon values...
but you might want to read this two links to change the execution time and the memory limit.. you could also change them on your php.ini
Good Luck!
EDITED
hmm thanks to your question i've learned something :P First of all, we all agree that you're receiving that error because Cake executes the query and tries to store the results in one array but php doesn't support an array that big so it runs out of memory and crashes.. I have never used the classic mysql_query() (i prefer PDO) but after reading the docs, it seems that mysql_query stores the results inside a resource therefore, it's not loading the results on memory, and that allows you to loop the results (like looping though a big file). So now i see the difference... and your question is actually, this one:
Can I stop CakePHP fetching all rows for a query?
=) i understand your frustration with cake, sometimes i also get frustrated with it (could you believe there's no simple way to execute a query with a HAVING clause?? u_U)
Cheers!
I'd suggest you utilize the Containable behavior on your model. This is the easiest way to control the amount of data that's returned. I've confident that this is precisely what you need to implement.
CakePHP :: Containable :: Core Behaviors
You should limit the rows returned from your query (like 500 rows) and allow the user to fetch more rows when needed (next 500 rows at a time). You could do that nicely with the pagination component and a little AJAX.

CakePHP database query - am I overcomplicating things?

So, I need to search a real estate database for all homes belonging to realtors who are part of the same real estate agency as the current realtor. I'm currently doing this something like this:
$agency_data = $this->Realtor->find('all',array(
'conditions'=>
array(business_name'=>$realtor_settings['Realtor']['business_name']),
'fields'=>array('num'),
'recursive'=> -1
));
foreach($agency_data as $k=>$v){
foreach($v as $k=>$v1){
$agency_nums[] = $v1['num'];
}
}
$conditions = array(
'realtor_num'=>$agency_nums
);
It seems a bit crazy to me that I'm having to work so hard to break down the results of my first query, just to get a simple, one-dimensional array of ids that I can use to build a condition for my subsequent query. Am I doing this in an insanely roundabout way? Is there an easy way to write a single CakePHP query to communicate "select * from homes where realtor_num in (select num from realtors where business_name = 'n')"? If so, would it be any more efficient?
For sure it's complicated (in your way) :)
Depending from the results you can do following:
$agency_data = $this->Realtor->find('list',array(
'conditions'=>array('business_name'=>$realtor_settings['Realtor']['business_name']),
'fields'=>array('num', 'num'),
'recursive'=> -1
));
$agency_data; //this already contain array of id's
Method 2 - building a sub query there are 2 ways strict and not so strict :) The first one you can see here (search for Sub-queries).
The other option is to have following conditions parameter:
$this->Realtor->find('all', array('conditions'=>array('field in (select num from realtors where business_name like "'.$some_variable.'"))));
Of course be careful with the $some_variable in the sub-query. You shold escape it - use Sanitize class for example.
$agency_data = $this->Realtor->find('all',array(
'conditions'=>
array('business_name'=>$realtor_settings['Realtor']['business_name']),
'fields'=>array('num'),
'recursive'=> -1
));
$conditions = Set::extract("{n}.Realtor.num", $agency_data);
I would use something like Set::extract to grab the list of data you are looking for. The advantage of doing it this way is that you can reuse the same dataset in other places and save queries. You could also write the set::extract statement in this format:
$conditions = Set::extract("/Realtor/num", $agency_data);