问题
How do I write this kind of COALESCE()
statement in the query builder?
SQL
SELECT COALESCE(n.value, p.value) AS value
FROM nodes n
LEFT JOIN parents p ON p.id = n.parent_id
PHP
I can retrieve both the child and parent values and then go through the result set and just use the parent one if the child one is empty, but if there is a more elegant way to build it into the query itself, I would prefer that.
$child = $this->Nodes->find()
->select(['id', 'value'])
->where(['Nodes.id' => $id])
->contain([
'Parents' => function ($q) {
return $q->select('value');
}
])
->first();
if (empty($child->value)) {
$child->value = $child->parent->value;
}
Update 1
So this is what I have at the moment, but it doesn't work.
$child = $this->Nodes->find()
->select(['id', 'value'])
->where(['Nodes.id' => $id])
->contain([
'Parents' => function ($q) {
return $q->select([
'value' => $q->func()->coalesce([
'Nodes.value',
'Parents.value'
])
]);
}
])
->first();
Returns:
object(Cake\ORM\Entity) {
'id' => (int) 234,
'value' => (float) 0,
'[new]' => false,
'[accessible]' => [
'*' => true
],
'[dirty]' => [],
'[original]' => [],
'[virtual]' => [],
'[errors]' => [],
'[invalid]' => [],
'[repository]' => 'Nodes'
}
The child value is NULL
and the parent value is 1.00
so I would expect the entity value to be 'value' => (float) 1.00
but I assume it's coming out of the query as FALSE
converted to (float) 0
.
Update 2
It seems aliasing the coalesce to a name which already exists as a normal field does not work. It requires a unique field name for the coalesce result.
Update 3
I did another test and selected the name
field from the two tables instead, and it just returns the actual strings I entered into the function (they do not get evaluated as column names):
return $q->select([
'value' => $q->func()->coalesce([
'Nodes.name',
'Parents.name'
])
]);
The returned entity has:
'value' => 'Nodes.name'
So my new question would be how to get the query builder to evaluate the strings as table/field names?
回答1:
I could not get Cake's coalesce()
function to evaluate the parameters as fields, it was just returning the actual strings of the field names.
I got it working by manually creating the COALESCE
statement instead.
// Create the query object first, so it can be used to create a SQL expression
$query = $this->Nodes->find();
// Modify the query
$query
->select([
'id',
'value' => $query->newExpr('COALESCE(Nodes.value, Parents.value)')
])
->where(['Nodes.id' => $id])
->contain('Parents')
->first();
回答2:
See http://book.cakephp.org/3.0/en/orm/query-builder.html#using-sql-functions
Haven't tried it but I guess it's:
$child = $this->Nodes->find()
->select(['id', 'value'])
->where(['Nodes.id' => $id])
->contain([
'Parents' => function ($q) {
return $q->select(['value' => $query->func()->coalesce([
/* Fields go here... I think. :) */
])]);
}
])
->first();
If this isn't working check the unit tests of the core how to call this function.
来源:https://stackoverflow.com/questions/40534557/cakephp-3-how-to-write-coalesce-in-query-builder