Как запросить таблицу MySql для отображения корня и его дочернего элемента. - PullRequest
7 голосов
/ 03 ноября 2011
UserID      UserName       ParentID      TopID
  1         abc            Null           Null
  2         edf             1             1
  3         gef             1             1
  4         huj             3             1
  5         jdi             4             1
  6         das             2             1
  7         new            Null           Null
  8         gka             7             7

TopID и ParentID взяты из userID

Я хочу получить запись пользователя, а также его дочернюю и дочернюю записи.Здесь userid1 - это корень, а его дочерний элемент - userid2 и userid 3. Поэтому, если идентификатор пользователя равен 1, я должен отобразить все записи от userid 1 до userid 6, поскольку все они являются дочерними и SUbchild корня.Точно так же для userid3 я должен отобразить userid3 и его дочерний Userid 4 и Child of Userid 4 Userid5, если ID пользователя равен 3

, выход должен быть

Userid  Username
3          gef
4          huj
5          jdi

Я буду знать идентификатор пользователя и topIDтак, как я могу сделать запрос, чтобы получить вышеуказанный результат.

SELECT UserID, UserName  FROM tbl_User WHERE ParentID=3 OR UserID=3 And TopID=1;

По вышеуказанному запросу я могу отобразить идентификатор пользователя 3 и идентификатор 4 Я не могу отобразить идентификатор пользователя 5, Вид поражен в нем,Нужна помощь.Спасибо

Ответы [ 4 ]

5 голосов
/ 08 ноября 2011

Технически возможно выполнять рекурсивные иерархические запросы в MySQL с использованием хранимых процедур.

Вот один вариант, адаптированный к вашему сценарию:

