Ik heb op dit moment voor een uitgebreide webshop 3 tabellen in een MySQL omgeving. Een productentabel, een categorieëntabel en een koppeltabel tussen beide. Zowel de productentabel als koppeltabel zijn beide gewoon standaardlayout; product_id en category_id als (gecombineerde)primary etc.
Categorieëntabel is ook redelijk standaard, met de toevoeging van parent_child relaties; die voor de query weinig uitmaken, maar voor de front-end wel handig zijn.
Een product kan in 1 of meerdere categoriëen vallen. Aan de hand van een script kan de gebruiker specificeren aan welke 'search' de getoonde producten moeten voldoen. Door de parent-child relaties tussen de categorieëen wordt een overzicht gemaakt van selecteerbare categorieëen. Vervolgens wordt er door een simpel script het aantal mogelijke (logische) permutaties berekend. Lees; de klant selecteert de (hoofd)categorieëen
• zuivel (200)
• frisdrank (300)
En, puur ter illustratie de subcategorieëen
• zoete dranken (1000)
• zure dranken (1001).
De mogelijke permutaties zijn dan deze:
[[200,1000],[200,1001],[300,1000],[300,1001]]
In dezelfde database staat een stored procedure, die aan de hand van een set categorienummers de producten ophaalt. (code hieronder). De aanroep is als volgt:
Hierop krijg ik dus distinct alle producten die horen in zowel category_id 200 als 1000 zitten. Met de 4 permutaties die hierboven genoemd zijn; wordt deze query dus 4 maal uitgevoerd; en via PHP gemerged tot een array met unieke product_id, die voldoen aan de filter die de gebruiker heeft gespecificeerd. Als ik het laatste nummer (2) aanpas naar 1, krijg ik alle producten die in 1 van de 2 categorieën zit. Dit als soort 'scoringssysteem'. Dit wordt echter (nog) niet gebruikt, en dit nummer is vooralsnog altijd gelijk aan het aantal elementen in de set.
Deze query, hoewel langzaam, is acceptabel qua tijden. Zodra ik echter extra gegevens ga joinen door de stored procedure (zie hieronder) aan te passen worden de querytijden onacceptabel hoog (lees 0,2 seconden of meer door simpelweg 'product_enabled' toe te voegen, en 0,8 seconden voor een SELECT *).
Zeker als je weet dat deze query in 1 overzichtsslag meerdere keren aangeroepen wordt, soms wel tot 30 maal per index, afhankelijk van de hoeveelheid mogelijke permutaties van de door de gebruiker geselecteerde categorieëen. (er wordt hierbij rekening gehouden met onmogelijke combinaties).
De query MOET geoptimaliseerd worden, aangezien er gesorteerd moet gaan worden op actieve voorraden en zelfs populariteit. Extra gegevens verkrijgen vanuit de stored procedure is érg aantrekkelijk aangezien er tot 30000 artikelen per query gereturned worden. Om deze naderhand in PHP nogmaals bij langs te gaan om te checken op voorraad, populariteit, whatever, is ondenkbaar traag.
Is er iemand die een idee heeft hoe ik dit beter aan kan pakken of iemand die de onderstaande query kan optimaliseren? Ik zie even door de bomen het bos niet meer.
Stored procedure getproductsbycategoryset
EDIT:
Het lijkt erop dat de volgende query precies hetzelfde doet, maar dan 3x sneller:
Dit versnelt het proces dan al met een behoorlijke factor; maar nogmaals; ik ben benieuwd naar andere aanpakken van dit probleem.
Categorieëntabel is ook redelijk standaard, met de toevoeging van parent_child relaties; die voor de query weinig uitmaken, maar voor de front-end wel handig zijn.
code:
1
2
3
4
5
6
7
8
| category_id | parent_id | <overig> ______________________________ 200 | 100 | <overig> 300 | 100 | <overig> ... | ... | <overig> 1000 | 511 | <overig> 1001 | 511 | <overig> 1002 | 511 | <overig> |
Een product kan in 1 of meerdere categoriëen vallen. Aan de hand van een script kan de gebruiker specificeren aan welke 'search' de getoonde producten moeten voldoen. Door de parent-child relaties tussen de categorieëen wordt een overzicht gemaakt van selecteerbare categorieëen. Vervolgens wordt er door een simpel script het aantal mogelijke (logische) permutaties berekend. Lees; de klant selecteert de (hoofd)categorieëen
• zuivel (200)
• frisdrank (300)
En, puur ter illustratie de subcategorieëen
• zoete dranken (1000)
• zure dranken (1001).
De mogelijke permutaties zijn dan deze:
[[200,1000],[200,1001],[300,1000],[300,1001]]
In dezelfde database staat een stored procedure, die aan de hand van een set categorienummers de producten ophaalt. (code hieronder). De aanroep is als volgt:
code:
1
| CALL GETPRODUCTSBYCATEGORYSET('200,1000', 2) |
Hierop krijg ik dus distinct alle producten die horen in zowel category_id 200 als 1000 zitten. Met de 4 permutaties die hierboven genoemd zijn; wordt deze query dus 4 maal uitgevoerd; en via PHP gemerged tot een array met unieke product_id, die voldoen aan de filter die de gebruiker heeft gespecificeerd. Als ik het laatste nummer (2) aanpas naar 1, krijg ik alle producten die in 1 van de 2 categorieën zit. Dit als soort 'scoringssysteem'. Dit wordt echter (nog) niet gebruikt, en dit nummer is vooralsnog altijd gelijk aan het aantal elementen in de set.
Deze query, hoewel langzaam, is acceptabel qua tijden. Zodra ik echter extra gegevens ga joinen door de stored procedure (zie hieronder) aan te passen worden de querytijden onacceptabel hoog (lees 0,2 seconden of meer door simpelweg 'product_enabled' toe te voegen, en 0,8 seconden voor een SELECT *).
Zeker als je weet dat deze query in 1 overzichtsslag meerdere keren aangeroepen wordt, soms wel tot 30 maal per index, afhankelijk van de hoeveelheid mogelijke permutaties van de door de gebruiker geselecteerde categorieëen. (er wordt hierbij rekening gehouden met onmogelijke combinaties).
De query MOET geoptimaliseerd worden, aangezien er gesorteerd moet gaan worden op actieve voorraden en zelfs populariteit. Extra gegevens verkrijgen vanuit de stored procedure is érg aantrekkelijk aangezien er tot 30000 artikelen per query gereturned worden. Om deze naderhand in PHP nogmaals bij langs te gaan om te checken op voorraad, populariteit, whatever, is ondenkbaar traag.
Is er iemand die een idee heeft hoe ik dit beter aan kan pakken of iemand die de onderstaande query kan optimaliseren? Ik zie even door de bomen het bos niet meer.
Stored procedure getproductsbycategoryset
code:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
| DELIMITER $$ CREATE DEFINER=`root`@`localhost` PROCEDURE `getproductsbycategoryset`( IN CATSET TEXT, IN NUM TINYINT(3) UNSIGNED ) BEGIN SELECT C.product_id FROM (SELECT product_id, category_id FROM product_category_mapping WHERE FIND_IN_SET(category_id, CATSET)) C GROUP BY C.product_id HAVING COUNT(C.product_id) >= NUM; END |
EDIT:
Het lijkt erop dat de volgende query precies hetzelfde doet, maar dan 3x sneller:
code:
1
| SELECT product_id FROM inf_product_category_mapping WHERE FIND_IN_SET(category_id, CATSET) GROUP BY product_id HAVING COUNT(product_id) >= NUM; |
Dit versnelt het proces dan al met een behoorlijke factor; maar nogmaals; ik ben benieuwd naar andere aanpakken van dit probleem.
[ Voor 4% gewijzigd door Scyth op 06-12-2011 14:12 . Reden: typo ]
Dell Studio XPS 16
Project: BavBierSub 1.0 BavBierSub 2.0