2014-06-18 40 views
-2

我以前问过这个问题,然后有人建议它是另一个以前回答的问题的副本。但是,尽管尝试了3个小时,我仍然无法将该解决方案适应于我所需的解决方案。通过关键字调整查询以返回类别层次

所以,我的新问题是如何根据自己的需要调整该解决方案。

我的类别/子类别的数据库架构的简化版本是这样的:

tblAllCategories

record_id title     level parent_cat_id parent_id keywords 
------------------------------------------------------------------------------------------- 
1   Antiques & Collectables 0  NULL    NULL   junk 
2   Art      0  NULL    NULL   
25   Furniture    1  1     1    
59   Office Furniture   2  1     25   retro,shabby chic 
101  Chairs     3  1     59    

注:

  • 级别0 =顶级类别,等级1 =第二级等
  • parent_cat_id是顶级类别(即h AVING 0级)
  • PARENT_ID指水平立即

我加入了关键字列,以帮助关键字搜索,以便在某些相关类别的商品会在用户输入的关键词,但没有被退回的相关水平之上不要选择要钻入的类别。

因此,在用户输入关键字后,例如“Retro”,我不仅需要返回其关键字列中具有术语“retro”的类别,还需要返回所有更高级别的类别。因此,根据以上架构,对“复古”的搜索将返回类别59及其超类别 - 25和1.

查询应按级别排序,以便前端搜索结果将显示这样的事情(必要编码后):

enter image description here

提供的解决方案是从this question

,查询如下:

SELECT T2.id, T2.title,T2.controller,T2.method,T2.url 
FROM (
    SELECT 
     @r AS _id, 
     (SELECT @r := parent_id FROM menu WHERE id = _id) AS parent_id, 
     @l := @l + 1 AS lvl 
    FROM 
     (SELECT @r := 31, @l := 0) vars, 
     menu m 
    WHERE @r <> 0) T1 
JOIN menu T2 
ON T1._id = T2.id 
ORDER BY T1.lvl DESC; 

我需要修改此查询以处理传递的关键字,而不是ID。

+0

您需要一个分层或递归查询。在SQLServer或Oracle中,这将使用CTE完成,但不幸的是,我不相信MySQL具有这种能力。是否只有3级?如果这样的话就像你的查询可以工作。 –

回答

1

编辑vars子查询有@r等于与keywork行的RECORD_ID,像

SELECT T2.record_id, T2.title,T2.level,T2.keywords 
FROM (SELECT @r AS _id 
      , (SELECT @r := parent_id 
       FROM tblAllCategories 
       WHERE record_id = _id) AS parent_id 
      , @l := @l + 1 AS lvl 
     FROM (SELECT @r := record_id, @l := 0 
       FROM tblAllCategories 
       WHERE keywords like '%retro%') vars 
      , tblAllCategories m 
     WHERE @r <> 0) T1 
    JOIN tblAllCategories T2 ON T1._id = T2.record_id 
ORDER BY T1.lvl DESC; 

SQLFiddle demo

具有keywork为逗号分隔值不最好的是,该表与关键字表(与强制结点表)之间的多对多关系将会更好,因为它将避免使用LIKE。在这个例子中,如果有另一个类别的关键词'retrobike',那么这个类别和他的所有层次结构都会出现在结果中。

+0

非常感谢Serpiton。 – user460114

1

这将需要一段时间,所以要喝点咖啡。

有很多很好的资源可用于分层开发。大多数你会看到下面来自像this这样的网站,它指的是你很乐意推荐的Celko。

您需要做的第一件事就是删除关键字字段。开发,使用和维护方面的额外努力远不及所得到的好处。我会告诉你以后如何实现它。

在此设计中,将行看作节点。每个节点都有两个值,即左边界和右边界。这些形成了一个范围或影响范围。如果一个节点的边界为1:4,而另一个节点的比例为2:3,则第二个节点是第一个子节点,因为其跨度包含在第一个节点的范围内。另外,由于第二个节点的边界是连续的,因此它下面不能有节点,所以它必须是叶节点。这听起来可能听起来很复杂,特别是在考虑多层次的节点时,但您会看到SQL如何编写起来相对容易,而且表的维护工作量很小。

完整的脚本是here

CREATE TABLE categories (
    id  INT not null auto_increment PRIMARY KEY, 
    name VARCHAR(50) NOT NULL, 
    lBound INT    NOT NULL, 
    rBound INT    NOT NULL, 
    -- MySQL does not implement check constraints. These are here for illustration. 
    -- The functionality will be implemented via trigger. 
    CONSTRAINT cat_ptr_incr_chk CHECK (lBound < rBound), -- basic integrity check 
    CONSTRAINT cat_ptr_root_chk CHECK (lBound >= 0)  -- eliminates negative values 
); 
create unique index ndx_cat_lBound on categories(lBound); 
create unique index ndx_cat_rBound on categories(rBound); 

