0

My SQL query updates all the stocks in database, but it is not working very efficiently and sometimes I get 504 timeout errors. The code works fine. How can I make it work better.

P.S: Please ignore lack of prepared statements, I'll add them later.

Some information about tables (Wordpress Woocommerce Plugin default tables):

wp_posts: This table includes posts. (Posts can be a product, or product variation. Example a product is butterfly t-shirt, a product variation is butterfly t-shirt red large).

wp_postmeta: This table includes meta information about posts. For example, if a product variation is instock, or what color it is, or what size it is.

  //This array gives, which products are there, and their respective categories.
  $allProducts = array("Fermuarlı Kapşonlu Sweatshirt" => "'2653','2659'","Kapşonlu Sweatshirt" => "'2646','2651'","Sweatshirt" => "'2644','2650'","Kadın Tişört" => "'2654','2656'","Atlet" => "'2655','2657'","Tişört" => "'2643','2304'");

  //Below arrays gives information about, which product variations are out of stock.
  $tisort_OutOfStock =array();
  $atlet_OutOfStock =array("all_colors"=>"'3xl','4xl','5xl'");
  $kadin_tisort_OutOfStock =array("all_colors"=>"'xxl','3xl','4xl','5xl'");
  $sweatshirt_OutOfStock =array("beyaz"=>"'xxl','3xl','4xl','5xl'","kirmizi"=>"'xxl','3xl','4xl','5xl'","bordo"=>"'5xl'","antrasit"=>"'5xl'");
  $kapsonlu_sweatshirt_OutOfStock =array("gri-kircilli"=>"'5xl'");
  $fermuarli_kapsonlu_sweatshirt_OutOfStock =array("gri-kircilli"=>"'5xl'","siyah"=>"'5xl'");

  //Reset stocks before updating.
  $resetStocks = "UPDATE wp_postmeta set meta_value = 'instock' where meta_key = '_stock_status'";
  $wpdb->query($resetStocks);
  echo "Stoklar are reseted<br>";

  //Foreach product, foreach color, update if product doesn't have stock.
  foreach( $allProducts as $key => $urun ){

    switch ($key) {
    case "Kadın Tişört": $tempArray = $kadin_tisort_OutOfStock; break;
    case "Fermuarlı Kapşonlu Sweatshirt": $tempArray = $fermuarli_kapsonlu_sweatshirt_OutOfStock; break;
    case "Kapşonlu Sweatshirt": $tempArray = $kapsonlu_sweatshirt_OutOfStock; break;
    case "Sweatshirt": $tempArray = $sweatshirt_OutOfStock; break;
    case "Atlet": $tempArray = $atlet_OutOfStock; break;
    case "Tişört": $tempArray = $tisort_OutOfStock; break;
    }

    foreach( $tempArray as $color => $size ){

      $query = "UPDATE wp_postmeta set meta_value = 'outofstock' where meta_key = '_stock_status' and post_id in
      (
      select post_id from (select * from wp_postmeta) AS X where meta_key = 'attribute_pa_beden' and meta_value in (".$size.")
      and post_id in (select post_id from (select * from wp_postmeta) AS Y where meta_key = 'attribute_pa_renk' and ((meta_value = '".$color."') OR ('".$color."' = 'all_colors')))
      and post_id in (select id from wp_posts where post_type = 'product_variation' and post_parent in (select object_id FROM wp_term_relationships where term_taxonomy_id in (".$urun.")))
      )";

      global $wpdb;
      $updatedRowCount = $wpdb->query($query);
    }
  }
HOY
  • 1,187
  • 9
  • 40
  • 74

2 Answers2

2

Start by working on the key SELECT:

        SELECT  post_id
            from  
            (
                SELECT  *
                    from  wp_postmeta
            ) AS X
            where  meta_key = 'attribute_pa_beden'
              and  meta_value in (".$size.")
              and  post_id in (
                SELECT  post_id
                    from  
                    (
                        SELECT  *
                            from  wp_postmeta
                    ) AS Y
                    where  meta_key = 'attribute_pa_renk'
                      and  ((meta_value = '".$color."')
                              OR  ('".$color."' = 'all_colors'))
                          )
              and  post_id in (
                SELECT  id
                    from  wp_posts
                    where  post_type = 'product_variation'
                      and  post_parent in (
                        SELECT  object_id
                            FROM  wp_term_relationships
                            where  term_taxonomy_id in (".$urun."))) 
                      )";

