Skip to content

Conversation

@indykoning
Copy link
Member

This PR adds support for a combined uniquePerStoreKey
Not every database table has a single field which is duplicate with multiple stores, but unique otherwise.

An example of what this could work with before is the catalog_product_entity_media_gallery_value
value_id is completely unique except for store_id where it can have the same value_id

An example where this wouldn't work before is the catalog_product_entity_varchar table.
Where an entry is unique per store only when attribute_id and entity_id are combined. Since an attribute can have values attached and a product can also have multiple values attached, but a product cannot have multiple values of the same attribute attached.

An example of it's usage in that case would be:

new ForCurrentStoreWithoutLimitScope(['entity_id', 'attribute_id'], 'store_id')

@indykoning indykoning requested a review from royduin as a code owner July 24, 2025 09:07
Jade-GG
Jade-GG previously approved these changes Jul 24, 2025
@indykoning
Copy link
Member Author

indykoning commented Jul 24, 2025

I've heavily optimised the query now, due to no longer using where not and concat it can take full advantage of the indexes. See the explain of a query heavily dependent on this feature:

id select_type table partitions type possible_keys key key_len ref rows filtered Extra
1 PRIMARY catalog_product_entity ALL 29730 100.00 Using where
14 DEPENDENT SUBQUERY catalog_product_entity_text ref CATALOG_PRODUCT_ENTITY_TEXT_ENTITY_ID_ATTRIBUTE_ID_STORE_ID,CATALOG_PRODUCT_ENTITY_TEXT_ATTRIBUTE_ID,CATALOG_PRODUCT_ENTITY_TEXT_STORE_ID CATALOG_PRODUCT_ENTITY_TEXT_ENTITY_ID_ATTRIBUTE_ID_STORE_ID 4 catalog_product_entity.entity_id 34 4.72 Using index condition; Using where
14 DEPENDENT SUBQUERY eav_attribute eq_ref PRIMARY PRIMARY 2 catalog_product_entity_text.attribute_id 1 10.00 Using where
16 DEPENDENT SUBQUERY comparison eq_ref CATALOG_PRODUCT_ENTITY_TEXT_ENTITY_ID_ATTRIBUTE_ID_STORE_ID,CATALOG_PRODUCT_ENTITY_TEXT_ATTRIBUTE_ID,CATALOG_PRODUCT_ENTITY_TEXT_STORE_ID CATALOG_PRODUCT_ENTITY_TEXT_ENTITY_ID_ATTRIBUTE_ID_STORE_ID 8 catalog_product_entity_text.entity_id,catalog_product_entity_text.attribute_id,const 1 100.00 Using index
11 DEPENDENT SUBQUERY catalog_product_entity_datetime ref CATALOG_PRODUCT_ENTITY_DATETIME_ENTITY_ID_ATTRIBUTE_ID_STORE_ID,CATALOG_PRODUCT_ENTITY_DATETIME_ATTRIBUTE_ID,CATALOG_PRODUCT_ENTITY_DATETIME_STORE_ID CATALOG_PRODUCT_ENTITY_DATETIME_ENTITY_ID_ATTRIBUTE_ID_STORE_ID 4 catalog_product_entity.entity_id 1 16.67 Using index condition; Using where
11 DEPENDENT SUBQUERY eav_attribute eq_ref PRIMARY PRIMARY 2 catalog_product_entity_datetime.attribute_id 1 10.00 Using where
13 DEPENDENT SUBQUERY comparison eq_ref CATALOG_PRODUCT_ENTITY_DATETIME_ENTITY_ID_ATTRIBUTE_ID_STORE_ID,CATALOG_PRODUCT_ENTITY_DATETIME_ATTRIBUTE_ID,CATALOG_PRODUCT_ENTITY_DATETIME_STORE_ID CATALOG_PRODUCT_ENTITY_DATETIME_ENTITY_ID_ATTRIBUTE_ID_STORE_ID 8 catalog_product_entity_datetime.entity_id,catalog_product_entity_datetime.attribute_id,const 1 100.00 Using index
8 DEPENDENT SUBQUERY catalog_product_entity_decimal ref CATALOG_PRODUCT_ENTITY_DECIMAL_ENTITY_ID_ATTRIBUTE_ID_STORE_ID,CATALOG_PRODUCT_ENTITY_DECIMAL_STORE_ID,CATALOG_PRODUCT_ENTITY_DECIMAL_ATTRIBUTE_ID CATALOG_PRODUCT_ENTITY_DECIMAL_ENTITY_ID_ATTRIBUTE_ID_STORE_ID 4 catalog_product_entity.entity_id 19 6.15 Using index condition; Using where
8 DEPENDENT SUBQUERY eav_attribute eq_ref PRIMARY PRIMARY 2 catalog_product_entity_decimal.attribute_id 1 10.00 Using where
10 DEPENDENT SUBQUERY comparison eq_ref CATALOG_PRODUCT_ENTITY_DECIMAL_ENTITY_ID_ATTRIBUTE_ID_STORE_ID,CATALOG_PRODUCT_ENTITY_DECIMAL_STORE_ID,CATALOG_PRODUCT_ENTITY_DECIMAL_ATTRIBUTE_ID CATALOG_PRODUCT_ENTITY_DECIMAL_ENTITY_ID_ATTRIBUTE_ID_STORE_ID 8 catalog_product_entity_decimal.entity_id,catalog_product_entity_decimal.attribute_id,const 1 100.00 Using index
5 DEPENDENT SUBQUERY catalog_product_entity_int ref CATALOG_PRODUCT_ENTITY_INT_ENTITY_ID_ATTRIBUTE_ID_STORE_ID,CATALOG_PRODUCT_ENTITY_INT_ATTRIBUTE_ID,CATALOG_PRODUCT_ENTITY_INT_STORE_ID,CATALOG_PRODUCT_ENTITY_INT_ATTRIBUTE_ID_STORE_ID_VALUE CATALOG_PRODUCT_ENTITY_INT_ENTITY_ID_ATTRIBUTE_ID_STORE_ID 4 catalog_product_entity.entity_id 25 6.86 Using index condition; Using where
5 DEPENDENT SUBQUERY eav_attribute eq_ref PRIMARY PRIMARY 2 catalog_product_entity_int.attribute_id 1 10.00 Using where
7 DEPENDENT SUBQUERY comparison eq_ref CATALOG_PRODUCT_ENTITY_INT_ENTITY_ID_ATTRIBUTE_ID_STORE_ID,CATALOG_PRODUCT_ENTITY_INT_ATTRIBUTE_ID,CATALOG_PRODUCT_ENTITY_INT_STORE_ID,CATALOG_PRODUCT_ENTITY_INT_ATTRIBUTE_ID_STORE_ID_VALUE CATALOG_PRODUCT_ENTITY_INT_ENTITY_ID_ATTRIBUTE_ID_STORE_ID 8 catalog_product_entity_int.entity_id,catalog_product_entity_int.attribute_id,const 1 100.00 Using index
2 DEPENDENT SUBQUERY catalog_product_entity_varchar ref CATALOG_PRODUCT_ENTITY_VARCHAR_ENTITY_ID_ATTRIBUTE_ID_STORE_ID,CATALOG_PRODUCT_ENTITY_VARCHAR_ATTRIBUTE_ID,CATALOG_PRODUCT_ENTITY_VARCHAR_STORE_ID CATALOG_PRODUCT_ENTITY_VARCHAR_ENTITY_ID_ATTRIBUTE_ID_STORE_ID 4 catalog_product_entity.entity_id 43 6.83 Using index condition; Using where
2 DEPENDENT SUBQUERY eav_attribute eq_ref PRIMARY PRIMARY 2 catalog_product_entity_varchar.attribute_id 1 10.00 Using where
4 DEPENDENT SUBQUERY comparison eq_ref CATALOG_PRODUCT_ENTITY_VARCHAR_ENTITY_ID_ATTRIBUTE_ID_STORE_ID,CATALOG_PRODUCT_ENTITY_VARCHAR_ATTRIBUTE_ID,CATALOG_PRODUCT_ENTITY_VARCHAR_STORE_ID CATALOG_PRODUCT_ENTITY_VARCHAR_ENTITY_ID_ATTRIBUTE_ID_STORE_ID 8 catalog_product_entity_varchar.entity_id,catalog_product_entity_varchar.attribute_id,const 1 100.00 Using index

As you can see every subquery uses the index.
With a limit of 1, a query with this many tables to check takes 3ms
(with a dataset of 29k products, and in total 3.6mln product_entity_xxx records)

@royduin
Copy link
Member

royduin commented Jul 25, 2025

Do we need some unit tests for this so we're sure the results are the same?

@royduin royduin marked this pull request as draft July 29, 2025 12:38
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants