Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Mysql Bulk update performance improvements for (when.. case)

I had a query like this, that should be executed after each MySQL actions. This query is now slowing down page loading because the data increased, I did everything like proper indexing, but the query is still slow relatively.

Is there any other way to perform those checks?

$query = "
UPDATE {$tprefix}wh_profg 
   SET status =
       CASE 
       WHEN 
          batchno in (
            select 
                batchno 
            from 
                {$tprefix}wh_profulldetail 
            where 
                remainingdays <= 0
          )
       THEN 
          'expired'

       WHEN 
          QC = 'rejected' and QA != 'rejected' 
            and status != 'expired' 
       THEN 
          'QC-rejected'

       WHEN 
          QA = 'rejected' and QC != 'rejected' 
            and status != 'expired' 
       THEN 
          'QA-rejected'

       WHEN 
          QA = 'rejected' and QC = 'rejected' 
            and status != 'expired' 
       THEN 
          'QA&QC-rejected'

       WHEN 
          (
            batchno in (
            select 
                batchno 
            from 
                {$tprefix}wh_profulldetail 
            where 
                available <= 0
            )
          ) and status != 'expired' 
            and status NOT LIKE '%rejected'
       THEN 
          'empty'

       WHEN 
          QC ='quarantine' and status != 'empty' 
          and status != 'expired' and 
          batchno in (
            select 
                batchno 
            from 
                {$tprefix}wh_profulldetail 
            where 
                available > 0 and remainingdays > 0
          )
       THEN 
          'quarantine'

       WHEN 
          QC ='approved' and QA = 'approved' 
          and status != 'empty' and status != 'expired' 
          and status NOT LIKE '%rejected' and 
          batchno in (
            select 
                batchno 
            from 
                {$tprefix}wh_profulldetail 
            where 
                available > 0 and remainingdays > 0
         )
       THEN 
          'available'
       ELSE
          'unknown'
       END
";
like image 842
Jason OOO Avatar asked Feb 13 '14 09:02

Jason OOO


People also ask

How to improve performance of update query in MySQL?

Another way to get fast updates is to delay updates and then do many updates in a row later. Performing multiple updates together is much quicker than doing one at a time if you lock the table. For a MyISAM table that uses dynamic row format, updating a row to a longer total length may split the row.


Video Answer


1 Answers

From the clarifications, I think you are saying that wh_profulldetail is a view that joins wh_profg and wh_profg_usage. Any inserts or updates to wh_profg_usage could affect wh_profg.status. Consequently, you will need INSERT and UPDATE triggers on both wh_profg and wh_profg_usage.

Once the triggers have been created, eliminate your original UPDATE query. The update will be performed by the triggers when needed. In addition, the update will only be performed on the one wh_profg row that is affected. This should eliminate your performance issues because your current update query is updating EVERY row in wh_profg.

The triggers would look something like:

DELIMITER //
CREATE TRIGGER insert_wh_profg_usage AFTER INSERT ON wh_profg_usage
   FOR EACH ROW
      UPDATE wh_profg a SET status = status WHERE batchno = NEW.batchno;
//

CREATE TRIGGER update_wh_profg_usage AFTER UPDATE ON wh_profg_usage
   FOR EACH ROW
      UPDATE wh_profg a SET status = status WHERE batchno = NEW.batchno;
//

CREATE TRIGGER update_wh_profg_usage AFTER DELETE ON wh_profg_usage
   FOR EACH ROW
      UPDATE wh_profg a SET status = status WHERE batchno = NEW.batchno;
//

CREATE TRIGGER insert_wh_profg BEFORE INSERT ON wh_profg
   FOR EACH ROW
   BEGIN

      SELECT 1
            ,remainingdays
            ,available
        INTO @detailfound
            ,@remainingdays
            ,@available
        FROM wh_profulldetail
       WHERE batchno = NEW.batchno;

      IF @remainingdays <= 0
      THEN SET NEW.status = 'expired';

      ELSEIF NEW.qc  = 'rejected'
         AND NEW.qa != 'rejected'
         AND new.status != 'expired'
        THEN SET NEW.status = 'QC-rejected';

      ELSEIF NEW.qa  = 'rejected'
         AND NEW.qc != 'rejected'
         AND new.status != 'expired'
        THEN SET NEW.status = 'QA-rejected';

      ELSEIF NEW.qa = 'rejected'
         AND NEW.qc = 'rejected'
         AND new.status != 'expired'
        THEN SET NEW.status = 'QA&QC-rejected';

      ELSEIF NEW.status != 'expired'
         AND NEW.status NOT LIKE '%rejected'
         AND @detailfound = 1
         AND @remainingdays <= 0
        THEN SET NEW.status = 'empty';

      ELSEIF NEW.qc ='quarantine'
         AND NEW.status != 'empty'
         AND NEW.status != 'expired'
         AND @detailfound = 1
         AND @available > 0
         AND @remainingdays > 0
        THEN SET NEW.status = 'quarantine';

      ELSEIF NEW.qc = 'approved'
         AND NEW.qa = 'approved'
         AND NEW.status != 'empty'
         AND NEW.status != 'expired'
         AND NEW.status NOT LIKE '%rejected'
         AND @detailfound = 1
         AND @available > 0
         AND @remainingdays > 0
        THEN SET NEW.status = 'available';

      ELSE SET NEW.status = 'unknown';

      END IF;

   END;
//

CREATE TRIGGER update_wh_profg BEFORE UPDATE ON wh_profg
   FOR EACH ROW
   BEGIN

      SELECT 1
            ,remainingdays
            ,available
        INTO @detailfound
            ,@remainingdays
            ,@available
        FROM wh_profulldetail
       WHERE batchno = NEW.batchno;

      IF @remainingdays <= 0
      THEN SET NEW.status = 'expired';

      ELSEIF NEW.qc  = 'rejected'
         AND NEW.qa != 'rejected'
         AND new.status != 'expired'
        THEN SET NEW.status = 'QC-rejected';

      ELSEIF NEW.qa  = 'rejected'
         AND NEW.qc != 'rejected'
         AND new.status != 'expired'
        THEN SET NEW.status = 'QA-rejected';

      ELSEIF NEW.qa = 'rejected'
         AND NEW.qc = 'rejected'
         AND new.status != 'expired'
        THEN SET NEW.status = 'QA&QC-rejected';

      ELSEIF NEW.status != 'expired'
         AND NEW.status NOT LIKE '%rejected'
         AND @detailfound = 1
         AND @remainingdays <= 0
        THEN SET NEW.status = 'empty';

      ELSEIF NEW.qc ='quarantine'
         AND NEW.status != 'empty'
         AND NEW.status != 'expired'
         AND @detailfound = 1
         AND @available > 0
         AND @remainingdays > 0
        THEN SET NEW.status = 'quarantine';

      ELSEIF NEW.qc = 'approved'
         AND NEW.qa = 'approved'
         AND NEW.status != 'empty'
         AND NEW.status != 'expired'
         AND NEW.status NOT LIKE '%rejected'
         AND @detailfound = 1
         AND @available > 0
         AND @remainingdays > 0
        THEN SET NEW.status = 'available';

      ELSE SET NEW.status = 'unknown';

      END IF;

   END;
//


delimiter ;

Without table structures or sample data or wh_profulldetail view, it's pretty tough to completely nail this.

like image 94
gwc Avatar answered Oct 06 '22 06:10

gwc