Unnecessary queries for updating #4964

Closed
opened 2026-01-22 14:54:12 +01:00 by admin · 12 comments
Owner

Originally created by @trsteel88 on GitHub (Jan 7, 2016).

Originally assigned to: @Ocramius on GitHub.

I'm not sure if this is by design. However, I've noticed that if you have a decimal and store it as a float, Doctrine will run a MySQL query unnecessarily for certain values.

e.g.

ProductModel.php

...
/**
     * Set price.
     *
     * @param float $price
     *
     * @return $this
     */
    public function setPrice($price)
    {
        $this->price = (float) $price;

        return $this;
    }
...

Code:
```php

$product->setPrice('10.20');
$product->setPrice(10.20);

For both examples above, (float) will remove the zero from the end. This results in "10.2". Since the value within the database is '10.20', Doctrine executes an UPDATE query even though nothing has changed.

Is this by design so values don't need to be checked and reduce overhead?

Originally created by @trsteel88 on GitHub (Jan 7, 2016). Originally assigned to: @Ocramius on GitHub. I'm not sure if this is by design. However, I've noticed that if you have a decimal and store it as a float, Doctrine will run a MySQL query unnecessarily for certain values. e.g. ProductModel.php `````` php ... /** * Set price. * * @param float $price * * @return $this */ public function setPrice($price) { $this->price = (float) $price; return $this; } ... Code: ```php $product->setPrice('10.20'); $product->setPrice(10.20); `````` For both examples above, (float) will remove the zero from the end. This results in "10.2". Since the value within the database is '10.20', Doctrine executes an UPDATE query even though nothing has changed. Is this by design so values don't need to be checked and reduce overhead?
admin added the Can't Fix label 2026-01-22 14:54:12 +01:00
admin closed this issue 2026-01-22 14:54:12 +01:00
Author
Owner

@Ocramius commented on GitHub (Jan 7, 2016):

@trsteel88 something else is happening here, in my opinion. Could you dump the entity diffs from a UnitOfWork? Can you reproduce this in a test case scenario?

@Ocramius commented on GitHub (Jan 7, 2016): @trsteel88 something else is happening here, in my opinion. Could you dump the entity diffs from a `UnitOfWork`? Can you reproduce this in a test case scenario?
Author
Owner

@billschaller commented on GitHub (Jan 7, 2016):

@Ocramius could be because some drivers return float/real/decimal as string and some don't.

@billschaller commented on GitHub (Jan 7, 2016): @Ocramius could be because some drivers return float/real/decimal as string and some don't.
Author
Owner

@trsteel88 commented on GitHub (Jan 7, 2016):

A dump of the UnitOfWork changeset for an entity is:

array:2 [
  "price" => array:2 [
    0 => "289.00"
    1 => 289.0
  ]
]
@trsteel88 commented on GitHub (Jan 7, 2016): A dump of the UnitOfWork changeset for an entity is: ``` php array:2 [ "price" => array:2 [ 0 => "289.00" 1 => 289.0 ] ] ```
Author
Owner

@Ocramius commented on GitHub (Jan 7, 2016):

@Ocramius could be because some drivers return float/real/decimal as string and some don't.

Ah yes, indeed. I was thinking that we were discussing fresh entities flushed multiple times.
Floating point values (entities with values mapped as numeric) will store the values as string inside your entities, therefore a (float) cast changes the type, and therefore the value is not the same anymore.

This is expected behavior: you are supposed to map the value as a string inside your entities, or to use a custom DBAL type if you trust a (float) cast enough :-)

Closing as can't fix.

