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); 
31

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.

  • thanks marius i was expecting your response, btw your ultimate module creator is kool – Deepak Mallah May 21 '14 at 6:45
  • 1
    it is not actually the fastest choose ... check my answer below – Fra Oct 12 '15 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 '15 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 '15 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 '15 at 13:11
27

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)
  • ...
  • 4
    it would be always better to comment downvote ... – Fra Mar 16 '16 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 '16 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 '17 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 '17 at 15:38
  • 1
    Saved me hours of work. – dipole_moment Aug 27 '17 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
  • you may review my answer to understand why those function takes different times ... – Fra Oct 25 '17 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 '18 at 12:53
  • @snh_nl you have to use the ID - comma seperated for multiselect attributes. – sv3n Mar 21 '18 at 13:12

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

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