2014-10-31 79 views
1

我有一個表名員工,其中包含列:EMPLOYEE_IDemployee_manager_idemployee_manager_id參考employee_id。這是一個分層數據。PHP MySQL的層次數據優化

我有這個輸出使用PHP,但不能實現它只使用一個mySQL查詢。到目前爲止,我需要使用遞歸函數在PHP中處理數據,以便實現這種輸出。

Sample Array Output 
0 => (
       employee_id => 2, 
       name => Jerald, 
       employee_manager_id => 1, 
       depth => 1 
      ), 
     1 => (
       employee_id => 3, 
       name => Mark, 
       employee_manager_id => 2, 
       depth => 2 
      ), 
     2 => (
       employee_id => 6, 
       name => Cyrus, 
       employee_manager_id => 3, 
       depth => 3 
      ), 
     3 => (
       employee_id => 4, 
       name => Gerby, 
       employee_manager_id => 2, 
       depth => 2 
      ) 

截至目前,這是我在PHP中實現上述輸出的遞歸函數。

function get_employees_by_hierarchy($_employee_id = 0, $_depth = 0, $_org_array = array()) { 
    if ($this->org_depth < $_depth) { 
     $this->org_depth = $_depth; 
    } 

    $_depth++; 
    $_query = "SELECT * FROM employees WHERE "; 

    if (!$_employee_id) { 
     $_query .= "employee_manager_id IS NULL OR employee_manager_id = 0"; 
    } 
    else { 
     $_query .= "employee_manager_id = " . $this->dbh->quoteSmart($_employee_id); 
    } 
    $_result = $this->query($_query); 

    while ($_row = $_result->fetchRow()) { 
     $_row['depth'] = $_depth; 
     array_push($_org_array, $_row); 
     $_org_array = $this->get_employees_by_hierarchy(
      $_row['employee_id'], 
      $_depth, 
      $_org_array 
     ); 
    } 
    return $_org_array; 
} 

我的問題是,反正是有這樣我就可以只用一個MySQL查詢得到陣列輸出我想要什麼? 如果在MySQL查詢中不可能,那麼在我當前的代碼中是否有優化?

任何幫助將不勝感激。

感謝

+1

我可以爲此寫一個很好的答案,但有很多博客文章已經非常徹底地涵蓋了這個問題。看看http://mikehillyer.com/articles/managing-hierarchical-data-in-mysql/爲更常見的選項之一 – 2014-10-31 17:14:51

回答

0

您可以嘗試一組嵌套又名celko樹,但插入和刪除是非常昂貴的。還有關閉和路徑枚舉(物化路徑),但我不是專家。 MySql不支持遞歸查詢。

0

我不認爲你可以在不對結果進行任何處理的情況下獲得當前模型的深度,但不需要進行多個查詢。

假設$employeesemployee_id索引員工列表,你可以做這樣的事情:

function set_employee_depth(&$employees, $id) { 
    if (!isset($employees[$id]['depth'])) { 
     $employee_manager_id = (int) $employees[$id]['employee_manager_id']; 
     if (!$employee_manager_id) { 
      $employees[$id]['depth'] = 0; 
     } elseif ($employee_manager_id !== $id) { 
      $employees[$id]['depth'] = 1 + set_employee_depth($employees, $employee_manager_id); 
     } else { 
      throw new \Exception('Employee cannot be its own manager!'); 
     } 
    } 
    return $employees[$id]['depth']; 
} 

foreach ($employees as $id => $employee) { 
    set_employee_depth($employees, $id); 
} 
0

所以,你的表由3列(employee_idnameemployee_manager_id)的。 employee_manager_id是自我引用employee_id。您想要構建一個包含所有記錄的數組,並添加一個名爲depth的額外字段,該字段表示該員工與「大老闆」的距離,只有一個查詢到數據庫。那是對的嗎?我還假設數據庫結構不能改變。

如果這些假設是正確的,這是一個基本的分層/樹數據結構,因此,你有幾種方法可以解決這個問題。


首先腳本

第一個腳本運行結果排列順序,首先找到主節點/幹線(大老闆),然後將它的孩子,那麼它的孫子等等。每次節點被「分類」時,它將從週期中移除,直到沒有節點離開。它假定:

  • 沒有孤兒記錄(含無效managers_ids員工)
  • 沒有循環引用,無論是簡單的(A是B的經理,B是A經理)或複雜( * B的經理,A的C和C經理)的乙經理
  • 每個路徑(從主節點到最後的節點)可以具有節點
  • $results通過運行一個簡單的查詢SELECT * FROM employees ORDER BY employee_manager_id
  • 產生無限數量的

代碼:

$finalArray = array(); 
$limit = count($results); 

while (count($results) > 0) { 
    $results[0]['cnt'] = isset($results[0]['cnt']) ? $results[0]['cnt']++ : 0; // set num of times each element was already visited 
    if ($results[0]['cnt'] === $limit) { //prevent an infinite cycle 
     break; 
    } 
    $manId = $results[0]['manager_id']; 
    if ($manId === null) { 
     $results[0]['depth'] = 0; 
    } else if (($key = searchForId($manId, $finalArray)) !== null) { 
     $results[0]['depth'] = $finalArray[$key]['depth'] + 1; //use the depth of parent to calculate its own 
    } else { 
     $results[] = $results[0]; //parent was not visited yet so we add it to the end of array 
     array_shift($results); 
     continue; 
    } 
    unset($results[0]['cnt']); 
    $finalArray[] = array_shift($results); 
} 

function searchForId($id, $array) { 
    foreach ($array as $key => $val) { 
     if ($val['id'] === $id) { 
      return $key; 
     } 
    } 
    return null; 
} 

這個腳本是非常簡單的。它只對數據庫運行一個查詢。在最好的情況下,它只會遍歷數組一次。 在更糟糕的情況下,它將訪問每個元素count(array) - 1,這可能會導致數組較大。但是,由於結果是預先排序的,所以最好的情況可能會更常見。


第二個腳本

第二腳本構建元素的實際樹。這有點複雜,但取得了類似的結果。此外,深度是動態計算的。

class Employee { 
    public $id; 
    public $name; 
    public $manager; 

    public function __construct($id, $name, Employee $manager = null) { 
     $this->id = $id; 
     $this->name = $name; 
     $this->manager = $manager; 
    } 

    public function setManager(Employee $manager) { 
     $this->manager = $manager; 
    } 

    public function getDepth() { 
     if ($this->manager === null) { 
      return 0; 
     } else { 
      return $this->manager->getDepth() + 1; 
     } 
    } 
} 

$finalArray = array(); 
$paths = array(); 


foreach ($results as $r) { 
    $finalArray[(int) $r['id']] = new Employee((int)$r['id'], $r['name']); 
    if ($r['manager_id'] !== null) { 
     $paths[(int) $r['id']] = (int) $r['manager_id']; 
    } 
} 

foreach ($paths as $k => $v) { 
    if (isset($finalArray[$k]) && isset($finalArray[$v])) { 
     $finalArray[$k]->setManager($finalArray[$v]); 
    } 
} 
0

Here is a link for answer

下面是使用PHP和MySQL製作樹結構分層管理完整的代碼。

+0

歡迎來到StackOverflow!雖然這可能會提供一個問題的答案,但由於該鏈接將來可能不可用,因此不鼓勵鏈接回答。請考慮增加一點小結。謝謝。 – Aurasphere 2016-07-08 10:52:47