Link to home
Start Free TrialLog in
Avatar of Crazy Horse
Crazy HorseFlag for South Africa

asked on

Is using sql INSERT IGNORE bad for performance?

What I am trying to do is when a payment is made, the amount is inputted into an input field and then a loop runs through all the customer invoices and as long as each invoice amount is less than the paid amount, less the deducted invoice amount, it inserts that invoice amount into another table. The problem though is if say half an invoice is paid and the user still has an outstanding balance. To cater for this I did another check which checks if the user after payment has an outstanding balance or not. If not or if in credit, it should then pay off all outstanding invoices as the user has a zero balance and therefore should not owe anything.

To do this, I used INSERT IGNORE. It seems to work but I was concerned that it might be inefficient over time as it has to check more and more records?

           $stmt = $db->prepare("INSERT IGNORE INTO `payment_link` (`invoice_id`, `customer_id`, `amount`) VALUES (:invoice_id, :customer_id, :amount)");
            $stmt->bindParam(":invoice_id", $invoice_id, PDO::PARAM_INT);
            $stmt->bindParam(":customer_id", $this->customer_id);
            $stmt->bindParam(":amount", $amount, PDO::PARAM_STR);
            
            foreach ($invoices as $invoice) {
                if ($invoice->amount <= $this->amount) { 
                    $amount = $invoice->amount;
                    $invoice_id = $invoice->id;
                    $this->amount = $this->amount - $invoice->amount;
                    $stmt->execute();
                    
                } 
            }
            
            if($balance <= 0) {
                 $stmt = $db->prepare("INSERT IGNORE INTO `payment_link` (`invoice_id`, `customer_id`, `amount`) VALUES (:invoice_id, :customer_id, :amount)");
                 $stmt->bindParam(":invoice_id", $invoice_id, PDO::PARAM_INT);
                 $stmt->bindParam(":customer_id", $this->customer_id);
                 $stmt->bindParam(":amount", $amount, PDO::PARAM_STR);
                
                 foreach ($invoices as $invoice) {
                     $amount = $invoice->amount;
                     $invoice_id = $invoice->id;
                     $stmt->execute();
                 }
            }

Open in new window

Avatar of Nicolas Lecanu
Nicolas Lecanu
Flag of Guadeloupe image

Hi Black Sulfur,

Just one question, if balance  is > 0 what you want to do ?
Avatar of Crazy Horse

ASKER

I don't want anything to happen because their balance is > 0. Something should only happen once they make a payment ie: their balance should reduce and the invoices that have been paid off should reflect in another link table which holds the invoice id and customer id as well as the amount.

Or am I missing something vital here?
No, but INSERT IGNORE is to ignore error when you try to insert invalid data or the same data, you can see here an exemple.

So I don't think this will be inefficient over time, but you have to understand exaclty what INSERT IGNORE do in your code.
I am just trying to not enter duplicate entries that is why I am using INSERT IGNORE.

So, I made the invoice ID column unique in the database. If I did not use INSERT IGNORE it would give me the duplicate entry error and the application would break. I thought that INSERT IGNORE would be a good solution for this but was worried about performance but I am not sure if it works the way I think in that if there were 100 records to be inserted, it would loop over 100 records checking for duplicates and ignoring them and then only inserting a unique entry.
I undertsand, it's a solution.
But a clean solution will be before doing an insert, to do a select for those values.
If you don't get a record back then you know it is safe to insert.

You Just have to use two separate queries:
   -  one for check if the value you want to insert exist,
   - and after  if no record found, then the insert.
Good idea, but what is the difference in performance between using INSERT IGNORE and using a SELECT statement to check through 100's of records to see if they exist? Because, it's not just checking if one value exists, but multiple.
good question Sulfur, but I have no idea about that, cause its the same traitement,  INSERT IGNORE will check the 100's of records to found the error and the check also will check the 100's records.

I don't know the type of loop they used but  I can tell that the good point to check first if you have a same value, is that you will have 0 error in return.
Sorry not error, but 0 warning like in the exemple I send you.
Thanks for your comments, you have given me something to think about. The thing is, I don't need to show the user any error message if there were duplicates. For example, on a registration form where I need to check that the email is unique, I obviously would not user INSERT IGNORE because I need to give the user some feedback about the duplicate entry.

However in this case, I don't need to tell the user anything. I just need to insert new records without duplicating any.
ASKER CERTIFIED SOLUTION
Avatar of Nicolas Lecanu
Nicolas Lecanu
Flag of Guadeloupe image

Link to home
membership
This solution is only available to members.
To access this solution, you must be a member of Experts Exchange.
Start Free Trial
Thanks for that, I will try that also. But I have also just found after testing that your question about what happens when balance is > 0 is significant because there is a time when it matters.

Nothing is happening for example when the balance is 850 (2 x invoices of 500 and 350) and payments are made for 200, 150, 200.

Even though this is now 550 in payments, one invoice should move to the link table but it does not. I need to cater for this somehow.
ok, that an other problem you just found, but pls think that we don't read in your mind,
we don't really know what is your variables and when you say balance invoices etc... it's confusing

When you make codes it's important to put comment, it's an advice that I want to give you ^^