15

I'm looking for the fastest and reliable method for mass attribute update. Below are the methods am aware of but I don't know with which I should go.

$store_id = 0;
Mage::getSingleton('catalog/product_action')->updateAttributes(
    array($product_id),
    array('attribute_code' => $attribute_code),
    $store_id
);

or

$product->setData($attribute_code, 1234); 
$product->getResource()->saveAttribute($product, $attribute_code); 

3 Answers 3

33

The fastest way is to do direct selects and inserts/updates in the database, but that's not the safest. You can easily break stuff.

I use the Mage::getSingleton('catalog/product_action')->updateAttributes(...) approach.
It is fast, you can use it to bulk update product attributes, you can update an attribute value for a specific store.
I think it covers most of the cases needed.

11
  • thanks marius i was expecting your response, btw your ultimate module creator is kool May 21, 2014 at 6:45
  • 1
    it is not actually the fastest choose ... check my answer below
    – Fra
    Oct 12, 2015 at 11:58
  • @Fra How is your method faster? It involves load. that anything but fast. For example, in method 2, the first line with the product load is useless. You are not using $product anywhere.
    – Marius
    Oct 12, 2015 at 12:20
  • @Fra. When you downvote someone's answer it's a nice thing to state a reason. What's wrong with my answer?
    – Marius
    Oct 12, 2015 at 12:45
  • 1
    I could say the same thing for your answer...it's not the fastest way. direct SQL queries are the fastest way. I don't think your reason is enough for downvoting. But you are entitled to your opinion.
    – Marius
    Oct 12, 2015 at 13:11
32

Actually there are 3 ways to update an attribute on a product without save the full product. Depending on the code/requirements one can be faster that the other.

Method 1:

$product = Mage::getModel('catalog/product')->load($product_id);
$resource = $product->getResource();

$product->setData($attribue_code, $value);
$resource->saveAttribute($product, $attribute_code);

Method 2:

$updater = Mage::getSingleton('catalog/product_action');
$updater->updateAttributes(array($product_id), array( $attribute_code => $value), 0);

Method 3: (fastest)

 $update_resource = Mage::getResourceSingleton('catalog/product_action');
 $update_resource->updateAttributes(array($product_id), array( $attribute_code => $value), 0);

All above method are much faster that save the entire product anyway there are some major performance difference:

Method 1:

  • is the fastest, but required you to load the product.
  • it doesn't trigger the reindex event (that is why is faster)
  • it works in frontend

Method 2:

  • allows to bulk update product
    (you can pass multiple product and multiple attributes)
  • it triggers the mass action event and the relative dependent reindex
  • it does not work in frontend

Method 3:

  • it is like method 2, but does not call any other observer/indexer
    (so it is a mixed approach between method 1 and 2)

Method 3 is the more flexible one anyway you'll have to reindex those product/attributes manually. (in order to get them updated on frontend)
It may be useful if you want to update a lot of products fast and then call the reindex at the end.
(if you use method 2 a reindex for each product is called after the update and these multiple calls make the whole process slow)

To manually reindex a single product see the functions provided by Mage_Catalog_Model_Product_Flat_Indexer such as:

  • updateAttribute($attributeCode, $store = null, $productIds = null)
  • updateProduct($productIds, $store = null)
  • ...
9
  • 4
    it would be always better to comment downvote ...
    – Fra
    Mar 16, 2016 at 7:39
  • Just a small note, the $value should be a the value code (integer), not the 'textual' value you see in your interface
    – Ali Alwash
    Sep 12, 2016 at 19:48
  • hmmm interesting. Method 2 sounds great, only you have no controll over the 'feedback' what succeeded, what did not? Method 1 is also good. But when I am done updating my loop over data: can I manually kick the reindex maybe to solve this for particulr product ids?
    – snh_nl
    Jun 17, 2017 at 14:21
  • Strange. using Method 2. I am getting an error unknown filter by column ``` Caught exception: SQLSTATE[42S22]: Column not found: 1054 Unknown column 'catalog_product_entity.value_id' in 'field list', query was: SELECT catalog_product_entity.value_id FROM catalog_product_entity WHERE (entity_type_id=4 AND attribute_id='68' AND entity_id='29')``` not expecting that. Running on 1.9.3.2
    – snh_nl
    Jun 17, 2017 at 15:38
  • 1
    Saved me hours of work. Aug 27, 2017 at 17:57
3

Update

