Answers.Body: <p>I haven't actually tested this, but going through the <a href="https://docs.craftcms.com/api/v3/craft-services-relations.html" rel="nofollow noreferrer">Relations service</a> instead of the Elements service might avoid the issues you're facing. Using the <a href="https://www.yiiframework.com/doc/api/2.0/yii-db-query#each()-detail" rel="nofollow noreferrer"><code>each()</code></a> method when looping over the element query results (as mentioned by August in the comments above) is also a good idea.</p>
  <pre class="lang-php prettyprint-override"><code>$fieldProductMediaField = Craft::$app-&gt;getFields()-&gt;getFieldByHandle('fieldProductMedia');
  
  foreach ($query-&gt;each() as $product) {
      Craft::$app-&gt;getRelations()-&gt;saveRelations($fieldProductMediaField, $product, []);
      ...
  }
  </code></pre>
  
Answers.ContentLicense: CC BY-SA 4.0
Answers.CreationDate: 2022-03-02T08:19:14.470Z
Answers.Text: That's working. Really great, thanks!
Answers.UserDisplayName: bartpulse
Answers.ContentLicense: CC BY-SA 4.0
Answers.CreationDate: 2022-03-01T18:33:15.083Z
Answers.LastActivityDate: 2022-03-01T18:33:15.083Z
Answers.UserDisplayName: Mats Mikkel Rummelhoff
Body: <p>I have a large import with over 5000 products. These product elements have a image field wich is a assets relation field. Looping over the products and setting the field as null and then save takes waaaayyy too long:</p>
  <pre><code>    foreach($products as $product) {
          $counter++;
          $product-&gt;setFieldValue('fieldProductMedia', NULL);
  
          Craft::$app-&gt;getElements()-&gt;saveElement($product, false, false, false);
      
          $message = $counter .'/' . $total;
          Console::stdout($message);
          Console::stdout(PHP_EOL);
      }
  </code></pre>
  <p>I tried using a query like so:</p>
  <pre><code>DELETE r FROM `relations` AS r INNER JOIN elements AS e ON r.sourceId = e.id WHERE r.fieldId = 131 AND (e.type LIKE '%Product%' OR e.type LIKE '%Variant%');
  </code></pre>
  <p>But this messes with the foreign key constraint. So I tried it another way but this causes key constraints again when re-importing the images:</p>
  <pre><code>        $relations = (new Query())
              -&gt;select(['r.id'])
              -&gt;from('relations AS r')
              -&gt;innerJoin('elements AS e','r.sourceId = e.id')
              -&gt;where(['r.fieldId' =&gt; '131'])
              -&gt;andWhere(&quot;e.type LIKE '%Product%' OR e.type LIKE '%Variant%'&quot;)
              -&gt;indexBy('id')
              -&gt;all();
  
          $result = Craft::$app-&gt;db-&gt;createCommand()
              -&gt;delete(Table::RELATIONS, ['id' =&gt; array_keys($relations)])
              -&gt;execute();
  </code></pre>
  <p>Any ideas?</p>
  
Comments.ContentLicense: CC BY-SA 4.0
Comments.CreationDate: 2022-03-01T15:20:12.607Z
Comments.Text: Why does it matter that it takes some time to loop over the elements? Is it a one-time operation or does it need to happen regularly? Can it be done in the background?
Comments.UserDisplayName: MoritzLost
Comments.ContentLicense: CC BY-SA 4.0
Comments.CreationDate: 2022-03-01T15:35:18.303Z
Comments.Text: It's related to another fun issue I'm having with memory that keeps increasing after each save(). I think it's a bug in Craft:
  4454/5729 - 1800.85 mb
  Error: Allowed memory size of 2147483648 bytes exhausted (tried to allocate 135168 bytes)
