[{"data":1,"prerenderedAt":-1},["ShallowReactive",2],{"theme-db":3,"$f1nGPfUHhUmEUORASzphm_sPq24O5vgPETXuovgjQS1c":22,"megamenu":61,"$fKnz2vuX4bZz1LbUTiuFsvSZ3e07l5_5fqNYp4Tzdhi8":130,"$fI1jUsf_rSjI-iJtClhCW1HlkX52BVzmD6a1LbJjLiss":198,"footer-db":212,"header-db":230},{"theme":4},{"colors":5,"typography":13,"ui":17,"defaultColorMode":21},{"primary":6,"secondary":7,"background":8,"foreground":9,"muted":10,"headerBg":11,"footerBg":12,"topBarBg":9,"topBarText":11},"#4F46E5","#0D9488","#F9FAFB","#111827","#6B7280","#ffffff","#020617",{"fontFamily":14,"fontUrl":15,"baseFontSize":16},"Inter, system-ui, sans-serif","https:\u002F\u002Ffonts.googleapis.com\u002Fcss2?family=Inter:wght@400;500;600;700&family=Playfair+Display:ital,wght@0,400;0,700;0,800;0,900;1,400;1,700&display=swap","16px",{"borderRadius":18,"contentWidth":19,"shadow":20},"lg","7xl",true,"light",{"title":23,"slug":24,"metaDescription":25,"category":26,"tags":27,"difficulty":34,"psVersions":35,"content":39,"faq":40,"tldr":56,"readingTime":57,"generatedAt":58,"publishDate":58,"relatedArticles":59,"sourceCategory":60},"Exporter les EAN par produit avec les ventes mensuelles en SQL PrestaShop","exporter-ean-ventes-mensuelles-requete-sql-prestashop","Requête SQL pour extraire les codes EAN13 et les quantités vendues par mois depuis PrestaShop. Export optimisé pour l'analyse des ventes par déclinaison.","catalogue",[28,29,30,31,32,33],"sql","ean13","export","statistiques","déclinaisons","ventes","intermediaire",[36,37,38],"1.6","1.7","8.x","\u003Ch2>Pourquoi exporter les EAN avec les statistiques de ventes\u003C\u002Fh2>\n\u003Cp>Le code EAN13 (European Article Number) est l'identifiant universel de chaque produit ou déclinaison dans PrestaShop. Croiser ces codes avec les volumes de ventes mensuels est indispensable pour plusieurs cas d'usage concrets :\u003C\u002Fp>\n\u003Cul>\n\u003Cli>**Réapprovisionnement fournisseur** : envoyer les EAN des produits les plus vendus avec les quantités pour passer commande.\u003C\u002Fli>\n\u003Cli>**Analyse saisonnière** : identifier les pics de vente par référence et par mois.\u003C\u002Fli>\n\u003Cli>**Synchronisation marketplace** : Amazon, Cdiscount ou Google Shopping exigent des EAN valides — vérifier leur présence dans les commandes réelles permet de détecter les fiches incomplètes.\u003C\u002Fli>\n\u003Cli>**Reporting comptable** : ventiler le chiffre d'affaires par référence physique plutôt que par nom produit.\u003C\u002Fli>\n\u003C\u002Ful>\n\u003Ch2>La requête SQL complète\u003C\u002Fh2>\n\u003Cp>Voici la requête qui extrait, pour chaque produit vendu, le code EAN13 de sa déclinaison et le total des quantités commandées, ventilé par année et par mois :\u003C\u002Fp>\n\u003Cpre>\u003Ccode class=\"language-sql\">\nSELECT\n    YEAR(o.date_add)              AS annee,\n    MONTH(o.date_add)             AS mois,\n    od.product_name               AS nom_produit,\n    SUM(od.product_quantity)      AS total_vendu,\n    pa.ean13                      AS ean13_declinaison\nFROM ps_order_detail od\nLEFT JOIN ps_orders o\n    ON o.id_order = od.id_order\nLEFT JOIN ps_product_attribute pa\n    ON od.product_attribute_id = pa.id_product_attribute\nWHERE o.valid = 1\nGROUP BY\n    od.product_name,\n    pa.ean13,\n    YEAR(o.date_add),\n    MONTH(o.date_add)\nORDER BY\n    annee DESC,\n    mois DESC,\n    total_vendu DESC;\n\u003C\u002Fcode>\u003C\u002Fpre>\n\u003Cblockquote>\u003Cp>\u003Cstrong>Remarque :\u003C\u002Fstrong> Pensez à remplacer le préfixe \u003Ccode>ps_\u003C\u002Fcode> par celui de votre installation si vous l'avez personnalisé.\u003C\u002Fp>\u003C\u002Fblockquote>\n\u003Ch2>Décryptage de la requête\u003C\u002Fh2>\n\u003Ch3>Les tables impliquées\u003C\u002Fh3>\n\u003Ctr>\u003Cth>Table\u003C\u002Fth>\u003Cth>Rôle\u003C\u002Fth>\u003C\u002Ftr>\n\u003Ctr>\u003Cth>`ps_order_detail`\u003C\u002Fth>\u003Cth>Contient chaque ligne de commande : nom du produit, quantité, prix, et l'identifiant de la déclinaison commandée.\u003C\u002Fth>\u003C\u002Ftr>\n\u003Ctr>\u003Cth>`ps_orders`\u003C\u002Fth>\u003Cth>La commande parent : date, statut de validation, client.\u003C\u002Fth>\u003C\u002Ftr>\n\u003Ctr>\u003Cth>`ps_product_attribute`\u003C\u002Fth>\u003Cth>Les déclinaisons (combinaisons) du produit : c'est ici que PrestaShop stocke le code EAN13 par variante.\u003C\u002Fth>\u003C\u002Ftr>\n\u003Ch3>Pourquoi un LEFT JOIN sur `ps_product_attribute` ?\u003C\u002Fh3>\n\u003Cp>Tous les produits n'ont pas de déclinaison. Un produit simple (sans taille, couleur, etc.) aura \u003Ccode>product_attribute_id = 0\u003C\u002Fcode> dans \u003Ccode>ps_order_detail\u003C\u002Fcode>. Le \u003Ccode>LEFT JOIN\u003C\u002Fcode> garantit que ces produits apparaissent quand même dans les résultats, avec un EAN NULL.\u003C\u002Fp>\n\u003Cp>Pour récupérer l'EAN des produits simples (sans déclinaison), il faut ajouter une jointure sur \u003Ccode>ps_product\u003C\u002Fcode> :\u003C\u002Fp>\n\u003Cpre>\u003Ccode class=\"language-sql\">\nSELECT\n    YEAR(o.date_add)              AS annee,\n    MONTH(o.date_add)             AS mois,\n    od.product_name               AS nom_produit,\n    SUM(od.product_quantity)      AS total_vendu,\n    COALESCE(pa.ean13, p.ean13)   AS ean13\nFROM ps_order_detail od\nLEFT JOIN ps_orders o\n    ON o.id_order = od.id_order\nLEFT JOIN ps_product_attribute pa\n    ON od.product_attribute_id = pa.id_product_attribute\nLEFT JOIN ps_product p\n    ON od.product_id = p.id_product\nWHERE o.valid = 1\nGROUP BY\n    od.product_name,\n    COALESCE(pa.ean13, p.ean13),\n    YEAR(o.date_add),\n    MONTH(o.date_add)\nORDER BY\n    annee DESC,\n    mois DESC,\n    total_vendu DESC;\n\u003C\u002Fcode>\u003C\u002Fpre>\n\u003Cp>La fonction \u003Ccode>COALESCE\u003C\u002Fcode> prend le premier EAN non NULL : celui de la déclinaison en priorité, sinon celui du produit parent.\u003C\u002Fp>\n\u003Ch3>Le filtre `o.valid = 1`\u003C\u002Fh3>\n\u003Cp>Le champ \u003Ccode>valid\u003C\u002Fcode> de \u003Ccode>ps_orders\u003C\u002Fcode> vaut 1 uniquement pour les commandes considérées comme abouties (payées). Cela exclut automatiquement les paniers abandonnés, les commandes annulées ou en erreur de paiement. C'est le filtre standard pour toute analyse de ventes fiable.\u003C\u002Fp>\n\u003Ch3>Le piège du GROUP BY\u003C\u002Fh3>\n\u003Cp>En MySQL\u002FMariaDB en mode strict (\u003Ccode>ONLY_FULL_GROUP_BY\u003C\u002Fcode>, activé par défaut depuis MySQL 5.7 et MariaDB 10.2), chaque colonne du \u003Ccode>SELECT\u003C\u002Fcode> qui n'est pas dans une fonction d'agrégation (\u003Ccode>SUM\u003C\u002Fcode>, \u003Ccode>COUNT\u003C\u002Fcode>, etc.) \u003Cstrong>doit\u003C\u002Fstrong> figurer dans le \u003Ccode>GROUP BY\u003C\u002Fcode>. Si vous obtenez l'erreur :\u003C\u002Fp>\n\u003Cpre>\u003Ccode class=\"language-\">\nERROR 1055 (42000): 'ps_product_attribute.ean13' isn't in GROUP BY\n\u003C\u002Fcode>\u003C\u002Fpre>\n\u003Cp>…c'est que le champ \u003Ccode>ean13\u003C\u002Fcode> est sélectionné mais pas groupé. La solution : ajouter \u003Ccode>pa.ean13\u003C\u002Fcode> (ou \u003Ccode>COALESCE(pa.ean13, p.ean13)\u003C\u002Fcode>) dans la clause \u003Ccode>GROUP BY\u003C\u002Fcode>, comme dans les requêtes ci-dessus.\u003C\u002Fp>\n\u003Ch2>Variantes utiles\u003C\u002Fh2>\n\u003Ch3>Filtrer sur une période précise\u003C\u002Fh3>\n\u003Cpre>\u003Ccode class=\"language-sql\">\nWHERE o.valid = 1\n  AND o.date_add BETWEEN '2025-01-01' AND '2025-12-31'\n\u003C\u002Fcode>\u003C\u002Fpre>\n\u003Ch3>Exporter uniquement les produits sans EAN (audit qualité)\u003C\u002Fh3>\n\u003Cpre>\u003Ccode class=\"language-sql\">\nSELECT DISTINCT\n    od.product_id,\n    od.product_name\nFROM ps_order_detail od\nLEFT JOIN ps_product_attribute pa\n    ON od.product_attribute_id = pa.id_product_attribute\nLEFT JOIN ps_product p\n    ON od.product_id = p.id_product\nWHERE COALESCE(pa.ean13, p.ean13, '') = ''\nORDER BY od.product_name;\n\u003C\u002Fcode>\u003C\u002Fpre>\n\u003Cp>Cette requête identifie tous les produits qui ont été vendus mais dont l'EAN est manquant — indispensable avant une synchronisation marketplace.\u003C\u002Fp>\n\u003Ch3>Ajouter la référence produit\u003C\u002Fh3>\n\u003Cpre>\u003Ccode class=\"language-sql\">\nSELECT\n    YEAR(o.date_add)              AS annee,\n    MONTH(o.date_add)             AS mois,\n    od.product_name,\n    od.product_reference          AS reference,\n    SUM(od.product_quantity)      AS total_vendu,\n    COALESCE(pa.ean13, p.ean13)   AS ean13\nFROM ps_order_detail od\nLEFT JOIN ps_orders o\n    ON o.id_order = od.id_order\nLEFT JOIN ps_product_attribute pa\n    ON od.product_attribute_id = pa.id_product_attribute\nLEFT JOIN ps_product p\n    ON od.product_id = p.id_product\nWHERE o.valid = 1\nGROUP BY\n    od.product_name,\n    od.product_reference,\n    COALESCE(pa.ean13, p.ean13),\n    YEAR(o.date_add),\n    MONTH(o.date_add)\nORDER BY annee DESC, mois DESC, total_vendu DESC;\n\u003C\u002Fcode>\u003C\u002Fpre>\n\u003Ch2>Exécuter la requête\u003C\u002Fh2>\n\u003Ch3>Depuis le back-office PrestaShop\u003C\u002Fh3>\n\u003Cp>Allez dans \u003Cstrong>Paramètres avancés → Base de données → Requête SQL\u003C\u002Fstrong> et collez la requête. PrestaShop vous permet ensuite d'exporter le résultat en CSV.\u003C\u002Fp>\n\u003Ch3>En ligne de commande (recommandé pour les gros volumes)\u003C\u002Fh3>\n\u003Cpre>\u003Ccode class=\"language-bash\">\nmysql -u votre_user -p votre_base -e \"\n    SELECT YEAR(o.date_add) AS annee, MONTH(o.date_add) AS mois,\n           od.product_name, SUM(od.product_quantity) AS total,\n           COALESCE(pa.ean13, p.ean13) AS ean13\n    FROM ps_order_detail od\n    LEFT JOIN ps_orders o ON o.id_order = od.id_order\n    LEFT JOIN ps_product_attribute pa ON od.product_attribute_id = pa.id_product_attribute\n    LEFT JOIN ps_product p ON od.product_id = p.id_product\n    WHERE o.valid = 1\n    GROUP BY od.product_name, COALESCE(pa.ean13, p.ean13),\n             YEAR(o.date_add), MONTH(o.date_add)\n    ORDER BY annee DESC, mois DESC, total DESC\n\" &gt; export_ean_ventes.tsv\n\u003C\u002Fcode>\u003C\u002Fpre>\n\u003Cp>Le résultat sera un fichier TSV (tabulations) directement exploitable dans Excel ou Google Sheets.\u003C\u002Fp>\n\u003Ch2>Adaptation PrestaShop 8.x\u003C\u002Fh2>\n\u003Cp>La structure des tables \u003Ccode>ps_orders\u003C\u002Fcode>, \u003Ccode>ps_order_detail\u003C\u002Fcode>, \u003Ccode>ps_product\u003C\u002Fcode> et \u003Ccode>ps_product_attribute\u003C\u002Fcode> n'a pas changé entre PrestaShop 1.6 et 8.x. Ces requêtes fonctionnent donc sur toutes les versions.\u003C\u002Fp>\n\u003Cp>Le seul point d'attention concerne le mode SQL de MariaDB\u002FMySQL : PrestaShop 8.x exige MariaDB 10.5+ ou MySQL 8.0+, qui activent \u003Ccode>ONLY_FULL_GROUP_BY\u003C\u002Fcode> par défaut. Assurez-vous que toutes les colonnes non agrégées sont bien présentes dans le \u003Ccode>GROUP BY\u003C\u002Fcode>, comme dans les exemples fournis.\u003C\u002Fp>",[41,44,47,50,53],{"q":42,"a":43},"Comment exporter les codes EAN13 de tous mes produits PrestaShop en SQL ?","Pour exporter tous les EAN (déclinaisons incluses), utilisez : SELECT p.id_product, pl.name, COALESCE(pa.ean13, p.ean13) AS ean13 FROM ps_product p LEFT JOIN ps_product_lang pl ON p.id_product = pl.id_product AND pl.id_lang = 1 LEFT JOIN ps_product_attribute pa ON p.id_product = pa.id_product WHERE COALESCE(pa.ean13, p.ean13, '') != '' ORDER BY pl.name. Cela combine les EAN des produits simples et de leurs déclinaisons.",{"q":45,"a":46},"Pourquoi ma requête SQL PrestaShop affiche une erreur GROUP BY avec ONLY_FULL_GROUP_BY ?","Depuis MySQL 5.7 et MariaDB 10.2, le mode ONLY_FULL_GROUP_BY est activé par défaut. Chaque colonne du SELECT qui n'est pas dans une fonction d'agrégation (SUM, COUNT…) doit obligatoirement figurer dans le GROUP BY. Plutôt que de désactiver ce mode (ce qui masquerait des erreurs logiques), ajoutez simplement les colonnes manquantes dans votre clause GROUP BY.",{"q":48,"a":49},"Comment savoir quels produits PrestaShop n'ont pas de code EAN renseigné ?","Exécutez cette requête : SELECT p.id_product, pl.name FROM ps_product p LEFT JOIN ps_product_lang pl ON p.id_product = pl.id_product AND pl.id_lang = 1 WHERE (p.ean13 IS NULL OR p.ean13 = '') AND p.active = 1. Pour inclure les déclinaisons sans EAN, ajoutez un LEFT JOIN sur ps_product_attribute et filtrez avec COALESCE.",{"q":51,"a":52},"Le champ valid de ps_orders est-il fiable pour filtrer les commandes payées ?","Le champ valid est mis à jour par PrestaShop lorsqu'un état de commande marqué comme 'logable' est atteint (typiquement : paiement accepté). C'est le filtre standard utilisé par les statistiques internes de PrestaShop. Il est fiable pour exclure paniers abandonnés et commandes annulées, mais vérifiez la configuration de vos états de commande personnalisés dans le back-office (Commandes → États).",{"q":54,"a":55},"Comment exporter les ventes avec EAN directement en CSV depuis PrestaShop ?","Deux méthodes : via le back-office dans Paramètres avancés → Base de données → Requête SQL (bouton Exporter), ou en ligne de commande avec mysql -e '...' > fichier.tsv. Pour un vrai CSV avec séparateur point-virgule, ajoutez les options : mysql --batch --raw -e '...' | sed 's\u002F\\t\u002F;\u002Fg' > export.csv. La méthode CLI est recommandée pour les catalogues de plus de 10 000 références.","Requête SQL pour croiser les codes EAN13 des déclinaisons PrestaShop avec les volumes de ventes mensuels, en gérant le piège du GROUP BY strict et les produits sans déclinaison via COALESCE.",5,"2026-03-21T16:46:35.000Z",[],"Utilisation de PrestaShop : configuration et difficultés",{"items":62},[63,74,82,90,98,107,114,122],{"id":64,"type":65,"label":66,"href":68,"icon":69,"description":69,"badge":69,"groupTitle":69,"style":69,"gridColumns":69,"cssClass":69,"psCategoryId":69,"showPsChildren":70,"position":71,"children":72,"psChildren":73},41,"link",{"fr":67},"Expertise","\u002Fexpertise",null,false,0,[],[],{"id":75,"type":65,"label":76,"href":78,"icon":69,"description":69,"badge":69,"groupTitle":69,"style":69,"gridColumns":69,"cssClass":69,"psCategoryId":69,"showPsChildren":70,"position":79,"children":80,"psChildren":81},42,{"fr":77},"Blog","\u002Fblog",1,[],[],{"id":83,"type":65,"label":84,"href":86,"icon":69,"description":69,"badge":69,"groupTitle":69,"style":69,"gridColumns":69,"cssClass":69,"psCategoryId":69,"showPsChildren":70,"position":87,"children":88,"psChildren":89},43,{"fr":85},"Modules PrestaShop","\u002Fmodules",2,[],[],{"id":91,"type":65,"label":92,"href":94,"icon":69,"description":69,"badge":69,"groupTitle":69,"style":69,"gridColumns":69,"cssClass":69,"psCategoryId":69,"showPsChildren":70,"position":95,"children":96,"psChildren":97},44,{"fr":93},"Outils IA","\u002Foutils-ia",3,[],[],{"id":99,"type":65,"label":100,"href":102,"icon":69,"description":69,"badge":69,"groupTitle":69,"style":103,"gridColumns":69,"cssClass":69,"psCategoryId":69,"showPsChildren":70,"position":104,"children":105,"psChildren":106},45,{"fr":101},"Offre Starter ✨","\u002Foffre-starter",{"highlight":20},4,[],[],{"id":108,"type":65,"label":109,"href":111,"icon":69,"description":69,"badge":69,"groupTitle":69,"style":69,"gridColumns":69,"cssClass":69,"psCategoryId":69,"showPsChildren":70,"position":57,"children":112,"psChildren":113},46,{"fr":110},"Academy","\u002Facademy",[],[],{"id":115,"type":65,"label":116,"href":118,"icon":69,"description":69,"badge":69,"groupTitle":69,"style":69,"gridColumns":69,"cssClass":69,"psCategoryId":69,"showPsChildren":70,"position":119,"children":120,"psChildren":121},47,{"fr":117},"À propos","\u002Fa-propos",6,[],[],{"id":123,"type":65,"label":124,"href":126,"icon":69,"description":69,"badge":69,"groupTitle":69,"style":69,"gridColumns":69,"cssClass":69,"psCategoryId":69,"showPsChildren":70,"position":127,"children":128,"psChildren":129},48,{"fr":125},"Contact","\u002Fcontact",7,[],[],{"columns":131},[132,144,174,190],{"title":133,"links":134},"Plateforme",[135,137,140,141],{"label":136,"href":102,"external":70},"Offre Starter (2 500 €)",{"label":138,"href":139,"external":70},"Devenir Ambassadeur","\u002Fambassadeur",{"label":85,"href":86,"external":70},{"label":142,"href":143,"external":20},"CodeMyShop.com","https:\u002F\u002Fcodemyshop.com",{"title":145,"links":146},"Le Synedre",[147,150,153,156,159,162,165,168,171],{"label":148,"href":149,"external":70},"L'histoire","\u002Fsynedre",{"label":151,"href":152,"external":70},"Constitution","\u002Fsynedre\u002Fconstitution",{"label":154,"href":155,"external":70},"L'équipe","\u002Fequipe",{"label":157,"href":158,"external":70},"Le réacteur en direct","\u002Freacteur",{"label":160,"href":161,"external":70},"Le Drill (entraînement)","\u002Fdrill",{"label":163,"href":164,"external":70},"Protocole de réunion","\u002Fsynedre\u002Freunion",{"label":166,"href":167,"external":70},"Les agents IA","\u002Fagents-ia",{"label":169,"href":170,"external":70},"La Conduite","\u002Fsynedre\u002Fconduite",{"label":172,"href":173,"external":70},"Charte plateforme","\u002Fsynedre\u002Fcharte",{"title":175,"links":176},"Ressources",[177,178,179,182,184,187],{"label":77,"href":78,"external":70},{"label":110,"href":111,"external":70},{"label":180,"href":181,"external":70},"Dictionnaire","\u002Fdictionnaire",{"label":183,"href":68,"external":70},"Expertise PrestaShop",{"label":185,"href":186,"external":70},"Flywheel","\u002Fflywheel",{"label":188,"href":189,"external":70},"Manifeste","\u002Fmanifeste",{"title":117,"links":191},[192,194,197],{"label":193,"href":118,"external":70},"Alexandre Carette",{"label":195,"href":196,"external":70},"Dossier de presse","\u002Fpresse",{"label":125,"href":126,"external":70},{"academy":199,"blog":200,"expertise":211},[],[201,205,208],{"title":202,"url":203,"score":79,"type":204},"PrestaShop headless avec Nuxt 3 : pourquoi séparer back et front","\u002Fblog\u002Fprestashop\u002Farchitecture\u002Fprestashop-headless-nuxt-separation-front-back","blog",{"title":206,"url":207,"score":79,"type":204},"PrestaShop headless : Nuxt 3, pas Next.js — le choix souverain","\u002Fblog\u002Fprestashop\u002Farchitecture\u002Fprestashop-headless-nuxt-nextjs-souverainete",{"title":209,"url":210,"score":79,"type":204},"Sylius rachète PrestaShop : ce que ça change pour vous","\u002Fblog\u002Fprestashop\u002Farchitecture\u002Fsylius-rachat-prestashop-headless-souverainete",[],{"footer":213},{"theme":214,"description":69,"hours":69,"logo":215,"contact":218,"social":219,"bottomBar":229},"dark",{"src":216,"href":217,"alt":193},"\u002Flogo-ac.svg","\u002F",{"email":69,"phone":69,"address":69,"cta":69},[220,223,226],{"platform":221,"href":222,"label":221},"linkedin","https:\u002F\u002Fwww.linkedin.com\u002Fin\u002Falexandre-carette\u002F",{"platform":224,"href":225,"label":224},"malt","https:\u002F\u002Fwww.malt.fr\u002Fprofile\u002Falexandrecarette",{"platform":227,"href":228,"label":227},"github","https:\u002F\u002Fgithub.com\u002Fprest4cafe",{"copyright":69},{"header":231},{"logo":232,"topBar":235,"contactEmail":238,"features":239,"navBar":69},{"src":216,"alt":233,"text":193,"href":217,"class":234},"Alexandre Carette — Architecte E-commerce Souverain","h-10 w-10",{"message":69,"showLanguages":70,"align":236,"languages":237},"left",[],"contact@alexandrecarette.fr",{"showSearch":70,"showWishlist":70,"showLogin":20,"showContact":70,"showCart":70,"stickyHeader":20,"headerLayout":240},"inline"]