Сортировка Mysql Иерархические данные

У меня есть вопрос, и я работаю над этим на прошлой неделе и пока не могу решить. Я могу запрашивать подменю с помощью соединения, но я не могу его заказать.

у меня такая таблица

| id   |  name           |  parent | order  |    
|-------------------------------------------|
| 1    | menu1           |  0      |   1    |
| 2    | submenu1        |  1      |   2    |
| 3    | submenu2        |  1      |   1    |
| 4    | subsubmenu      |  2      |   1    |
| 5    | subsubsubmenu:) |  4      |   1    |
| 6    | menu2           |  0      |   3    |
| 7    | menu3           |  0      |   2    |
|-------------------------------------------|

Я хочу получить что-то вроде этого.

| - menu1
      | - submenu2
      | - submenu1
              | - subsubmenu
                       | - subsubsubmenu:)
| - menu3
| - menu2

Может ли кто-нибудь дать мне представление о том, как с этим справиться? Спасибо


person Mücahit Büyükyılmaz    schedule 06.04.2013    source источник
comment
Это зависит от того, какой результат вы хотите получить, хотите ли вы получить подменю в плоском списке или в виде отдельных столбцов. Можете ли вы привести пример запроса, который вы используете?   -  person aurelijusv    schedule 06.04.2013
comment
Мне это нужно в плоском списке. Может быть, с колонкой уровня.   -  person Mücahit Büyükyılmaz    schedule 06.04.2013
comment
Можете ли вы показать свой текущий код?   -  person jnovack    schedule 06.04.2013
comment
да, я использовал запрос здесь explainextended.com/2009/03/17/ иерархические-запросы-в-mysql   -  person Mücahit Büyükyılmaz    schedule 06.04.2013
comment
было бы так просто, если бы MySQL наконец появился в 21 веке и поддерживал рекурсивные запросы.   -  person a_horse_with_no_name    schedule 06.04.2013


Ответы (3)


Я изучил http://explainextended.com/2009/03/17/hierarchical-queries-in-mysql/ и нашли решение вашей проблемы. Я думаю, у вас уже есть свое решение, но для всех, кто ищет такое же решение, я отвечаю здесь.

Мое решение будет работать и для реляционных таблиц, поскольку мы не можем установить ноль (0) в родительском поле в реляционной таблице. Это будет NULL, и мое решение отлично работает и для реляционных таблиц.

The Function

DROP FUNCTION IF EXISTS hierarchy_connect_by_parent_eq_prior_id;
DELIMITER $$
CREATE FUNCTION hierarchy_connect_by_parent_eq_prior_id(value INT) RETURNS INTEGER
NOT DETERMINISTIC
READS SQL DATA
BEGIN
    DECLARE _parent INT;
    DECLARE _rank INT;
    DECLARE CONTINUE HANDLER FOR NOT FOUND SET @id = NULL;

    SET _parent = @id;
    SET _rank = 0;

    IF @id IS NULL THEN
            RETURN NULL;
    END IF;

    LOOP
        SET @innerrank = 0;
        SELECT p.id 
        INTO   @id
        FROM   (
                SELECT   id, @innerrank := @innerrank+1 AS rank 
                FROM     yourTable 
                WHERE    COALESCE(parent, 0) = _parent 
                ORDER BY yourField
                ) p 
        WHERE   p.rank > _rank LIMIT 0, 1;
        IF @id IS NOT NULL OR _parent = @start_with THEN
                SET @level = @level + 1;
                RETURN @id;
        END IF;
        SET @level := @level - 1;
        SET @innerrank = 0;
        SELECT COALESCE(p.parent, 0), p.rank
        INTO   _parent, _rank
        FROM   (
                SELECT id, parent, @innerrank := @innerrank+1 AS rank
                FROM    yourTable
                WHERE   COALESCE(parent, 0) = (
                    SELECT COALESCE(parent, 0) FROM yourTable WHERE id = _parent
                    ) 
                ORDER BY yourField
               ) p
        WHERE p.id = _parent;
    END LOOP;       
