Mass delete products in Magento

Please note. This article applies to Magento 1.4 and greater only. Some elements of the code are compatible with 1.3, but it will not work in 1.3 and below without modification.

If you have a BIG catalogue - you soon find out that management via the admin can just become tedious. We performed a product import of 80,000 products which unfortunately was supplied with inaccurate information.

Because of the complexity of the products and sheer volume of attributes- it is quicker to delete then reimport. But deleting via the admin for 80k products is nigh on impossible.

The easiest way to pull this off is by means of a quick script. The key to this script is that the indexing functionality is disabled entirely for the process, then enabled again at the end - requiring a manual update at the end, or you could code in an index refresh using our article on refreshing catalogue indexes using a php script.

echo "Started " . date("d/m/y h:i:s") . "rn";
define('MAGENTO', "/domains/");
require_once MAGENTO . '/app/Mage.php';
$products = Mage::getModel('catalog/product')->getCollection()->addFieldToFilter('data_set', 1544);
$sql = "";
$undoSql = "";

for ($i = 0; $i <= 8; $i++) {
    $sql.= "UPDATE index_process SET mode = 'manual' WHERE index_process.process_id =$i LIMIT 1;";
    $undoSql.= "UPDATE index_process SET mode = 'real_time' WHERE index_process.process_id =$i LIMIT 1;";

$mysqli = Mage::getSingleton('core/resource')->getConnection('core_write');
$total_products = count($products);
$count = 0;

foreach($products as $product) {
    if ($count++ % 100 == 0) {
        $cur = strtotime(date("d/m/y h:i:s")) - $time;
        $time = strtotime(date("d/m/y h:i:s"));
        echo round((($count / $total_products) * 100) , 2) . "% deleted ($count/$total_products) " . round(100 / $cur) . " p/s " . date("h:i:s") . "rn";

echo "Ended " . date("d/m/y h:i:s") . "rn";

In our case, we filtered the type of products by its "Data Set" - an attribute assigned to that batch of products, but you can change this to suit any means of filtering.

The above script runs at around 25 products deleted per second.

  • Ed

    Hey thanks for the scripts on this site. I get the following error when using this script:

    There has been an error processing your request
    SELECT `e`.* FROM `catalog_product_entity` AS `e`

    I've looked through it but don't see where this SQL statement is being generated.

    The only change I made to your script was chaning:

    $products = Mage::getModel('catalog/product')
    ->addFieldToFilter('data_set', 1544);


    $products = Mage::getModel('catalog/product')

    as I want to delete all products.

    Do you know why this error would appear?

    • Dj

       I have the same problem.  I didnt understand how to modify it for all products.

  • Thanks for the script.

    On my server it works only with the speed near 2 products per second. But it is still good.

    Hint for others.

    You can delete intervals of products depending on their ids.

    $products = Mage::getModel('catalog/product')

    if($product->getId()>4133 && $product->getId()delete();
    echo $product->getId().' deleted'."n";

  • Could someone please supply me with a code that will work with Magento v1.3.2.4? I need to delete 160,000 products asap.

    • Get in touch with us, we could put a cheap script together for you.

  • David

    Why are you using raw sql in magento! This is very bad practice. Magento has gone to great lengths to make sure developers avoid this. Fair enough, using the mage models will be a little slower than raw sql but still, for a leading magento developer this is really suprising!

    • Hi David,

      The only SQL used is just to disable the "update on save" index management. Magento models have their place, and we adhere to their coding standards whenever possible, but in some circumstances, its pretty quick to use plain 'ole SQL, especially in a 50 line standalone, disposable script.

      And if you don't like raw SQL in Magento, you'll hate our Full Text Index re-building extension 😉 3 seconds for 250,000 products vs. 1h30 mins ... which would you rather

  • Enrique

    Thanks for your script I can delete all my products without problem, 24 seconds / 100 products. You save me a lot of time.

    • Chris

      How do I actually implement / run this script?

      • I have 2700 products that are in disabled status. I would like to just delete them only. Do i change ->addFieldToFilter('data_set', 1544); To ->addFieldToFilter('status', 0);

        And what do you put here for your domain I have Magento in my root dir with no domain.

        define('MAGENTO', "/domains/");
        because every time I run this script I just get the date and nothing happens, the browser at the bottom says "done"

  • Sunit


    I have use above script for delete product. After that i am trying to create product, it create successfully but not display in front. I am using magenot Can you please let me know why it is not display.

    Thank You,
    Sunit Ardeshna

  • Love the script - it has the potential to be very useful.
    However it kept timing out on my server (slow hosting perhaps? I did have 2000 products)
    So I had to run it maybe 4 or 5 times.
    The last batch I just deleted manually from the admin interface...
    But of course that didn't switch those darn indexes back to "update on save".

    I've been scratching my head for an hour trying to work out why no new products were appearing!
    Still - good work guys!

  • Colum Quinn

    thanks for the script. The only thing is that it times out very regularly.
    Ive tried extending the php max_execution_time in both .htaccess and in the script itself but I've had no joy.
    Any other ideas. I'm on a shared server and running Magento
    much appreciated

  • JB


    i have to delete a ID range of old products.
    can anyone explain, how i have to use the "addFieldToFilter" feature in this case?

  • Webninja

    Is there a way to modify this script so that it only deletes the disabled products?

    • Sergiy Popov

      Try this sql from MySQL console:
      DELETE e.* FROM catalog_product_entity e
      INNER JOIN catalog_product_entity_int v
      ON v.entity_id=e.entity_id
      AND v.attribute_id=(SELECT attribute_id FROM eav_attribute WHERE attribute_code='status')
      WHERE v.value=2;

      More information here