CREATE TABLE `user` (
  `UserID` int(16) unsigned NOT NULL,
  `UserName` varchar(32),
  `ParentID` int(16) DEFAULT NULL,
  `TopID` int(16) DEFAULT NULL,
  PRIMARY KEY (`UserID`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;

INSERT INTO user VALUES (1, 'abc', NULL, NULL), (2, 'edf', 1, 1), (3, 'gef', 1, 1), 
 (4, 'huj', 3, 1), (5, 'jdi', 4, 1), (6, 'das', 2, 1), (7, 'new', NULL, NULL), 
 (8, 'gka', 7, 7);

DELIMITER $$
DROP PROCEDURE IF EXISTS `Hierarchy` $$
CREATE PROCEDURE `Hierarchy` (IN GivenID INT, IN initial INT)
BEGIN
    DECLARE done INT DEFAULT 0;
    DECLARE next_id INT;

    -- CURSOR TO LOOP THROUGH RESULTS --
    DECLARE cur1 CURSOR FOR SELECT UserID FROM user WHERE ParentID = GivenID;
    DECLARE CONTINUE HANDLER FOR NOT FOUND SET done = 1;

    -- CREATE A TEMPORARY TABLE TO HOLD RESULTS --
    IF initial=1 THEN
        -- MAKE SURE TABLE DOESN'T CONTAIN OUTDATED INFO IF IT EXISTS (USUALLY ON ERROR) --
        DROP TABLE IF EXISTS OUT_TEMP; 
        CREATE TEMPORARY TABLE OUT_TEMP (userID int, UserName varchar(32));
    END IF;

    -- ADD OURSELF TO THE TEMPORARY TABLE --
    INSERT INTO OUT_TEMP SELECT UserID, UserName FROM user WHERE UserID = GivenID;

    -- AND LOOP THROUGH THE CURSOR --
    OPEN cur1;
    read_loop: LOOP
        FETCH cur1 INTO next_id;

        -- NO ROWS FOUND, LEAVE LOOP --
        IF done THEN
        LEAVE read_loop;
        END IF;

        -- NEXT ROUND --
        CALL Hierarchy(next_id, 0);     
    END LOOP;

    CLOSE cur1;

    -- THIS IS THE INITIAL CALL, LET'S GET THE RESULTS --
    IF initial=1 THEN
    SELECT * FROM OUT_TEMP;
        -- CLEAN UP AFTER OURSELVES --
        DROP TABLE OUT_TEMP; 
    END IF;
END $$
DELIMITER ;

CALL Hierarchy(3,1);
+--------+----------+
| userID | UserName |
+--------+----------+
|      3 | gef      |
|      4 | huj      |
|      5 | jdi      |
+--------+----------+
3 rows in set (0.07 sec)

Query OK, 0 rows affected (0.07 sec)

CALL Hierarchy(1,1);
+--------+----------+
| userID | UserName |
+--------+----------+
|      1 | abc      |
|      2 | edf      |
|      6 | das      |
|      3 | gef      |
|      4 | huj      |
|      5 | jdi      |
+--------+----------+
6 rows in set (0.10 sec)

Query OK, 0 rows affected (0.10 sec)

Время указать на некоторые предостережения :

  • С тех поррекурсивно вызывает хранимую процедуру, вам нужно увеличить размер max_sp_recursion_depth , максимальное значение которого равно 255 (по умолчанию 0).

  • Мои результаты нанезанятый сервер с ограниченным количеством тестовых данных (10 кортежей из таблицы user) занял 0,07–0,10 секунды.Производительность такова, что может быть лучше поместить рекурсию на уровень приложения.

  • Я не воспользовался вашим столбцом TopID, так что может быть логическая ошибка,Но два контрольных примера дали мне ожидаемые результаты.

Отказ от ответственности: Этот пример был просто для того, чтобы показать, что можно сделать в MySQL, а нечто я в любом случае одобряю это.Хранимые процедуры, временные таблицы и курсоры, возможно, не лучший способ решить эту проблему.

2 голосов
/ 03 ноября 2011

Ну, не совсем чистая реализация, но, так как вам нужны только дочерние и дочерние элементы, может сработать любой из них:

Query1:

SELECT UserID, UserName
FROM tbl_user
WHERE ParentID = 3 OR UserID = 3
UNION
SELECT UserID, UserName
FROM tbl_user
WHERE ParentID IN (SELECT UserID
FROM tbl_user
WHERE ParentID = 3);

Запрос 2:

SELECT UserID, UserName
FROM tbl_user 
WHERE UserID = 3
OR ParentID = 3
OR ParentID IN (SELECT UserID
    FROM tbl_user
    WHERE ParentID = 3);

РЕДАКТИРОВАТЬ 1: В качестве альтернативы вы можете изменить структуру таблицы, чтобы было удобнее запрашивать все дочерние элементы определенной категории. Пожалуйста, перейдите по этой ссылке, чтобы узнать больше о хранении иерархических данных в MySQL .

Кроме того, вы можете подумать о иерархическом хранении ваших данных в виде дерева, что очень хорошо объяснено в этой статье .

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

1 голос
/ 11 ноября 2011

Это одна из лучших статей, которые я видел для объяснения метода «Модифицированного обхода дерева предзаказов» для хранения древовидных данных в базе данных в стиле SQL.

http://www.sitepoint.com/hierarchical-data-database/

Материал MPTT начинается на странице 2.

По сути, вы сохраняете значения "Left" и "Right" для каждого узла в дереве таким образом, чтобы получить все дочерние элементы ParentA, вы получаете левое и правое для ParentA, затем

SELECT * 
FROM TableName
WHERE Left > ParentLeft 
AND Right < ParentRight
0 голосов
/ 11 ноября 2011

Чтобы получить всех родителей выбранного ребенка (user_id = 3 в этом примере):

<pre> SELECT @parent_id AS _user_id, user_name, ( SELECT @parent_id := parent_id FROM users WHERE user_id = _user_id ) AS parent FROM ( -- initialize variables SELECT @parent_id := 3 ) vars, users u WHERE @parent_id <> 0;</p> <p>

Чтобы получить всех детей выбранного user_id

<pre> SELECT ui.user_id AS 'user_id', ui.user_name AS 'user_name', parent_id, FROM ( SELECT connect_by_parent(user_id) AS user_id FROM ( SELECT @start_user := 3, @user_id := @start_user ) vars, users WHERE @user_id IS NOT NULL ) uo JOIN users ui ON ui.user_id = uo.user_id

Для этого требуется следующая функция

<pre> CREATE FUNCTION connect_by_parent(value INT) RETURNS INT NOT DETERMINISTIC READS SQL DATA BEGIN DECLARE _user_id INT; DECLARE _parent_id INT; DECLARE _next INT; DECLARE CONTINUE HANDLER FOR NOT FOUND SET @user_id = NULL;</p> <pre><code>SET _parent_id = @user_id; SET _user_id = -1; IF @user_id IS NULL THEN RETURN NULL; END IF; LOOP SELECT MIN(user_id) INTO @user_id FROM users WHERE parent_id = _parent_id AND user_id > _user_id; IF @user_id IS NOT NULL OR _parent_id = @start_with THEN RETURN @user_id; END IF; SELECT user_id, parent_id INTO _user_id, _parent_id FROM users WHERE user_id = _parent_id; END LOOP;

END

В этом примере интенсивно используются переменные сеанса, с которыми многие пользователи sql могут быть незнакомы, поэтому вот ссылка, которая может дать некоторое представление: переменные сеанса

...