PrestaShop6 min

PrestaShop database schema: the key tables explained

Par Pierre-Arthur Demengel
PrestaShopDatabaseMigrationData
Partager

The PrestaShop database schema has no table by table reference in the official documentation I read: the developer docs explain the conventions and point to the db_structure.sql file, which holds almost all tables. For products, combinations, orders and customers, the tables to read are product, product_attribute, orders, order_detail, customer and address, plus their _lang and _shop companions.

I checked the table names below in the file on GitHub, on the development branch, on 1 November 2026. Your version may differ, so run SHOW CREATE TABLE on your own database before relying on any column. My interest: I work on Sylius migrations, and most people reading this are planning to move data somewhere.

What the official PrestaShop documentation says

The PrestaShop 9 database page sets out three rules worth knowing. Names are in snake case. Tables ending in _lang hold translations, while those ending in _shop hold per-store records. And since PrestaShop 9, the prefix is randomised by default instead of ps_, so copied queries can fail on a new installation. The file itself uses the placeholder PREFIX_, which is what I have left out in the names here.

The structure page contains an important caveat: the structure file covers "almost all tables" and some, such as those for stocks, are handled through Doctrine entities in src/PrestaShopBundle/Entity/. It is also used once, at installation. Later changes are applied by versioned SQL files in the autoupgrade module. A shop that has been upgraded for years, and whose modules added more, will not match the file exactly.

Products and combinations

A combination is PrestaShop's word for a sellable version of a product, such as a red shirt in size M. This is the part of the schema that most often trips up newcomers, because the price lives in several places.

TableHoldsUseful columns
productBase productreference, price, id_category_default, active
product_langTranslated textname, description, link_rewrite, id_lang, id_shop
product_shopPer-store valuesprice, id_tax_rules_group, on_sale
product_attributeCombinationid_product, reference, price, weight, default_on
product_attribute_combinationLinks combination to valuesid_attribute, id_product_attribute
attribute_group, attributeSize, colour and their valuesgroup_type, color, position
stock_availableStockquantity, physical_quantity, reserved_quantity
specific_priceDiscounted or targeted pricesprice, reduction, from, to, id_group

Three points deserve care. On a multistore shop, prefer the values in the _shop tables, because they carry what each store really uses. The price on a combination is generally understood as an adjustment relative to the product and not as a final price, so test it against what your front office displays. And stock rows carry both id_product and id_product_attribute, so a product with combinations has several rows to reconcile.

Orders: amounts frozen at the time of sale

The orders table stores the reference, the customer, the delivery and invoice addresses, the current_state, and totals such as total_paid_tax_incl. The lines sit in order_detail, which repeats product_name, product_reference and the prices. That duplication is deliberate and valuable: an order must keep showing what was sold even if the product has since changed or vanished. So read your order lines from order_detail, never by joining to the current catalog.

The rest of the picture is in order_history (status changes with dates), order_invoice (including invoice number and totals), order_payment (linked through order_reference, with amount and transaction_id), and order_slip for credit notes. If you need to prove accounting continuity, these are the tables your accountant will want looked after.

Customers, addresses and passwords

The customer table holds email, firstname, lastname, passwd, newsletter, optin and two flags that are easy to overlook: is_guest and deleted. Filter them consciously. Migrating soft-deleted accounts may conflict with a deletion a customer has asked for, and the marketing consent columns should follow the person, not be assumed. Addresses are in address, which also carries id_manufacturer and id_supplier, so not every row is a customer's. It has its own deleted flag too, and old orders may still refer to deleted addresses.

For passwords, the PrestaShop hashing class on the development branch handles both bcrypt and a salted MD5. If your destination is Symfony-based, you do not necessarily need a mass password reset: the Symfony password documentation shows how to keep a legacy hasher and rehash at next login. Whether that works depends on the formats found in your own data, and the salt must be handled as a secret.

When reading the schema is the wrong approach

If you only need to move a modest catalog, the admin exports or an established migration tool may be enough, and a PrestaShop specialist will know the quirks of your version better than I do. If you need to sync constantly with another system, use the webservice and not direct access to tables, as the data model belongs to the application. Direct reading makes the most sense for one-time extractions on a copy of the database.

If the destination is Sylius, the next reading is my piece on importing products, variants and orders. To go further with your own case, send your version and catalog size through my Sylius developer page. I reply within 24 working hours. I can also take a look at your existing e-commerce project if you are still weighing up whether to move.

Questions fréquentes

Partager
13 projets livrésGrand-Est & BelgiqueLighthouse >90Disponible immédiatement

Un projet en tête ?

Discutons de votre site web. Réponse garantie sous 24h.

Ou appelez directement :06 95 41 30 25