Я пытаюсь понять, как создать поисковую систему для моей базы данных.
Структура для виртуальных папок:
таблицы каталогов
id name parent
Структура для СМИ:
настольные носители
id name parent
Я использую следующую функцию PHP, чтобы перечислить все мои каталоги
function print_menu($id = 0) {
$sql = "SELECT directories.id AS id, directories.name AS name, directories.parent AS parent, directories.icon AS icon FROM directories
WHERE directories.parent = '".$id."' GROUP BY directories.id ORDER BY directories.name ASC";
$req = $cnx->prepare($sql);
$req->execute();
if ($req->rowCount()>0) {
echo "<ul>";
while ($data=$req->fetch()) {
echo "<li>".$data['name']."</li>";
print_menu($data['id']);
}
echo "</ul>";
}
}
Теперь допустим, я получил следующие папки
И я хочу найти носители с именем «% media_1%» в «проекте 1», включая все его подпапки, как я могу это сделать, не выполняя запрос для каждой папки и подпапок? Уровень вложенных папок может варьироваться.
В общем, с помощью таблицы «tree», чтобы получить список всех потомков, и известной максимальной глубины, вы можете построить запрос следующим образом:
SELECT layerN.ID
FROM theTable AS layer1
INNER JOIN theTable AS layer2
ON layer1.id = layer2.parent_id
OR layer1.id = layer2.id
INNER JOIN theTable AS layer3
ON layer2.id = layer3.parent_id
OR layer2.id = layer3.id
...
INNER JOIN theTable AS layerN
ON `layerN-1`.id = layerN.parent_id
OR `layerN-1`.id = layerN.id
WHERE layer1.parent_id = [chosen_parent_node_id]
;
Без дополнительных предложений «ИЛИ» в условиях «ON» промежуточные узлы не были бы включены.
Если промежуточные узлы нежелательны и «листовые» узлы имеют разную глубину, INNER JOIN
s нужно будет LEFT JOIN
с, и SELECT
поле будет немного сложнее:
SELECT IFNULL(layerN.id, IFNULL(`layerN-1`.id, IFNULL(....))) AS leafID
вперемежку
Вы можете иметь дополнительную таблицу для подведения итогов общего происхождения, в простейшей версии будет два поля ancestor_id
а также descendant_id
(хотя вы можете указать и «расстояние»). Самая большая проблема будет состоять в том, что его нужно будет поддерживать с помощью кода некоторой формы, триггеры в древовидной таблице, вероятно, будут наиболее эффективным и надежным методом.
Итак, я нашел решение, я использую функцию, опубликованную выше, чтобы получить идентификаторы всех подкаталогов для данного каталога, скажем, «проект 1» имеет идентификатор 42, который я буду использовать
$all_ids = array();
function print_menu($id = 0) {
$sql = "SELECT directories.id AS id, directories.name AS name, directories.parent AS parent, directories.icon AS icon FROM directories
WHERE directories.parent = '".$id."' GROUP BY directories.id ORDER BY directories.name ASC";
$req = $cnx->prepare($sql);
$req->execute();
if ($req->rowCount()>0) {
while ($data=$req->fetch()) {
$all_ids[] = $data['id'];
print_menu($data['id']);
}
}
}
all_ids[] = 42;
print_menu(42)
Тогда я просто делаю запрос, как
SELECT * FROM medias WHERE medias.parent IN (".implode(',', $all_ids).") AND medias.name LIKE '%media_1%'
Запрос будет искать только медиа в «проекте 1» и его подкаталогах, независимо от того, сколько уровней