2012-09-10 23 views
0

我已經構建了一些產品數據庫,但我從不真正滿意數據庫的效率。我的最好的辦法仍然是有一個產品表,一個類別表,然後一個關係表,即:最高效的分層產品表

產品表:

id product_name  data 
1  Some prod  More info... 
2  Another prod  Even more... 

分類表:

id parent_id cat_name 
1  0   Main cat 1 
2  1   Sub cat 1 
3  1   Sub cat 2 
4  3   Sub sub cat 1 
5  0   Main cat 2 

關係表:

id prod_id cat_id 
1  1   2 
2  1   4 
3  2   5 
etcetera... 

這使得它很容易檢索產品,也很容易擁有e產品分配給多個類別。

但是,創建類別列表的結構並不像我想的那麼簡單。首先,我需要循環主要類別,然後相應地調用所有子級。我只想顯示已分配產品的類別,但當然如果包含產品,我還需要顯示所有父類別。這導致大量的查詢,連接和條件例程來呈現當前類別。我想知道是否可能有更有效的結構方法解決這個問題?你不需要寫我的代碼,我只是想知道可能有什麼更好的原則?

+1

MySQL不支持遞歸函數,所以它不太適合存儲分層數據的鄰接列表模型。您應該考慮重構您的數據以使用嵌套或封閉表。請參閱[本答案](http://stackoverflow.com/a/192462/623041)瞭解更多信息。 – eggyal

+0

@eggyal作爲旁註不是支持遞歸函數,但是具有固定的遞歸深度(默認設置爲0)? –

+0

或者您可以使用支持'WITH'和分層結構的數據庫平臺。 – podiluska

回答

2

典型的遞歸方法是鄰接表和多對多表。

典型的非遞歸方法是多對多的祖先表和nested sets

Adjancency lists是那些包含「parent_id」引用的結構。

遞歸地讀取多對多表是您的方法。

祖先表是多對多的表,但它們也包含子祖父連接並指定每個連接的級別。它們具有最大的靈活性和最快的讀寫速度。

嵌套集是一種非常不同的方法,它們只允許嚴格的樹結構而不是圖。它們的寫入成本更高,但讀取起來非常容易。

關於嵌套集,手動維護結構非常困難。而且你需要實現幾個函數並選擇每個函數的使用時間,因爲在子列表末尾插入一個節點需要一個函數(appendNodeTo($parentNode)),而在子列表中間插入一個節點需要另一個函數。移動節點取決於天氣節點是終端節點(一片葉子)還是具有子節點(一個分支)以及具有特定功能的節點。

+0

我是否正確地認識到遞歸方法在從數據庫中檢索時非常高效,但在插入時速度較慢? (這是相當不錯的...) – jtheman

+1

我已經更新了一些規範的答案。是的,這個結構使得更新/插入變得困難,它需要你在樹中重新連線一半的祖先節點來進行一次更新。 –

+0

謝謝。我正在考慮嵌套集合方法,因爲它很容易讓數據變得容易。但是,正如你所建議的,缺點當然是我需要編寫一個自動例程來在類別列表中插入/更新項目。當我爲此構建CMS時,它當然是需要的。 – jtheman

1

這不是最有效的方式,但這種方式,你只需要1個SQL請求(查詢)

public function get_menu_data() 
    { 
     $result = mysql_query(" 
      SELECT 
       id, parent, name 
      FROM 
       category 
      ORDER BY 
       parent, name 
     "); 
     //$cat = $this->db->get("category"); 
     $menuData = array( 
      'items' => array(), 
      'parents' => array() 
     ); 

     while ($menuItem = mysql_fetch_assoc($result)) 
     { 
      $menuData['items'][$menuItem['id']] = $menuItem; 
      $menuData['parents'][$menuItem['parent']][] = $menuItem['id']; 
     } 
     return $menuData; 

    } 

function buildMenu($parentId, $menuData) 
{ 
    $html = ''; 
    if (isset($menuData['parents'][$parentId])) 
    { 
     $html = '<ul>'; 
     foreach ($menuData['parents'][$parentId] as $itemId) 
     { 
      $html .= '<li>' . $menuData['items'][$itemId]['name']; 
      // find childitems recursively 
      $html .= $this -> buildMenu($itemId, $menuData); 
      $html .= '</li>'; 
     } 
     $html .= '</ul>'; 
    } 
    return $html; 
} 

這樣稱呼它: 顯示所有類別:

buildMenu(0,get_menu_data()); 

的顯示子類別分類1:

buildMenu(1,get_menu_data()); 

祝你好運,我希望這段代碼可以幫助你

+0

謝謝。 Hovewer我還需要查詢數據庫以檢查產品是否存在於每個類別中,並僅根據每個類別或其子級別是否包含產品來顯示它。 – jtheman