选择具有父子关系的类别数据的查询

时间:2013-04-22 11:48:00

标签: mysql sql select tree

我有这个db表

CREATE TABLE IF NOT EXISTS `category` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `parent_id` int(11) NOT NULL,
  `name` varchar(100) NOT NULL,
  `inherit` enum('Y','N') NOT NULL DEFAULT 'N',
  PRIMARY KEY (`id`)
) ENGINE=MyISAM  DEFAULT CHARSET=latin1 AUTO_INCREMENT=15 ;

--
-- Dumping data for table `category`
--

INSERT INTO `category` (`id`, `parent_id`, `name`, `inherit`) VALUES
(1, 0, 'Fruits', 'N'),
(2, 0, 'Electronics', 'N'),
(3, 0, 'Furniture', 'N'),
(4, 0, 'Garden', 'N'),
(5, 1, 'Apples', 'N'),
(6, 1, 'Bananas', 'N'),
(7, 5, 'Green Apples', 'Y'),
(8, 5, 'Red Apples', 'N'),
(9, 2, 'Mobiles', 'Y'),
(10, 2, 'Televisions', 'N'),
(11, 9, 'Android', 'N'),
(12, 9, 'iPhone', 'Y'),
(13, 7, 'One Dozen Green Apples', 'Y'),
(14, 7, 'Two Dozens Green Apples', 'N');

还有另一个表格,我保留了user_id,category_id,例如 user_id 1000可以看到1和5,我将此信息放在会话中,以便我的查询变为

SELECT *
FROM `category`
WHERE id
IN ( 1, 5 )

此查询显示Fruits > Apples - 这一切正常。但是,我已将“Green Apples”标记为Inherit = 'Yes',因此用户1000也应该看到“Green Apples”,但不会看到“Red Apples”。如果Green Apples下的子类别标记为inherit ='Y'...例如“一打绿苹果”也应该列出!!

我想给UNION一个机会,但是无法弄清楚如何让两个级别更深......

SELECT * FROM (
    SELECT *
    FROM `category`
    WHERE id
    IN ( 1, 5 )

    UNION

    SELECT c.*
    FROM `category` c
    INNER JOIN `category` parent ON parent.id = c.id AND c.inherit = 'Y'
    WHERE c.parent_id
    IN ( 1, 5 )
) all_cats

你对我有什么建议?我是打开表结构更改,如果这使查询更容易?感谢

3 个答案:

答案 0 :(得分:4)

尝试自我加入:

编辑:我忘记了WHERE子句

SELECT
    a.name,
    b.name,
    c.name
FROM 
    category as a
LEFT JOIN category as b
    ON b.parent_id = a.id
INNER JOIN category as c
    ON c.parent_id = b.id
    AND c.inherit = 'Y'
WHERE
    a.id = 1

但理想的解决方案是使用递归函数来执行此操作,因为在该表中您描述的是类别树。上面的查询是静态的,它返回2个级别(2个子类别),根据我的理解,你需要动态的东西。

类似下面的功能:

public String getCategory(int categId){

    String sSql = "SELECT name FROM category WHERE id = " + categId ;
    String name = oDb.exec(sql).get("name");

    sSql = "SELECT id FROM category WHERE inherit = 'Y' AND parent_id = " + categId ;
    int nextCategId = oDb.exec(sql).get("id");

    if(nextCategId != null){
        return name + "," + getCategory(nextCategId);
    }else{
        return name;
    }

}

因此,假设继承了Apples类,getCategory(1)的结果应为Fruits,Apples,Green Apples

答案 1 :(得分:1)

要在mysql中使用一个查询获取所有类别,您将需要与类别树中的深度级别一样多的自连接。 如果你的深度水平不受限制,这显然是不可能的。 但是,您可以使用存储过程来执行id。

假设您有一个包含用户类别的表格,如:

CREATE TABLE IF NOT EXISTS `user_category` (
  `user_id` int(11) NOT NULL,
  `category_id` int(11) NOT NULL,
  PRIMARY KEY (`user_id`,`category_id`)
) ENGINE=MyISAM  DEFAULT CHARSET=latin1;

有一些数据,如:

INSERT INTO `user_category` ( `user_id`, `category_id` ) VALUES
( 1000, 1 ),
( 1000, 5 );

您可以创建一个存储类别ID的临时表cat_tree,使用user_category表中给定user_id的所有父类别对其进行初始化,并且只要最后一个连接插入,就重复自连接树还没有一个类别。

DELIMITER //
DROP PROCEDURE IF EXISTS show_user_categories//
CREATE PROCEDURE show_user_categories( uid INT(11) )
BEGIN
  DECLARE found INT(11) DEFAULT 1;
  DROP TABLE IF EXISTS cat_tree;
  CREATE TABLE cat_tree (cat_id int(11) PRIMARY KEY) ENGINE=HEAP;
  INSERT INTO cat_tree
    SELECT category_id FROM user_category
    WHERE user_id = uid;
  SET found = ROW_COUNT();
  WHILE found > 0 DO
    INSERT IGNORE INTO cat_tree
      SELECT c_child.id FROM cat_tree c JOIN category c_child 
      WHERE c.cat_id = c_child.parent_id AND c_child.inherit = 'Y';
    SET found = ROW_COUNT();
  END WHILE;
  SELECT cat_id FROM cat_tree;
  DROP TABLE cat_tree;
END;//
DELIMITER ;

此过程为您提供给定user_id的类别ID的完整列表。

CALL show_user_categories( 1000 ); 

请参阅sqlfiddle上的工作示例。

答案 2 :(得分:0)

在SQL Server中:

;with CTE as
(
select id,name from category where id in(1,5)
union all
select c.id,c.name from category c join CTE ct on  c.parent_id=ct.id and c.inherit='Y'
)


select * from CTE