Поиск запроса для отображения узлов только на одном уровне

#sql

#sql

Вопрос:

введите описание изображения здесь

На изображении выше показан подход с вложенным деревом модели. я ищу запрос, который будет отображать только уровень номер 1 с узлами FRUIT и MEAT при вызове FRUIT или MEAT

Я

Я сформулировал комбинацию дочерних элементов и шипений, которые, как я думаю, будут использоваться при вызове Red, как показано ниже.

  INSERT 



 $sqlinsert = "INSERT INTO categories
(categories_id, parent_id,name, parent)
VALUES('','','$name', '$parent')"; 
$enterquery = mysql_query($sqlinsert) or die(mysql_error()); 
$customer_id = mysql_insert_id();
  

Я хочу вставить parent_id, связав новый заполненный родительский элемент, который будет связан с существующим полем с именем «name», а затем, если родительское поле = существующему полю name, затем возьмите category_id из этого поля name и поместите его в качестве parent_id нового ВСТАВЛЕННОГО имени.

например, пользователь вставляет имя «blue» и родительское «food», тогда имя blue примет идентификатор категории food и поместит его в качестве parent_id blue…

Комментарии:

1. Как выглядит структура вашей базы данных? Что вы пробовали и что не сработало?

2. моя модель с вложенным набором, такая как

3. Эта структура данных таблицы СОЗДАЕТ ТАБЛИЦУ, ЕСЛИ ОНА НЕ СУЩЕСТВУЕТ categories ( category_id int(11) NOT NULL AUTO_INCREMENT, name varchar(20) NOT NULL, left_node int(11) NOT NULL, right_node int(11) NOT NULL, ПЕРВИЧНЫЙ КЛЮЧ ( category_id ) ) ENGINE=MyISAM КОДИРОВКА ПО УМОЛЧАНИЮ =latin1 AUTO_INCREMENT =12 ;

4. Вы пробовали запросы на dev.mysql.com/tech-resources/articles/hierarchical-data.html для модели вложенного набора. Запрос в разделе «Поиск глубины узлов», похоже, соответствует вашим требованиям.

Ответ №1:

Вы уверены, что хотите, чтобы left_node и right_node БЫЛИ НЕ РАВНЫ НУЛЮ? Какое значение вы используете для листа (например, банана)? Для этого вопроса не имеет значения.

Похоже, вы используете MySQL, который не реализует наилучший способ ответить на этот вопрос. Вам нужен рекурсивный CTE. Однако, в качестве взлома, вы можете сделать что-то вроде этого:

 CREATE VIEW parents AS 
SELECT c.category_id AS parent, 
 c2.category_id AS child
FROM categories c JOIN categories c2 
ON c2.category_id=c.left_node OR c2.category_id=c.right_node;
  

Теперь вы можете получить первый уровень с

 SELECT child FROM parents
WHERE parent NOT IN (SELECT child FROM parents);
  

Подзапрос NOT IN возвращает корневой файл. Чтобы получить произвольный уровень, насколько я знаю, вам нужна рекурсивная или итеративная процедура или база данных, которая допускает рекурсивный CTE, или более ужасные представления.

[редактировать: вы можете сделать это одним трудночитаемым ВЫБОРОМ

 SELECT child FROM parents
    WHERE parent NOT IN (SELECT child FROM
        (  SELECT c.category_id AS parent, 
     c2.category_id AS child
    FROM categories c JOIN categories c2 
    ON c2.category_id=c.left_node OR c2.category_id=c.right_node) AS anyname);
  

]

Комментарии:

1. @Andrew У меня вопрос с базой данных mysql в качестве сервера, мне придется обновить базу данных mysql, чтобы она могла поддерживать рекурсивный CTE, или мне придется установить новый sql Server, который поддерживает рекурсивный CTE-сервер. И что, если я думаю, что рекурсивный CTE-сервер все еще будет поддерживать запросы Mysql на данный момент?

