Commit cb4cd92595 for woocommerce
commit cb4cd925952517b343281ceaeedeb1f02376bf41
Author: Vladimir Reznichenko <kalessil@gmail.com>
Date: Tue Jan 13 12:20:41 2026 +0100
[Performance] Product attributes lookup table: speedup a slow delete-SQL (#62748)
Improved the performance of the product attributes lookup table synchronization (insert and delete operations).
One of the delete queries was performing a full table scan; we added a new index (confirmed the community proposal) and split it into two queries to further improve SQL performance.
diff --git a/plugins/woocommerce/changelog/performance-57045-product-attributes-lookup-indexes b/plugins/woocommerce/changelog/performance-57045-product-attributes-lookup-indexes
new file mode 100644
index 0000000000..70d7850896
--- /dev/null
+++ b/plugins/woocommerce/changelog/performance-57045-product-attributes-lookup-indexes
@@ -0,0 +1,4 @@
+Significance: patch
+Type: performance
+
+Improved the performance of the product attributes lookup table synchronization (insert and delete operations).
diff --git a/plugins/woocommerce/src/Internal/ProductAttributesLookup/DataRegenerator.php b/plugins/woocommerce/src/Internal/ProductAttributesLookup/DataRegenerator.php
index 94841a2927..c9d5af78b5 100644
--- a/plugins/woocommerce/src/Internal/ProductAttributesLookup/DataRegenerator.php
+++ b/plugins/woocommerce/src/Internal/ProductAttributesLookup/DataRegenerator.php
@@ -533,7 +533,8 @@ class DataRegenerator {
is_variation_attribute tinyint(1) NOT NULL,
in_stock tinyint(1) NOT NULL,
INDEX is_variation_attribute_term_id (is_variation_attribute, term_id),
- PRIMARY KEY ( `product_or_parent_id`, `term_id`, `product_id`, `taxonomy` )
+ PRIMARY KEY ( `product_or_parent_id`, `term_id`, `product_id`, `taxonomy` ),
+ KEY product_id (product_id)
) $collate;";
}
diff --git a/plugins/woocommerce/src/Internal/ProductAttributesLookup/LookupDataStore.php b/plugins/woocommerce/src/Internal/ProductAttributesLookup/LookupDataStore.php
index 897e9ba75e..66322ef8e9 100644
--- a/plugins/woocommerce/src/Internal/ProductAttributesLookup/LookupDataStore.php
+++ b/plugins/woocommerce/src/Internal/ProductAttributesLookup/LookupDataStore.php
@@ -265,15 +265,14 @@ class LookupDataStore {
$in_stock = $product->is_in_stock();
- // phpcs:disable WordPress.DB.PreparedSQL.NotPrepared
$wpdb->query(
$wpdb->prepare(
- 'UPDATE ' . $this->lookup_table_name . ' SET in_stock = %d WHERE product_id = %d',
+ 'UPDATE %i SET in_stock = %d WHERE product_id = %d',
+ $this->lookup_table_name,
$in_stock ? 1 : 0,
$product->get_id()
)
);
- // phpcs:enable WordPress.DB.PreparedSQL.NotPrepared
}
/**
@@ -359,15 +358,21 @@ class LookupDataStore {
private function delete_data_for( int $product_id ) {
global $wpdb;
- // phpcs:disable WordPress.DB.PreparedSQL.NotPrepared
+ // Single query handled with `index_merge` strategy, while separate with `range` (better performing) on available indexes.
$wpdb->query(
$wpdb->prepare(
- 'DELETE FROM ' . $this->lookup_table_name . ' WHERE product_id = %d OR product_or_parent_id = %d',
- $product_id,
+ 'DELETE FROM %i WHERE product_or_parent_id = %d',
+ $this->lookup_table_name,
+ $product_id
+ )
+ );
+ $wpdb->query(
+ $wpdb->prepare(
+ 'DELETE FROM %i WHERE product_id = %d',
+ $this->lookup_table_name,
$product_id
)
);
- // phpcs:enable WordPress.DB.PreparedSQL.NotPrepared
}
/**
@@ -601,20 +606,19 @@ class LookupDataStore {
/**
* Insert one entry in the lookup table.
*
- * @param int $product_id The product id.
- * @param int $product_or_parent_id The product id for non-variable products, the main/parent product id for variations.
- * @param string $taxonomy Taxonomy name.
- * @param int $term_id Term id.
+ * @param int $product_id The product id.
+ * @param int $product_or_parent_id The product id for non-variable products, the main/parent product id for variations.
+ * @param string $taxonomy Taxonomy name.
+ * @param int $term_id Term id.
* @param bool $is_variation_attribute True if the taxonomy corresponds to an attribute used to define variations.
- * @param bool $has_stock True if the product is in stock.
+ * @param bool $has_stock True if the product is in stock.
*/
private function insert_lookup_table_data( int $product_id, int $product_or_parent_id, string $taxonomy, int $term_id, bool $is_variation_attribute, bool $has_stock ) {
global $wpdb;
- // phpcs:disable WordPress.DB.PreparedSQL.NotPrepared
$wpdb->query(
$wpdb->prepare(
- 'INSERT INTO ' . $this->lookup_table_name . ' (
+ 'INSERT INTO %i (
product_id,
product_or_parent_id,
taxonomy,
@@ -623,6 +627,7 @@ class LookupDataStore {
in_stock)
VALUES
( %d, %d, %s, %d, %d, %d )',
+ $this->lookup_table_name,
$product_id,
$product_or_parent_id,
$taxonomy,
@@ -631,7 +636,6 @@ class LookupDataStore {
$has_stock ? 1 : 0
)
);
- // phpcs:enable WordPress.DB.PreparedSQL.NotPrepared
}
/**
@@ -843,13 +847,13 @@ class LookupDataStore {
private function create_data_for_product_cpt_core( int $product_id ) {
global $wpdb;
- // phpcs:disable WordPress.DB.PreparedSQL
- $sql = $wpdb->prepare(
- "delete from {$this->lookup_table_name} where product_or_parent_id=%d",
- $product_id
+ $wpdb->query(
+ $wpdb->prepare(
+ 'DELETE FROM %i WHERE product_or_parent_id = %d',
+ $this->lookup_table_name,
+ $product_id
+ )
);
- $wpdb->query( $sql );
- // phpcs:enable WordPress.DB.PreparedSQL
// * Obtain list of product variations, together with stock statuses; also get the product type.
// For a variation this will return just one entry, with type 'variation'.