SQL query opmitization for HABTM in CakePHP
My last SQL optimization post found no response from the CakePHP community; which makes me wonder how many are actually using ACL with large databases. Anyhow, the ticket was submitted and gwoo seemed to approve the results. I can also now confirm that the last suggested indexes will give another push to your performance:
ARO: alias ACO: model and foreign_key
Now what’s that about HABTM? While reading the source code for the Model class, I stumbled on the Model::_deleteLinks() method:
function _deleteLinks($id) {
$db =& ConnectionManager::getDataSource($this->useDbConfig);
foreach ($this->hasAndBelongsToMany as $assoc => $data) {
if (isset($data['with'])) {
$model =& $this->{$data['with']};
$records = $model->findAll(array($data['foreignKey'] => $id), $model->primaryKey, null, null, null, -1);
if (!empty($records)) {
foreach ($records as $record) {
$model->delete($record[$model->name][$model->primaryKey]);
}
}
} else {
$table = $db->name($db->fullTableName($data['joinTable']));
$conditions = $db->name($data['foreignKey']) . ' = ' . $db->value($id);
$db->query("DELETE FROM {$table} WHERE {$conditions}");
}
}
}
If you look carefully, you will notice a useless loop in the case your HABTM has the ‘with’ key set. That means that for every association record there is for the foreignKey, an SQL query will be committed - some food for thought…
I came up with a quick alternative that still hasn’t been really tested but it should work:
function _deleteLinks($id) {
$db =& ConnectionManager::getDataSource($this->useDbConfig);
foreach ($this->hasAndBelongsToMany as $assoc => $data) {
if (isset($data['with'])) {
$model =& $this->{$data['with']};
$table = $model->useTable;
$conditions = $db->name($data['foreignKey']) . ' = ' . $id;
} else {
$table = $db->name($db->fullTableName($data['joinTable']));
$conditions = $db->name($data['foreignKey']) . ' = ' . $db->value($id);
}
$db->query("DELETE FROM {$table} WHERE {$conditions}");
}
}
I also asked gwoo again before making a fool of myself and he suggested it be submitted as an enhancement.
That’s it for now, thought I’d share that - I believe you’d want to make the changes until they are implemented in the core (if you care about performance that is).
October 7th, 2007 at 11:44 am
Hi,
It’s good to see tweaks being suggested (as tickets); if they have diffs/patches and a test case they are likely to get looked at a lot quicker than without.
I wonder if the reliance in this method on query could cause problems with not-mySql dbs, it’s quite a simple query so maybe not but …. test cases allow such ponderings to be answered quite quickly
keep up the (myway? Ok a diff with a side of test-case please) patchin’,
cheers,
AD
October 7th, 2007 at 4:31 pm
AD7Six: You’re totally right about the tests. Too much work right now to write one, so I thought I should at least share it - maybe someone else can write the test if he/she is concerned with performance? hint, hint
TBH, having to be portable across different DB engines slipped my mind at the time of writing; but, with my limited experience I believe it shouldn’t cause any problem. Hopefully I’ll have some kind of test when I’ll be wrapping the app, - until then, too much work to worry about.
Oh, before I forget, thanks for dropping by; and since I never had the chance to comment on your blog, just wanted to say cheers for all the bits I gathered there.
Jad