【问题标题】:Tree structure reversed (MySQL)树结构反转(MySQL)
【发布时间】:2016-08-15 18:42:28
【问题描述】:

我有一个以两种方式构建的类别树:每个类别都有一个路径和一个 parentID。路径由类别的 ID(从下到上)组成。 parentID 引用另一个类别。所以我的表是这样的:

id | name        | path    | parentID
---+-------------+---------+---------
1  | Root        | NULL    | NULL
2  | Main        | NULL    | 1
3  | Electronics | |2|     | 2
4  | Computers   | |3|2|   | 3
5  | PCs         | |4|3|2| | 4
6  | Macs        | |4|3|2| | 4
7  | Cameras     | |3|2|   | 3
8  | Canon       | |7|3|2| | 7

现在我不需要“根”和“主”类别。我试图实现的是这样的输出:

id | resolved_path
---+-----------------------------
3  | Electronics
4  | Electronics_Computers
5  | Electronics_Computers_PCs
6  | Electronics_Computers_Macs
7  | Electronics_Cameras
8  | Electronics_Cameras_Canon

所以我有不同的深度,我需要将类别按相反的顺序排列。我在网上并没有找到太多关于这个的信息。我得到的只是这个显示类别深度的 sn-p:

SELECT
*,
(ROUND(
     (LENGTH(cat.path) - LENGTH(REPLACE(cat.path, '|', ''))) / LENGTH('|')
) - 2) depth
FROM
    categories cat
WHERE
    cat.path IS NOT NULL

我不知道什么更容易:递归地遍历 parentID 或对路径做一些魔术。

【问题讨论】:

  • 你可以调整这个答案以相反的顺序工作:stackoverflow.com/a/5928675/1503505
  • @Preuk 我的问题是我的深度不同。所以我需要某种循环来使用这个解决方案。有什么办法吗?
  • 为什么不创建可用于搜索父类别的中间表 (categories_parents)?它更快更容易。
  • @JuanLago 这个想法其实还不错。只是为了确保我理解你的意思:我会通过 php (或类似的)遍历类别并制作一个表格,将父母存储到每个类别中?这并不是我真正想要的,因为我尝试使用 MySQL 来完成这一切,但我会试一试。

标签: mysql sql


【解决方案1】:

你可以使用这个(未优化的)函数作为基线:

DELIMITER //
DROP FUNCTION IF EXISTS extract_path //
CREATE FUNCTION extract_path(idlist VARCHAR(255))
RETURNS LONGTEXT
BEGIN
    DECLARE result LONGTEXT;
    DECLARE tmplist VARCHAR(255);
    DECLARE buffer VARCHAR(255);
    DECLARE lastpos INT;

    -- reverse and trim last separator (that first of reversed string)
    SELECT TRIM(BOTH FROM SUBSTRING(REVERSE(idlist), 2)) INTO tmplist;

    mainloop: LOOP
      -- split on separator
      SELECT LOCATE('|', tmplist) INTO lastpos;
      -- detect end
      IF lastpos IS NULL OR lastpos < 2
      THEN LEAVE mainloop;
      END IF;

      -- resolve next id
      SELECT cat.name INTO buffer
      FROM categories cat
      WHERE cat.id = TRIM(REVERSE(SUBSTRING(tmplist, 1, lastpos - 1)));

      -- append new element
      SELECT CONCAT(COALESCE(CONCAT(result, '_'), ''), buffer) INTO   result;

      -- prepare for next iteration
      SELECT TRIM(BOTH FROM SUBSTRING(tmplist, lastpos + 1)) INTO tmplist;

      -- detect end (corner case)
      IF tmplist IS NULL
      THEN LEAVE mainloop;
      END IF;

    END LOOP;

    RETURN result;
END //

SELECT extract_path('|1|2|3|');
-- with categories 1 -> foo ; 2 -> bar ; 3 -> baz
-- output is 'baz_bar_foo'
DELIMITER ;

【讨论】:

  • @jkrzefski 没问题,请问您是否需要澄清。无论如何,我建议您考虑按照 Juan Lago 的评论修复您的模型。您可以从我的函数中提取元素来构建您的中间表。
猜你喜欢
  • 2021-04-04
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2022-01-01
  • 2016-01-06
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多