2016-04-21 5 views
1

Hier ist eine vereinfachte Version meiner E-Commerce-Schema:Überschneidung Abfrage auf Produkteigenschaften Tabelle

[products]: 
id 
Name 
Price 
URL 

[attributes]: 
id 
Description 
Value 

[products_attributes]: 
products_id 
attributes_id 

Es ist die typische many-to-many-Beziehung.

Jetzt muss ich für alle 'Red' und 'XXL' Produkte abfragen.

Offensichtlich, diese SQL-Abfrage holen alle 'Red' Produkte, auch solche ohne das 'XXL' Attribut.

Wie bekomme ich nur die Produkte mit den beiden Attributen?

Antwort

2

Sie müssen jedes einzeln überprüfen, was einer der vielen Gründe ist, dass das EAV-Modell normalerweise ein schlechtes Design hat. Sie können dies tun, indem zwei Unterabfragen für EXISTS tun:

SELECT 
    P.id, P.name, P.price, P.url -- Because we never use SELECT * except for EXISTS, COUNT(*), etc. 
FROM 
    Products P 
WHERE 
    EXISTS 
    (
     SELECT * 
     FROM 
      Products_Attributes PA_RED 
     INNER JOIN Attributes A_RED ON 
      A_RED.attribute_id = PA_RED.attribute_id AND 
      A_RED.description = 'Color' AND -- Have you thought about the possibility that "Red" might be a value for multiple attributes? 
      A_RED.value = 'Red' 
     WHERE 
      PA_RED.product_id = P.product_id 
    ) AND 
    EXISTS 
    (
     SELECT * 
     FROM 
      Products_Attributes PA_XXL 
     INNER JOIN Attributes A_XXL ON 
      A_XXL.attribute_id = PA_XXL.attribute_id AND 
      A_XXL.description = 'Size' AND 
      A_XXL.value = 'XXL' 
     WHERE 
      PA_XXL.product_id = P.product_id 
    ) 

Sie könnten auch JOIN zu jeder der Tabellen zweimal:

SELECT 
    P.id, P.name, P.price, P.url 
FROM 
    Products P 
INNER JOIN Products_Attributes PA_RED ON PA_RED.product_id = P.product_id 
INNER JOIN Attributes A_RED ON 
    A_RED.attribute_id = PA_RED.attribute_id AND 
    A_RED.description = 'Color' AND 
    A_RED.value = 'Red' 
INNER JOIN Products_Attributes PA_XXL ON PA_XXL.product_id = P.product_id 
INNER JOIN Attributes A_XXL ON 
    A_XXL.attribute_id = PA_XXL.attribute_id AND 
    A_XXL.description = 'Size' AND 
    A_XXL.value = 'XXL' 

Natürlich darüber nachdenken, was die Abfrage aussieht, wenn Sie wollen Überprüfen Sie 5 verschiedene Attribute ...

Ein anderer Ansatz wäre, die Anzahl der Übereinstimmungen als Aggregat zu überprüfen. Sie müssen sicher sein, dass dasselbe Attribut nicht zweimal auf das gleiche Produkt abgestimmt werden kann:

SELECT 
    P.id, P.name, P.price, P.url 
FROM 
    Products P 
INNER JOIN Products_Attributes PA ON PA.product_id = P.product_id 
INNER JOIN Attributes A ON 
    A.attribute_id = PA.attribute_id AND 
    (A.description = 'Color' AND A.value = 'Red') OR 
    (A.description = 'Size' AND A.value = 'XXL') 
GROUP BY 
    P.id, P.name, P.price, P.url 
HAVING 
    COUNT(*) = 2 
+0

Sie haben wirklich den Punkt Mann! Vielen Dank! Sie sagen, EAV könnte eine schlechte Design-Wahl sein. Kannst du mir zu diesem Zweck einen besseren vorschlagen? –

+0

Wenn Sie die Attribute Ihrer Produkte kennen, dann legen Sie sie in die Tabelle: Produkte würden: Farbe, Größe, etc. Wenn Ihre Produkte breit sind (Sie sind Amazon und Sie verkaufen Bücher, Kleidung, Elektronik, etc. alle mit eigenen Attributen), dann möchten Sie wahrscheinlich keine relationale Datenbank verwenden. Produkte wie MongoDB oder NoSQL sind möglicherweise besser geeignet. Es ist nicht mein Fachgebiet, also könnten andere bessere Vorschläge haben. –

+1

Eine andere Möglichkeit, wenn Sie unterschiedliche Produkte haben, aber eine kleine Anzahl von Kategorien, dann könnten Sie ein Modell mit einer Produkttabelle und dann 1: 1-Beziehungen zu Product_Electronics, Product_Clothes usw. verwenden, solange diese Subtypen jeweils ähnliche Attribute haben. –

1

Eine Methode besteht darin, zwei Joins zu verwenden. Allerdings ziehe ich Aggregation und group by:

SELECT p.* 
FROM products p INNER JOIN 
    products_attributes pa 
    ON pa.products_id = p.id INNER JOIN 
    attributes pa 
    ON pa.attributes_id = a.id 
WHERE a.Value IN ('Red','XXL') 
GROUP BY p.id 
HAVING COUNT(DISTINCT a.Value) = 2; 

Hinweis: Diese p.* in einer Aggregations Abfrage auswählen. Im Allgemeinen rate ich dringend davon ab. In diesem Fall erfolgt die Aggregation jedoch über den Primärschlüssel in der Tabelle products, sodass die Auswahl zusätzlicher Spalten aus dieser Tabelle sicher ist. In der Tat wird die Einbeziehung zusätzlicher Spalten von ANSI SQL sogar für solche Fälle unterstützt.