@Ocramius commented on GitHub (Jan 7, 2016): > @Ocramius could be because some drivers return float/real/decimal as string and some don't. Ah yes, indeed. I was thinking that we were discussing fresh entities flushed multiple times. Floating point values (entities with values mapped as `numeric`) will store the values as `string` inside your entities, therefore a `(float)` cast changes the type, and therefore the value is not the same anymore. This is expected behavior: you are supposed to map the value as a `string` inside your entities, or to use a custom DBAL type if you trust a `(float)` cast enough :-) Closing as `can't fix`.
Author
Owner

@trsteel88 commented on GitHub (Jan 7, 2016):

Ah got it. Thanks for clarifying. I thought Doctrine would normalize the decimal before adding it to the changeset since it knows the precision/scale of the Decimal.

@trsteel88 commented on GitHub (Jan 7, 2016): Ah got it. Thanks for clarifying. I thought Doctrine would normalize the decimal before adding it to the changeset since it knows the precision/scale of the Decimal.
Author
Owner

@Ocramius commented on GitHub (Jan 7, 2016):

@trsteel88 we can't really convert numeric values in PHP due to the risk of a lossy cast :-\ If a (float) just cuts it for your use-case, then I suggest you use that, but the tool is indeed used also for monetary transactions, and you don't want to see problems there, heh...

@Ocramius commented on GitHub (Jan 7, 2016): @trsteel88 we can't really convert numeric values in PHP due to the risk of a lossy cast :-\ If a `(float)` just cuts it for your use-case, then I suggest you use that, but the tool is indeed used also for monetary transactions, and you don't want to see problems there, heh...
Author
Owner

@Ocramius commented on GitHub (Jan 7, 2016):

Actually, the FloatType should just work for you :-)

@Ocramius commented on GitHub (Jan 7, 2016): Actually, the [`FloatType`](https://github.com/doctrine/dbal/blob/fbd6ab259240880e3a2bb2dc8e6f0c1a89521cfa/lib/Doctrine/DBAL/Types/FloatType.php) should just work for you :-)
Author
Owner

@trsteel88 commented on GitHub (Jan 7, 2016):

No worries. Yes, I saw FloatType. However, it doesn't let you specify scale/precision. I want to store the data as a decimal with scale/precision but cast it to a float. FloatType is a Double type.

@trsteel88 commented on GitHub (Jan 7, 2016): No worries. Yes, I saw FloatType. However, it doesn't let you specify scale/precision. I want to store the data as a decimal with scale/precision but cast it to a float. FloatType is a Double type.
Author
Owner

@Ocramius commented on GitHub (Jan 7, 2016):

You'll probably need a hybrid type, assuming you can live with the possibility of lossy conversions

@Ocramius commented on GitHub (Jan 7, 2016): You'll probably need a hybrid type, assuming you can live with the possibility of lossy conversions
Author
Owner

@trsteel88 commented on GitHub (Jan 7, 2016):

Hmm, I could probably even create an EventListener that checks the value before and after by using round() according to the scale set on the mapping for that field.

Thanks for your help.

@trsteel88 commented on GitHub (Jan 7, 2016): Hmm, I could probably even create an EventListener that checks the value before and after by using round() according to the scale set on the mapping for that field. Thanks for your help.
Author
Owner

@trsteel88 commented on GitHub (Jan 7, 2016):

Or I could just do...

    /**
     * Get price.
     *
     * @return float
     */
    public function getPrice()
    {
        return (float) $this->price;
    }

    /**
     * Set price.
     *
     * @param float $price
     *
     * @return $this
     */
    public function setPrice($price)
    {
        $this->price = sprintf('%.2f', $price);

        return $this;
    }

Only downside, if $this->price is used within that model, it isn't going to be a float.

@trsteel88 commented on GitHub (Jan 7, 2016): Or I could just do... ``` php /** * Get price. * * @return float */ public function getPrice() { return (float) $this->price; } /** * Set price. * * @param float $price * * @return $this */ public function setPrice($price) { $this->price = sprintf('%.2f', $price); return $this; } ``` Only downside, if $this->price is used within that model, it isn't going to be a float.
Author
Owner

@Ocramius commented on GitHub (Jan 7, 2016):

I think it would be best to use an embeddable type (value object) to deal with this sort of issue, so you avoid doing the casts manually every time.

@Ocramius commented on GitHub (Jan 7, 2016): I think it would be best to use an embeddable type (value object) to deal with this sort of issue, so you avoid doing the casts manually every time.
Sign in to join this conversation.
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: doctrine/archived-orm#4964