2016-04-21 90 views
1

這裏是我的電子商務架構的簡化版本:交叉路口查詢屬性表

[products]: 
id 
Name 
Price 
URL 

[attributes]: 
id 
Description 
Value 

[products_attributes]: 
products_id 
attributes_id 

這是典型的多對一對多的關係。

現在我需要查詢所有'Red'和'XXL'產品。

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'); 

很顯然,SQL查詢獲取所有'Red'產品,即使那些沒有'XXL'屬性的產品。

我怎樣才能得到具有這兩個屬性的產品?

回答

2

您需要分別檢查每一個,這是EAV模型通常是不好的設計的許多原因之一。你可以通過做兩個子查詢的EXISTS

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 
    ) 

你也可以JOIN到每個表的兩倍:

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' 

當然,想想這個查詢看起來就像當你想檢查5個不同的屬性...

另一種方法是檢查匹配計數作爲一個聚合。您需要確保相同的屬性不能與同一產品兩次匹配:

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

您確實有點人!謝謝! 你說EAV可能是一個糟糕的設計選擇。你能爲此建議我一個更好的,也許? –

+0

如果你知道你的產品的屬性,然後你把它們放在桌子上:產品會有:顏色,尺寸等。如果你的產品廣泛(你是亞馬遜,你賣書,衣服,電子產品等等與他們自己的屬性),那麼你可能不想使用關係數據庫。像MongoDB或NoSQL這樣的產品可能更合適。這不是我的專業領域,所以其他人可能會有更好的建議。 –

+1

另一種可能性是,如果您有不同的產品,但只有少量類別,則可以使用帶有產品表的模型,然後使用與Product_Electronics,Product_Clothes等1:1關係的模型,只要這些子類型具有相似的屬性即可。 –

1

一種方法是使用兩個連接。不過,我更喜歡聚集和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; 

注:這是一個集合的查詢選擇p.*。總的來說,我強烈建議不要這樣做。但是,在這種情況下,聚合由products表上的主鍵進行,因此從該表中選擇其他列是安全的。事實上,對於這種情況,甚至包含額外的列甚至可以被ANSI SQL支持。