2. Postgresql имеет открытый исходный код и обрабатывает множество продвинутых конструкций SQL, намного больше, чем MySQL. Однако он больше, и многие люди считают, что медленнее. (Вы можете найти много дискуссий об этом в Интернете!) Возможно, было бы проще написать функцию MySQL, которая использовала цикл для перехода от произвольного узла к корневому. Вы также можете сохранить ‘родительское’ поле. Это вводит избыточную информацию в таблицу, которая должна поддерживаться действительной с помощью триггера, но это значительно упрощает переход от записи к родительской (из которой вы знаете уровень).

3. как я могу использовать первый select с CREATE VIEW в качестве ссылки на второй? Мне было интересно, как я могу использовать для ВЫБОРА в одном запросе?

4. Просто возьмите то место, где во втором запросе указано «родители», и вставьте в поле выбора определения представления.

5. это будет что-то вроде этого? ВЫБЕРИТЕ дочерний элемент ИЗ (ВЫБЕРИТЕ c.category_id КАК родительский, c2.category_id КАК дочерний ИЗ категорий c ПРИСОЕДИНИТЕ категории c2 К c2.category_id=c.left_node ИЛИ c2.category_id=c.right_node; ), ГДЕ родительский элемент ОТСУТСТВУЕТ (ВЫБЕРИТЕ дочерний ЭЛЕМЕНТ ИЗ родительских);

Ответ №2:

Я думаю, вам следует реструктурировать свои данные. Пусть дочерние элементы сохраняют идентификатор родительского элемента. Как и сейчас, я думаю, у вас будет много проблем с построением запросов и еще больше проблем, когда вам нужно добавить какой-либо узел, потому что вам нужно пересчитать значения left_node и right_node.

Вот структура таблицы, которую, я думаю, вам следует использовать.

 create table categories (
    category_id int primary key auto_increment,
    parent_id   int,
    name        varchar(255));

alter table categories
    add constraint FK_categories
    foreign key (parent_id)
    references categories (category_id); 
  

Вот код для вставки ваших тестовых данных. (Давайте продолжим говорить о еде: =) )

 insert into categories (category_id, parent_id, name)
  select 1, null, 'food'   union all
  select 2, 1,    'fruit'  union all
  select 3, 2,    'red'    union all
  select 4, 3,    'cherry' union all
  select 5, 2,    'yellow' union all
  select 6, 5,    'banana' union all
  select 7, 1,    'meat'   union all
  select 8, 7,    'beef'   union all
  select 9, 7,    'pork';
  

фрукты и мясо parent_id = 1 указывают на еду и т.д.

Запросы, которые вам нужно написать, теперь намного проще.

 /* Get parent */
select P.name
from categories as C
    inner join categories as P
        on C.parent_id = P.category_id
where C.name = 'red';        

/* Get children */
select C.name
from categories as C
    inner join categories as P
        on C.parent_id = P.category_id
where P.name = 'red';        

/* Get siblings */
select C2.name
from categories as C1
    inner join categories as C2
        on C1.parent_id = C2.parent_id
 where C1.name = 'red';       

/* Get grandparent */
select G.name
from categories C
    inner join categories as P
        on C.parent_id = P.category_id
    inner join categories as G
        on P.parent_id = G.category_id
where C.name = 'red';        
  

И когда вам нужно добавить синий цвет, вам нужно сделать только это.

 insert into categories(parent_id, name) 
    values(2, 'blue');
  

Установите parent_id для blue равным 2, потому что это categoriy_id для fruit.

Все узлы с тем же прародителем, что и red за исключением братьев и сестер red .

 select
    C2.name
from categories C
    inner join categories as P
        on C.parent_id = P.category_id
    inner join categories as P2
        on P2.parent_id = P.parent_id and
           P2.category_id <> P.category_id
    inner join categories as C2
        on P2.category_id = C2.parent_id
where C.name = 'red';        
  

Комментарии:

1. Это концепция, подобная возвращению sibilings, но она оказалась пустой, я протестировал ее в окне SQL.

2. Я хотел бы знать, почему в результате получается пустой результат. вы говорите об одном и том же значении поля данных?

3. подождите, я вижу, у вас другая структура таблицы… Я вижу, у вас есть UNION ALL, позвольте мне проверить структуру таблицы. Откуда берется все это объединение? Когда я вставлял значения в базу данных, я не думаю, что использовал UNION ALL, есть ли способ изменить это сейчас, вставив значения, чтобы посмотреть, возвращает ли это какие-то данные?