Comments.UserDisplayName: bartpulse
Comments.ContentLicense: CC BY-SA 4.0
Comments.CreationDate: 2022-03-01T15:48:02.800Z
Comments.Text: Maybe your code is just badly optimized? Based on your example, it looks like you're loading 5000+ entries into one huge array and then iterating through it? No wonder you're having memory problems. Instead, try to load the entries one by one so that only one entry is in memory at a time. Will probably solve your problem here as well. You can use `$entryQuery->ids()` to get an array of entry IDs, then iterate through that and load each entry in turn.
Comments.UserDisplayName: MoritzLost
Comments.ContentLicense: CC BY-SA 4.0
Comments.CreationDate: 2022-03-01T15:48:50.643Z
Comments.Text: Also, if you think you're looking at a memory leak in Craft, try to track it down to its source and open an issue on Github. Better to address the source of the problem than to put a bandaid on it.
Comments.UserDisplayName: MoritzLost
Comments.ContentLicense: CC BY-SA 4.0
Comments.CreationDate: 2022-03-01T15:52:42.737Z
Comments.Text: There is also the [`$productQuery->each($n)`](https://www.yiiframework.com/doc/api/2.0/yii-db-query#each()-detail) method that will batch the DB operation and Element population into `$n` chunks to reduce the footprint of the whole operation. As long as you don’t memoize anything outside the loop, PHP will garbage collect for you!
Comments.UserDisplayName: August Miller
Comments.ContentLicense: CC BY-SA 4.0
Comments.CreationDate: 2022-03-01T16:58:31.743Z
Comments.Text: @MoritzLost
   Yeah I already contacted them using support. With the release of Craft 4 on the horizon they doubt they'll have time to look into this.
Comments.UserDisplayName: bartpulse
Comments.ContentLicense: CC BY-SA 4.0
Comments.CreationDate: 2022-03-01T16:59:51.170Z
Comments.Text: @AugustMiller
  The memory increase happens inside the ->saveElement() method. The array constructed outside the loop is not the problem.
Comments.UserDisplayName: bartpulse
Tags: <craft3>
Title: Fast way to delete relations without messing up foreign keys
ThreadText: # Fast way to delete relations without messing up foreign keys
  
  (Asked by: username_0 on 2022-03-01)
  
  I have a large import with over 5000 products. These product elements have a image field wich is a assets relation field. Looping over the products and setting the field as null and then save takes waaaayyy too long:
  
  ```
      foreach($products as $product) {
          $counter++;
          $product->setFieldValue('fieldProductMedia', NULL);
  
          Craft::$app->getElements()->saveElement($product, false, false, false);
      
          $message = $counter .'/' . $total;
          Console::stdout($message);
          Console::stdout(PHP_EOL);
      }
  
  ```
  
  I tried using a query like so:
  
  ```
  DELETE r FROM `relations` AS r INNER JOIN elements AS e ON r.sourceId = e.id WHERE r.fieldId = 131 AND (e.type LIKE '%Product%' OR e.type LIKE '%Variant%');
  
  ```
  
  But this messes with the foreign key constraint. So I tried it another way but this causes key constraints again when re-importing the images:
  
  ```
          $relations = (new Query())
              ->select(['r.id'])
              ->from('relations AS r')
              ->innerJoin('elements AS e','r.sourceId = e.id')
              ->where(['r.fieldId' => '131'])
              ->andWhere("e.type LIKE '%Product%' OR e.type LIKE '%Variant%'")
              ->indexBy('id')
              ->all();
  
          $result = Craft::$app->db->createCommand()
              ->delete(Table::RELATIONS, ['id' => array_keys($relations)])
              ->execute();
  
  ```
  
  Any ideas?
  
  **Tags:** craft3
  
  > **Comments:**
  > **username_1**: Why does it matter that it takes some time to loop over the elements? Is it a one-time operation or does it need to happen regularly? Can it be done in the background?  
  > **username_0**: It's related to another fun issue I'm having with memory that keeps increasing after each save(). I think it's a bug in Craft: 4454/5729 - 1800.85 mb Error: Allowed memory size of 2147483648 bytes exhausted (tried to allocate 135168 bytes)  
  > **username_1**: Maybe your code is just badly optimized? Based on your example, it looks like you're loading 5000+ entries into one huge array and then iterating through it? No wonder you're having memory problems. Instead, try to load the entries one by one so that only one entry is in memory at a time. Will probably solve your problem here as well. You can use `$entryQuery->ids()` to get an array of entry IDs, then iterate through that and load each entry in turn.  
  > **username_1**: Also, if you think you're looking at a memory leak in Craft, try to track it down to its source and open an issue on Github. Better to address the source of the problem than to put a bandaid on it.  
  > **username_2**: There is also the [`$productQuery->each($n)`](https://www.yiiframework.com/doc/api/2.0/yii-db-query#each()-detail) method that will batch the DB operation and Element population into `$n` chunks to reduce the footprint of the whole operation. As long as you don’t memoize anything outside the loop, PHP will garbage collect for you!  
  > **username_0**: @username_1  Yeah I already contacted them using support. With the release of Craft 4 on the horizon they doubt they'll have time to look into this.  
  > **username_0**: @username_2 The memory increase happens inside the ->saveElement() method. The array constructed outside the loop is not the problem.  
  
  ---
  
  ## 1 answers
  
  ### Answer by username_3 on 2022-03-01. Score: 2 | Accepted answer
  
  I haven't actually tested this, but going through the [Relations service](https://docs.craftcms.com/api/v3/craft-services-relations.html) instead of the Elements service might avoid the issues you're facing. Using the [`each()`](https://www.yiiframework.com/doc/api/2.0/yii-db-query#each()-detail) method when looping over the element query results (as mentioned by username_2 in the comments above) is also a good idea.
  
  ```php
  $fieldProductMediaField = Craft::$app->getFields()->getFieldByHandle('fieldProductMedia');
  
  foreach ($query->each() as $product) {
      Craft::$app->getRelations()->saveRelations($fieldProductMediaField, $product, []);
      ...
  }
  
  ```
  
  > **Comments:**
  > **username_0**: That's working. Really great, thanks!  
  
  ---
  
  