Yeah, I realize you need to "hide" wp_postmeta from the UPDATE wp_postmeta, but we can rearrange things to make it more efficient. Note how you have two cases of fetching the entire wp_postmeta before filtering? This makes it impossible to use any indexes, hence is sloooow.

SELECT m1.post_id
    FROM wp_postmeta AS m1
    JOIN wp_postmeta AS m2  USING(post_id)
    JOIN wp_posts    AS p2  USING(post_id)
    JOIN wp_term_relationships AS tr  ON p2.post_parent = tr.object_id
    WHERE m1.meta_key = 'attribute_pa_beden' AND   m1.meta_value in ("$size")
      AND m2.meta_key = 'attribute_pa_renk'  AND ( m1.meta_value = '$color'
                                                   OR '$color' = 'all_colors' )
      AND p2.post_type = 'product_variation'
      AND tr.term_taxonomy_id IN ($urun)

Forget about the UPDATE until you get this SELECT debugged. (I may have made some mistakes, but doesn't it look a lot simpler? It will run a lot faster, especially with the indexes I recommend.)

The OR with color will probably be optimized out, so I won't worry about that.

I can't predict which of the 4 tables the Optimizer will start with, so these indexes are needed to give it choices:

tr:  (term_taxonomy_id, object_id)  -- in this order
posts:  (post_type, post_id)        -- in this order
postmeta:  (meta_key, meta_value)   -- see note below

After Optimizer picks which table to start with, then it will go to each of the other tables in turn; the order does not matter to us. These additional indexes may be useful:

posts:   (post_parent, post_id)        -- in this order
postmeta:  (post_id, meta_key, meta_value)   -- see note below

If meta_value is LONGTEXT, then it cannot be in an index, so leave it out. (No, don't bother with a "prefix" index.)

If you are using MySQL 5.5 or 5.6, meta_key is too long to be indexes; see my link for multiple workarounds.

EAV schema sucks, and you are finding out why.

Back to the kludge for UPDATE, add the wrapper:

UPDATE wp_postmeta AS m
    JOIN  ( SELECT post_id
              FROM ( the above query )
          ) AS kludge  USING (post_id)
    SET   m.meta_value = 'outofstock'
    WHERE m.meta_key = '_stock_status'
Rick James
  • 106,233
  • 9
  • 103
  • 171
  • thank you, I've also found similar results and achieved a faster query. In addition to yours, I used `explain` statement in mysql, which helped me a lot to debug the query. Here I am posting the video which is about that. https://www.youtube.com/watch?v=ojyEcNMAj8k, I have one question. As you mentioned, I also leave UPDATE statement until last second, and after optimising the select, I put it into a subquery instead of a wrapper like yours. `UPDATE wp_postmeta set meta_value = 'outofstock' where meta_key = '_stock_status' and post_id in (my optimized select query)`. Is that a bad syntax? – HOY Dec 28 '18 at 10:53
  • 1
    @HOY - In some situations, `IN ( SELECT ... )` is _much_ slower because it reevaluates the `SELECT` for _every_ `post_id`. My wrapper should totally prevent that. What does `EXPLAIN` tell you for the two approaches? – Rick James Dec 28 '18 at 16:30
  • 1
    MariaDB 10.3.5 can handle `UPDATE` without the extra wrapper/IN -- see 'simultaneous_assignment'. – Rick James Dec 28 '18 at 16:58
1
        SELECT  post_id
            from  
            (
                SELECT  *
                    from  wp_postmeta
            ) AS X
            where  ...

-->

        SELECT post_id 
            FROM wp_postmeta
            WHERE ...

(The subquery only slows down things.)

              and  post_id in (
                SELECT  post_id

Instead of IN ( SELECT ... ), use a JOIN.

In addition to those tips, see my tips on improving the postmeta schema.

Rick James
  • 106,233
  • 9
  • 103
  • 171
  • Can't do your first suggestion because of UPDATE statement. Here the reaason: https://stackoverflow.com/a/14302701/1235655. I'll try the second one and see how it works. – HOY Dec 27 '18 at 23:26
  • @HOY - I think I have addressed that concern in my other answer. – Rick James Dec 28 '18 at 04:06