4. Теперь он работает в окне тестирования SQL, даже когда значения разные, до того, как он возвращал пустой, не знаю почему, отлично, Но у меня больше цвета, в данном случае он просто возвращает два, КРАСНЫЙ и ЖЕЛТЫЙ, как насчет ЗЕЛЕНОГО или даже больше. У меня есть вопрос: Категории как P, Категории P и категории S интерпретируются как уровни или как количество узлов на одном уровне?

5. Что очень интересно, так это то, что если есть три узла на одном уровне, и вы указываете один в середине, то он не выведет ни один из своих уровней, но если указать один в углу, то он выведет один на другом конце. например, КРАСНЫЙ ЖЕЛТЫЙ ЗЕЛЕНЫЙ, если я укажу КРАСНЫЙ, то появится ЗЕЛЕНЫЙ, если я укажу ЗЕЛЕНЫЙ, то появится КРАСНЫЙ, но ЖЕЛТЫЙ не будет отображаться ни в одном экземпляре, если я укажу ЖЕЛТЫЙ, то он вернет пустое значение, что, если бы было четыре, те, что посередине, не соответствовали бы этому условию. Другое дело, как интерпретируются эти категории с псевдонимами, как в sql?

Ответ №3:

$node_name является ли узел, прямые дочерние элементы которого должны быть возвращены:

 SELECT c.*
FROM categories c
  INNER JOIN categories p ON c.left_node > p.left_node AND c.right_node < p.right_node
  LEFT JOIN categories g  ON g.left_node > p.left_node AND g.right_node < p.right_node
                         AND g.left_node < c.left_node AND g.right_node > c.right_node
WHERE p.name = '{$node_name}' AND g.category_id IS NULL
  

Комментарии:

1. Андрей, мне интересно, подойдет ли мой веб-хост, если я буду использовать инструкции cte server 2005 . Я предполагаю, что большинство веб-хостингов должны поддерживать CTE.

2. должен ли я выбирать все строки, подобные категориям (category_id, name, left_node, right_node), КАК (ВЫБЕРИТЕ 1, ‘Food’, 1, 18 UNION ALL ВЫБЕРИТЕ 2, ‘Fruit’, 2, 11 UNION ALL ВЫБЕРИТЕ 3, ‘Red’, 3, 6 UNION ALL ВЫБЕРИТЕ 4, ‘Cherry’, 4, 5 UNION ALL ВЫБЕРИТЕ 5, ‘Yellow’, 7, 10 UNION ALL ВЫБЕРИТЕ 6, ‘Banana’, 8, 9 UNION ALL ВЫБЕРИТЕ 7, ‘Meat’ , 12, 17 ОБЪЕДИНИТЬ ВСЕ ВЫБРАННЫЕ 8, ‘Говядина’, 13, 14 ОБЪЕДИНИТЬ ВСЕ ВЫБРАННЫЕ 9, ‘Свинина’, 15, 16 ?

3. когда вы говорите, предполагая, что @parent_id — это идентификатор элемента, чьи прямые дочерние элементы должны быть возвращены: что вы имеете в виду под этим? в структуре таблицы это поле не существует, или это просто какое-то временное поле, созданное как ссылка на поля, связанные с вызываемым узлом? или мне придется создать поле в базе данных для @parent_id?

4. Некоторые серверы баз данных поддерживают CTE, некоторые другие — нет. Я не знаю, какой у вас сервер базы данных, поэтому не могу сказать вам, можете ли вы использовать CTE. Но что касается моего решения, вам просто не нужен CTE, который я включил. Он используется только как замена реальной таблицы, и у вас уже есть реальная таблица. Поэтому просто игнорируйте CTE и используйте часть, начиная с SELECT c.* и далее.

5. Что касается @parent_id , это было всего лишь мое предположение о том, как вы, вероятно, указали бы, какая строка данных должна быть возвращена. Я предполагал, что вы укажете непосредственного родителя требуемых элементов, и я также предполагал, что вы укажете этого родителя по его идентификатору. Если мое предположение неверно, вы можете уточнить этот момент, чтобы я мог соответствующим образом изменить свое решение.