Оптимизация данных иерархии PHP MySQL

У меня есть таблица сотрудников, которая содержит столбцы: employee_id, название, 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, можно ли оптимизировать мой текущий код?

Любая помощь будет принята с благодарностью.

Спасибо

1

Решение

Вы можете попробовать вложенный набор a.k.a. celko tree, но вставить и удалить это очень дорого. Есть также замыкания и перечисление пути (материализованный путь), но я не эксперт. MySql не поддерживает рекурсивные запросы.

0

Другие решения

Я не думаю, что вы можете получить всю глубину с вашей текущей моделью без какой-либо обработки результатов, но вам не нужно делать несколько запросов.

Если предположить, $employees список сотрудников, проиндексированный 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);
}
0

Итак, ваша таблица состоит из 3 столбцов (employee_id, name, employee_manager_id). employee_manager_id это самостоятельная ссылка на employee_id, Вы хотите построить массив со всеми записями, добавив дополнительное поле с именем depth который представляет расстояние указанного сотрудника до «большого босса», с одним запросом к базе данных. Это верно? Я также предполагаю, что структура базы данных не может быть изменена.

Если эти предположения верны, это основной ИЕРАРХИЧЕСКАЯ / ДЕРЕВО структура данных и, таким образом, у вас есть несколько способов решения этой проблемы.


Первый скрипт запускает массив результатов последовательно, находя главный узел / ствол (большой босс) сначала, а потом добавляю детей, потом внуков и так далее. Каждый раз, когда узел «сортируется», он будет удаляться из цикла, пока не останется ни одного узла. Предполагается, что:

  • Нет бесхозных записей (сотрудники с недопустимыми manager_ids)
  • Циркулярных ссылок нет, либо просто (А менеджер Б и Б менеджер А) или сложный (* менеджер B, менеджер B и менеджер 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

Вот ссылка на ответ

Вот полный код для создания древовидной структуры для управления иерархией с использованием php и mysql.

0
По вопросам рекламы [email protected]