[英]PHP MySQL hierarchy data optimization
我有一個表employee,其中包含列: employee_id , name , employee_manager_id 。 employee_manager_id引用employee_id 。 這是一個分層數據。
我有使用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查詢中使用,是否需要在當前代碼中進行優化?
任何幫助將不勝感激。
謝謝
您可以嘗試使用嵌套的集celko樹,但是插入和刪除非常昂貴。 也有閉包和路徑枚舉(物化路徑),但我不是專家。 MySql不支持遞歸查詢。
我認為您不對結果進行任何處理就無法深入了解當前模型,但是您無需進行多個查詢。
假設$employees
是由employee_id索引的employee_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);
}
因此,您的表由3列組成( employee_id
, name
, employee_manager_id
)。 employee_manager_id
是對employee_id
的自引用。 您想要構造一個包含所有記錄的數組,並添加一個名為depth
的額外字段,該字段表示該員工到“大老板”的距離,而對數據庫只有一個查詢。 那是對的嗎? 我還假設數據庫結構無法更改。
如果這些假設是正確的,則這是基本的HIERARCHICAL / TREE數據結構,因此,您可以采用兩種方法來解決此問題。
第一個腳本按順序運行結果數組,首先找到主節點/主干 (大老板),然后添加其子代,然后添加其子代,依此類推。 每次對節點進行“排序”時,都會將其從循環中刪除,直到沒有剩余節點為止。 它假定:
$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]);
}
}
這是用於使用php和mysql進行層次結構管理的樹結構的完整代碼。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.