END;
$$
DELIMITER ;

Замените yourTable на имя вашей таблицы и yourField на имя поля, по которому вы хотите отсортировать данные.

The Query

SELECT ou.* FROM (
    SELECT hi.id, parent, yourField FROM (
        SELECT hierarchy_connect_by_parent_eq_prior_id(id) AS id, 
            @level AS level 
            FROM (
                SELECT @start_with := 0, @id := @start_with, @level := 0
                 ) vars, yourTable 
            WHERE @id IS NOT NULL
        ) ho 
    JOIN yourTable hi ON hi.id = ho.id
) ou

Замените yourTable на имя вашей таблицы и yourField на имя поля, которое вы хотите отобразить.

Это даст результат, который вам нужен. Я протестировал его и работает хорошо.

Вот http://sqlfiddle.com/#!9/9d060d/2 для увидеть его в действии.

person Tareq    schedule 27.05.2015

Вы не можете сделать это с помощью одного запроса. Просто используйте рекурсию на выбранном вами языке сценариев

class Hierarchy

    function __construct(db)
        db = db.connect
        this.stmt = db.prepare "SELECT name FROM Hierarchy WHERE parent = ?"
        this.tree = {}
    end

    function getHierarchyFromParent(parent = 0, tree = null)
        tree = this.tree if tree is null
        while result = this.stmt.execute(parent).fetch
            tree[result.name] = {
                children: this.getHierarchyFromParent result.id tree[result.name]
            }
        end
    end
end
person Explosion Pills    schedule 06.04.2013
comment
Да, именно так я использовал его раньше, но он действительно медленный с большими данными. Мне нужно получить все меню и подменю с 1 запросом. - person Mücahit Büyükyılmaz; 06.04.2013
comment
@MücahitBüyükyılmazMücahitBüyükyılmaz, даже если бы это был один запрос, это не обязательно было бы быстрее ... альтернативой было бы получение всех строк одновременно с помощью одного запроса, а затем создание ассоциаций родитель/потомок, дважды перебирая список - person Explosion Pills; 06.04.2013
comment
@ExplosionPills прав, если вы не хотите иметь запросы с подвыборкой и не хотите иметь плоский порядок в таблице, единственный вариант - рекурсивный порядок на уровне приложения (или на уровне базы данных с помощью процедур) - person aurelijusv; 06.04.2013

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

| id   |  name           |  parent | order  |    
|-------------------------------------------|
| 1    | menu1           |  0      |   1    |
| 2    | submenu1        |  1      |   2    |
| 3    | submenu2        |  1      |   3    |
| 4    | subsubmenu      |  2      |   4    |
| 5    | subsubsubmenu:) |  4      |   5    |
| 6    | menu2           |  0      |   6    |
| 7    | menu3           |  0      |   7    |
|-------------------------------------------|

Реализовать такой порядок не очень сложно.

person aurelijusv    schedule 06.04.2013
comment
На самом деле я думал об этом, но есть столбец для отдела, который я не писал. Я использую эту таблицу для веб-сайта нашего университета, и около 110 факультетов используют эту таблицу для своего меню. Заказывается отдельно для каждого отдела. - person Mücahit Büyükyılmaz; 06.04.2013
comment
не имеет значения, сколько пунктов меню или отделов будет использовать эту таблицу, вы все равно можете использовать плоский порядок, что сделает выбор очень эффективным. Я не вижу преимущества в том, чтобы делать сложные операторы выбора во внешнем интерфейсе вместо того, чтобы сохранять немного более сложную вставку/обновление в бэкэнде. - person aurelijusv; 06.04.2013
comment
@AurelijusValeiša интересная мысль. но вы не думаете, что порядок для menu2 и menu3 должен быть 1 ? Или я неправильно понял - person MalTec; 06.04.2016