I'm looking for the fastest and reliable method for mass attribute update

"Mass attribute update" for attributes or products?

Think updating multiple attributes is alread answered, but for products this can be usefull ...

If you want to update products from collection, you should not do this ...

foreach ($collection as $product) {
    $product->setSomeData(...);
    # not here
    $product->save();
}

This will dispatch events, rebuild pricerules and indices. With this no events (and some other things) are skipped and is lot faster.

foreach ($collection as $product) {
    $product->setSomeData(...);
}
$collection->save();

To avoid pricerule updates, you can add ...

$product->setIsMassupdate(true);

To disable/enable reindex on the fly take a look at this ... https://github.com/Flagbit/Magento-ChangeAttributeSet/commit/676f3af77fec880bc64333403675d183e8639fae

/**
 * Set indexer modes to manual
 */
private function _storeRealtimeIndexer()
{
    $collection = Mage::getSingleton('index/indexer')->getProcessesCollection();
    foreach ($collection as $process) {
        if($process->getMode() != Mage_Index_Model_Process::MODE_MANUAL){
            $this->_index[] = $process->getIndexerCode();
            $process->setData('mode', Mage_Index_Model_Process::MODE_MANUAL)->save();
        }
    }

}
/**
 * Restore indexer modes to realtime an reindex product data
 */
private function _restoreRealtimeIndexer()
{
    $reindexCodes = array(
        'catalog_product_attribute',
        'catalog_product_flat'
    );
    $indexer = Mage::getSingleton('index/indexer');
    foreach ($this->_index as $code) {
        $process = $indexer->getProcessByCode($code);
        if (in_array($code, $reindexCodes)) {
            $process->reindexAll();
        }
        $process->setData('mode', Mage_Index_Model_Process::MODE_REAL_TIME)->save();
    }
}

And also flushing cache before mass (product) update can increase performance ...

Mage::app()->getCacheInstance()->flush();

Some numbers from debugging here: https://github.com/Flagbit/Magento-ChangeAttributeSet/issues/16


Mage::getSingleton('catalog/product_action')->updateAttributes(...) seems not to be the fastest method ... at least not with mutlistore setup and flat tables turned on ...

  • saveAttribute()

    $product = Mage::getModel('catalog/product')->load($productId);
    $resource = $product->getResource();
    $product->setData($attributeCode, $attributeValue);
    $resource->saveAttribute($product, $attributeCode);
    
    • Total Incl. Wall Time (microsec): 437,787 microsecs
    • Total Incl. CPU (microsecs): 423,600 microsecs
    • Total Incl. MemUse (bytes): 4,433,848 bytes
    • Total Incl. PeakMemUse (bytes): 4,395,128 bytes
    • Number of Function Calls: 25,711
  • updateAttributes()

    Mage::getSingleton('catalog/product_action')->updateAttributes(
        array($productId),
        array($attributeCode => $attributeValue),
        $storeId
    );
    
    • Total Incl. Wall Time (microsec): 3,676,950 microsecs
    • Total Incl. CPU (microsecs): 3,122,064 microsecs
    • Total Incl. MemUse (bytes): 8,174,792 bytes
    • Total Incl. PeakMemUse (bytes): 8,199,192 bytes
    • Number of Function Calls: 150,132
  • updateAttributes() (resource singleton)

    Mage::getResourceSingleton('catalog/product_action')->updateAttributes(
        array($productId),
        array( $attributeCode => $attributeValue),
        $storeId
    );
    
    • Total Incl. Wall Time (microsec): 94,155 microsecs
    • Total Incl. CPU (microsecs): 48,568 microsecs
    • Total Incl. MemUse (bytes): 1,426,304 bytes
    • Total Incl. PeakMemUse (bytes): 1,370,456 bytes
    • Number of Function Calls: 2,221
3
  • you may review my answer to understand why those function takes different times ...
    – Fra
    Oct 25, 2017 at 5:44
  • For dropdown data does updateAttributes() (resource singleton) take the actual admin value? or the id of the dropdown element? (somehow we always get no value/empty value using this method ie nothing selected
    – snh_nl
    Mar 21, 2018 at 12:53
  • @snh_nl you have to use the ID - comma seperated for multiselect attributes.
    – sv3n
    Mar 21, 2018 at 13:12

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge that you have read and understand our privacy policy and code of conduct.

Not the answer you're looking for? Browse other questions tagged or ask your own question.