发现有什么在这里,说:“我是一个叶子结点”,“我是根”或“我的根节点是这样的,和这样的。”这些信息都包含在lBound和rBound(左边界,右边界)值中。我们来构建一些节点,以便我们可以看到这个样子。

INSERT INTO categories(name, lBound, rBound) 
values('Categories', 0, 1); 

ID name  lBound rBound 
== ========== ====== ====== 
1 Categories  0  1 

这是我们在创建表格上的触发器之前做的。实际上,插入触发器不必具有必须在第一行(整个结构的根节点)时才能识别的特殊代码。该代码只会在插入第一行时执行,而不会再次执行。现在我们不必担心它。

所以现在我有结构的根源。请注意,它的边界是0和1.没有什么可以适合0和1,所以这是一个叶节点。树根也是一片叶子。这意味着树是空的。

所以现在我们编写触发器和dml程序。代码在脚本中,所以我不会在这里复制它,只要说插入和删除触发器不会允许任何人发出插入或删除语句。任何人都可以发布更新,但只允许更改名称。插入,删除和完成更新的唯一方法是通过程序执行。考虑到这一点,我们来创建根下的第一个节点。

call ins_category('Electronics', 1); 

这将创建一个名为'Electronics'的节点作为ID = 1(根)节点的子节点。

ID name  lBound rBound 
== ========== ====== ====== 
1 Categories  0  3 
2 Electronics  1  2 

请注意触发器如何扩展根的右边界以允许新节点。下一个节点将是另一个级别。

call ins_category('Televisions', 2); 

节点2是电子的,所以新节点将是它的子节点。

ID name  lBound rBound 
== ========== ====== ====== 
1 Categories  0  5 
2 Electronics  1  4 
3 Televisions  2  3 

让我们创建一个新的较高级别的节点 - 它仍然必须在根目录下,但将电子旁边的子树的开始。

call ins_category('Antiques & Collectibles', 1); 

ID name     lBound rBound 
== ==========    ====== ====== 
1 Categories     0  7 
2 Electronics     1  4 
3 Televisions     2  3 
4 Antiques & Collectibles  5  6 

注意5-6不适合除根外的任何边界范围。所以它是一个直接位于根下的子节点,就像电子产品一样,但独立于其他子节点。

用于给出更清晰的结构图的SQL并不复杂。有很多更多的节点完成了树之后,让我们看看是什么样子:

-- Examine the tree or subtree using pre-order traversal. We start at the node 
-- specified in the where clause. The root of the entire tree has lBound = 0. 
-- Any other ID will show just the subtree starting at that node. 
SELECT n.ID, n.NAME, n.lBound, n.rBound 
FROM categories p 
join categories n 
    on n.lBound BETWEEN p.lBound AND p.rBound 
where p.lBound = 0 
ORDER BY n.lBound; 

+----+----------------------------+--------+--------+ 
| id | name      | lBound | rBound | 
+----+----------------------------+--------+--------+ 
| 1 | >Categories    |  0 |  31 | 
| 2 | -->Electronics    |  1 |  20 | 
| 3 | ---->Televisions   |  2 |  9 | 
| 4 | ------>Tube    |  3 |  4 | 
| 5 | ------>LCD     |  5 |  6 | 
| 6 | ------>Plasma    |  7 |  8 | 
| 7 | ---->Portable Electronics |  10 |  19 | 
| 8 | ------>MP3 Players   |  11 |  14 | 
| 9 | -------->Flash    |  12 |  13 | 
| 10 | ------>CD Players   |  15 |  16 | 
| 11 | ------>2-Way Radios  |  17 |  18 | 
| 12 | -->Antiques & Collectibles |  21 |  28 | 
| 14 | ---->Furniture    |  22 |  27 | 
| 15 | ------>Office Furniture |  23 |  26 | 
| 16 | -------->Chairs   |  24 |  25 | 
| 13 | -->Art      |  29 |  30 | 
+----+----------------------------+--------+--------+ 

输出上面实际上是在脚本中定义的视图,但它清楚地显示出层次结构。这可以很容易地转换为一组嵌套菜单或导航节点。

可以进行增强,但它们不需要改变这个基本结构。你会发现它很容易维护。我已经开始认为在一个DBMS如Oracle,SQL Server或PostGreSQL中这会让事情变得容易很多,这允许触发视图。然后,访问权限可以仅限于视图,因此触发器会处理所有事情。这将消除对单独的存储过程的需要。但这种方式并不坏。我可以愉快地生活在一起。事实上,使用单独的视图无法使用的存储过程具有简单性和灵活性(不能将参数传递给视图)。

关键字功能也被定义,但我不会在这里显示。看剧本。一次执行一次,以清楚了解正在发生的事情。如果你有任何问题,你知道在哪里找到我。

[编辑]添加了一些enhancements,包括使用关键字。