簡體   English   中英

PHP MySQL層次結構數據優化

[英]PHP MySQL hierarchy data optimization

我有一個表employee,其中包含列: employee_idnameemployee_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_idnameemployee_manager_id )。 employee_manager_id是對employee_id的自引用。 您想要構造一個包含所有記錄的數組,並添加一個名為depth的額外字段,該字段表示該員工到“大老板”的距離,而對數據庫只有一個查詢。 那是對的嗎? 我還假設數據庫結構無法更改。

如果這些假設是正確的,則這是基本的HIERARCHICAL / TREE數據結構,因此,您可以采用兩種方法來解決此問題。


第一個劇本

第一個腳本按順序運行結果數組,首先找到主節點/主干 (大老板),然后添加其子代,然后添加其子代,依此類推。 每次對節點進行“排序”時,都會將其從循環中刪除,直到沒有剩余節點為止。 它假定:

  • 沒有孤兒記錄(具有無效manager_ids的員工)
  • 沒有循環引用,無論是簡單的( A是B的經理,B是A的經理 )還是復雜的(* B的A經理,C的B經理和A的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]);
    }
}

這是答案的鏈接

這是用於使用php和mysql進行層次結構管理的樹結構的完整代碼。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM