【问题标题】:Looking for a query to display nodes in the same level only寻找查询以仅显示同一级别的节点
【发布时间】:2011-04-23 07:29:53
【问题描述】:

上图显示了嵌套模型树方法。我正在搜索一个查询,当调用 FRUIT 或 MEAT 时,该查询将仅显示具有节点 FRUIT 和 MEAT 的第 1 级

我

我已经制定了一个孩子和兄弟姐妹的组合,我认为当红色被称为如下时拉牛肉。

 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 的 parent_id 插入,该 parent_id 将与名为“name”的现有字段相关,然后如果 parent field = 到现有 name 字段,则从该 name 字段中获取 category_id 并将其作为新插入名称的 parent_id。

例如,用户插入名称“blue”和父名称“food”,那么名称 blue 将采用 food 的类别 id,并将其作为 blue 的 parent_id...

【问题讨论】:

  • 您的数据库结构是什么样的?你有什么尝试但没有奏效?
  • my 是一个嵌套集合模型,如
  • 这个表数据结构 CREATE TABLE IF NOT EXISTS 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, PRIMARY KEY (category_id) ENGINE=MyISAM DEFAULT CHARSET=latin1 AUTO_INCREMENT=12 ;
  • 您是否尝试过在dev.mysql.com/tech-resources/articles/hierarchical-data.html 上查询嵌套集模型。 “查找节点深度”部分下的查询似乎符合您的要求。

标签: sql


【解决方案1】:

您确定要 left_node 和 right_node 不为空吗?你为一片叶子(例如香蕉)使用什么价值?这个问题没关系。

您似乎正在使用 MySQL,它没有实现回答此问题的最佳方法。您需要递归 CTE。但是,作为 hack,您可以执行以下操作:

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 中执行此操作

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);

]

【讨论】:

  • @Andrew 我对 mysql 数据库作为服务器有疑问,我将不得不更新 mysql 数据库,以便它可以支持递归 CTE,或者我必须安装一个支持递归 CTE 服务器的新 sql 服务器.如果我安装递归 CTE 服务器现在仍然支持 Mysql 查询呢?
  • Postgresql 是开源的,可以处理很多高级 SQL 结构,比 MySQL 多很多。但是,它更大,许多人认为速度较慢。 (您可以在 Internet 上找到很多关于此的讨论!)编写一个使用循环从任意节点到根的 MySQL 函数可能更容易。您还可以保留“父”字段。它将冗余信息引入到表中,这些信息必须通过触发器保持有效,但它使得从记录到父级(您可以从中知道级别)更容易。
  • 如何使用第一个选择和 CREATE VIEW 作为第二个的参考?我想知道如何在一个查询中使用 SELECT?
  • 只需在第二个查询中显示“父母”的位置,然后粘贴到视图定义的 SELECT 中。
  • 会是这样吗? 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; ) WHERE parent NOT IN (SELECT child FROM parents) ;
【解决方案2】:

我认为你应该重组你的数据。让孩子持有父母的ID。就像现在一样,我认为您在构建查询时会遇到很多麻烦,当您需要添加一些节点时会更麻烦,因为您需要重新计算 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 设置为 2,因为那是水果的 categoriy_id。

与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';        

【讨论】:

  • 这是类似返回兄弟的概念但它是空的,我在SQL框中测试了它。
  • 我想知道为什么会导致这个结果为空。你说的是同一个字段值数据?
  • 等等我可以看到你有一个不同的表结构...我看到你有 UNION ALL 让我检查一下表结构。那个 Union 都是从哪里来的?当我在数据库中插入值时,我认为我没有使用 UNION ALL,现在有没有办法通过插入的值来更改它以查看是否返回一些数据?
  • 现在它正在 SQL 测试框上工作,即使值不同,在它返回空之前不知道为什么,很好,但我有更多颜色它只是返回两个情况下,RED 和 YELLOW GREEN 甚至更多。我有一个问题Categories as P、Categories P 和Categories S 被解释为级别还是同一级别内的节点数量?
  • 一件很有意思的事是,如果同一个关卡有三个节点,你指定中间的一个,那么它不会拉出它的任何一个关卡,但是如果指定了角落的一个然后它会拉起另一端的那个。例如,RED YELLOW GREEN 如果我指定 RED 那么它会拉起 GREEN 如果我指定 GREEN 它会拉起 RED 但在任何情况下都不会拉起 YELLOW 如果我指定 YELLOW 那么它将返回空,如果有中间的四个不满足这个条件。另一件事是如何在 sql 中解释这个 ALIASED 类别?
【解决方案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

【讨论】:

  • Andriy,我想知道如果我使用 cte server 2005+ 语句,我的虚拟主机是否可以。我想大多数网络主机都应该支持 CTE。
  • 我是否必须选择所有行,例如 WITH categories (category_id, name, left_node, right_node) AS ( SELECT 1, 'Food', 1, 18 UNION ALL SELECT 2, 'Fruit', 2 , 11 UNION ALL SELECT 3, 'Red', 3, 6 UNION ALL SELECT 4, 'Cherry', 4, 5 UNION ALL SELECT 5, 'Yellow', 7, 10 UNION ALL SELECT 6, 'Banana', 8, 9 UNION ALL SELECT 7, 'Meat', 12, 17 UNION ALL SELECT 8, 'Beef', 13, 14 UNION ALL SELECT 9, 'Pork', 15, 16 ?
  • 当你说假设@parent_id 是要返回其直接子项的项目的ID:你是什么意思?在表结构中该字段不存在,或者它只是一些临时字段创建为对与被调用节点相关的字段的引用?或者我必须在数据库中为@parent_id 创建一个字段?
  • 有些数据库服务器支持 CTE,有些则不支持。我不知道你的数据库服务器是什么,所以我不能告诉你是否可以使用 CTE。但至于我的解决方案,您不需要我包含的 CTE。它只是用作真实表的替代品,而您已经得到了真实表。所以只需忽略 CTE 并使用从 SELECT c.* 开始的部分。
  • 至于@parent_id,这只是我对您可能如何指定应该返回哪一行数据的假设。我假设您会指定所需项目的直接父项,并且我还假设您会通过其 ID 指定该父项。如果我的假设是错误的,您可能需要澄清这一点,以便我可以相应地修改我的解决方案。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2018-06-22
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多