user23198123's avatar

How to optimize variant_value and attribute_product_value table in e-commerce database ?

By all things about e-commerce I find, I see the database like this:

Table "products" {
  "id" UUID [not null, default: `uuid_generate_v4()`]
  "slug" TEXT [unique, not null]
  "product_name" VARCHAR(255) [not null]
  "sku" VARCHAR(255)
  "sale_price" NUMERIC [not null, default: 0]
  "compare_price" NUMERIC [default: 0]
  "buying_price" NUMERIC [default: NULL]
  "quantity" INTEGER [not null, default: 0]
  "short_description" VARCHAR(165) [not null]
  "product_description" TEXT [not null]
  "product_type" VARCHAR(64)
  "published" BOOLEAN [default: FALSE]
  "disable_out_of_stock" BOOLEAN [default: TRUE]
  "note" TEXT
  "created_at" TIMESTAMPTZ [not null, default: `NOW()`]
  "updated_at" TIMESTAMPTZ [not null, default: `NOW()`]
  "created_by" UUID
  "updated_by" UUID

Indexes {
  id [pk]
}
}

Table "product_categories" {
  "id" UUID [not null, default: `uuid_generate_v4()`]
  "product_id" UUID [not null]
  "category_id" UUID [not null]

Indexes {
  id [pk]
}
}

Table "product_shipping_info" {
  "id" UUID [not null, default: `uuid_generate_v4()`]
  "product_id" UUID
  "weight" NUMERIC [not null, default: 0]
  "weight_unit" VARCHAR(10)
  "volume" NUMERIC [not null, default: 0]
  "volume_unit" VARCHAR(10)
  "dimension_width" NUMERIC [not null, default: 0]
  "dimension_height" NUMERIC [not null, default: 0]
  "dimension_depth" NUMERIC [not null, default: 0]
  "dimension_unit" VARCHAR(10)

Indexes {
  id [pk]
}
}

Table "attributes" {
  "id" UUID [not null, default: `uuid_generate_v4()`]
  "attribute_name" VARCHAR(255) [not null]
  "created_at" TIMESTAMPTZ [not null, default: `NOW()`]
  "updated_at" TIMESTAMPTZ [not null, default: `NOW()`]
  "created_by" UUID
  "updated_by" UUID

Indexes {
  id [pk]
}
}

Table "attribute_values" {
  "id" UUID [not null, default: `uuid_generate_v4()`]
  "attribute_id" UUID [not null]
  "attribute_value" VARCHAR(255) [not null]
  "value" JSON
Indexes {
  id [pk]
}
}

Table "product_attributes" {
  "id" UUID [not null, default: `uuid_generate_v4()`]
  "product_id" UUID [not null]
  "attribute_id" UUID [not null]

Indexes {
  id [pk]
}
}

Table "product_attribute_values" {
  "id" UUID [not null, default: `uuid_generate_v4()`]
  "product_attribute_id" UUID [not null]
  "attribute_value_id" UUID [not null]

Indexes {
  id [pk]
}
}

Table "variant_options" {
  "id" UUID [not null, default: `uuid_generate_v4()`]
  "title" TEXT [not null]
  "image_id" UUID
  "product_id" UUID [not null]
  "sale_price" NUMERIC [not null, default: 0]
  "compare_price" NUMERIC [default: 0]
  "buying_price" NUMERIC [default: NULL]
  "quantity" INTEGER [not null, default: 0]
  "sku" VARCHAR(255)
  "active" BOOLEAN [default: TRUE]

Indexes {
  id [pk]
}
}

Table "variants" {
  "id" UUID [not null, default: `uuid_generate_v4()`]
  "variant_option" TEXT [not null]
  "product_id" UUID [not null]
  "variant_option_id" UUID [not null]

Indexes {
  id [pk]
}
}

Table "variant_values" {
  "id" UUID [not null, default: `uuid_generate_v4()`]
  "variant_id" UUID [not null]
  "product_attribute_value_id" UUID [not null]

Indexes {
  id [pk]
}
}


Ref:"products"."id" < "product_categories"."product_id"

Ref:"products"."id" < "product_shipping_info"."product_id" [delete: set null]

Ref:"attributes"."id" < "attribute_values"."attribute_id"

Ref:"products"."id" < "product_attributes"."product_id"

Ref:"attributes"."id" < "product_attributes"."attribute_id"

Ref:"product_attributes"."id" < "product_attribute_values"."product_attribute_id"

Ref:"attribute_values"."id" < "product_attribute_values"."attribute_value_id"

Ref:"products"."id" < "variant_options"."product_id"

Ref:"products"."id" < "variants"."product_id"

Ref:"variant_options"."id" < "variants"."variant_option_id"

Ref:"variants"."id" < "variant_values"."variant_id"

Ref:"product_attribute_values"."id" < "variant_values"."product_attribute_value_id"

Ref: "products"."disable_out_of_stock" < "products"."published"

Ref: "variant_options"."id" < "variant_options"."active"

Ref: "product_attributes"."product_id" < "product_attributes"."id"

Ref: "products"."product_type" < "products"."product_name"

But my wonder why they are connect variant_values to product_attribute_values. I think that not necessary to discard the variants_values and product_attribute_values table because the variants table have already the products_id which means we can query by attribute and values either by that.

And if the table functions like that design, I actually will need to insert records to product_attributes_values and variant_values additionally whenever I need to add a product. I think it will be too complex

Do I need to optimize it or not?

0 likes
5 replies
kevinbui's avatar

Your thread looks answerable. But first, can you rewrite your thread with this markdown guide? so it is more comprehensive.

1 like
martinbean's avatar

@user23198123 You should have a pivot table between product attributes and variants, not product attributes and products. It’s the variants that hold values for product attributes, not products.

Think of a T-shirt. It has two attributes: Size and Colour. The T-shirt then has many variants that are unique combinations of those two attributes. So you would have a small red shirt, medium red shirt, small blue shirt, medium blue shirt, and so on. So it’s the individual variants you need to set values for attributes, not the parent product. There’s only one parent product. How are you going to set multiple values?

I blogged about this approach here: https://martinbean.dev/blog/2023/01/27/product-variants-laravel/

1 like
user23198123's avatar

@martinbean I thought the product_attributes and products table was linked is right? I meant my product could have the attribute which not actually variant like: A jean can have attribute - manufacturing date, material, pattern design, style, origin, seasons (autumn,summer,spring,winter) ..... , Baby products can have: Recommended age, Ingredients, Food type, Flavor, Production date, Expiry date , .... Or you meant all things I listed up there was variant too? Or should be I create a meta_attribute in product table which is JSON type?

user23198123's avatar

@martinbean By the way, if I follow as you said, is that I need to discard the product_attribute and product_attribute_values , next replace to variant_attribute and variant_attribute_values? And split out the sku properties into SKUs table independently? Thank u again for helping me ?

Please or to participate in this conversation.