Можно ли сделать рекурсивный SQL-запрос в MySQL [duplicate]

Любой набор переменных также может быть завершен в классе. Переменные «Variable» могут быть добавлены к экземпляру класса во время выполнения, напрямую обращаясь к встроенному словарю через атрибут __dict__.

Следующий код определяет класс Variables, который добавляет переменные (в этом случае атрибуты) к своему экземпляру во время построения. Имена переменных берутся из указанного списка (который, например, мог быть сгенерирован программным кодом):

# some list of variable names
L = ['a', 'b', 'c']

class Variables:
    def __init__(self, L):
        for item in L:
            self.__dict__[item] = 100

v = Variables(L)
print(v.a, v.b, v.c)
#will produce 100 100 100
75
задан shA.t 31 May 2015 в 11:10
поделиться

5 ответов

Редактировать

Решение, упомянутое @leftclickben, также эффективно. Мы также можем использовать хранимую процедуру для того же самого.

CREATE PROCEDURE get_tree(IN id int)
 BEGIN
 DECLARE child_id int;
 DECLARE prev_id int;
 SET prev_id = id;
 SET child_id=0;
 SELECT col3 into child_id 
 FROM table1 WHERE col1=id ;
 create TEMPORARY  table IF NOT EXISTS temp_table as (select * from table1 where 1=0);
 truncate table temp_table;
 WHILE child_id <> 0 DO
   insert into temp_table select * from table1 WHERE col1=prev_id;
   SET prev_id = child_id;
   SET child_id=0;
   SELECT col3 into child_id
   FROM TABLE1 WHERE col1=prev_id;
 END WHILE;
 select * from temp_table;
 END //

Мы используем временную таблицу для хранения результатов вывода и, поскольку временные таблицы основаны на сеансах, мы не будем иметь никаких проблем относительно выходные данные неверны.

SQL FIDDLE Demo Попробуйте этот запрос:

SELECT 
    col1, col2, @pv := col3 as 'col3' 
FROM 
    table1
JOIN 
    (SELECT @pv := 1) tmp
WHERE 
    col1 = @pv

SQL FIDDLE Demo :

| COL1 | COL2 | COL3 |
+------+------+------+
|    1 |    a |    5 |
|    5 |    d |    3 |
|    3 |    k |    7 |

Примечание. Значение parent_id должно быть меньше, чем child_id для этого решения.

66
ответ дан Meherzad 21 August 2018 в 22:15
поделиться
  • 1
    Люди Pls отмечают этот ответ как оптимальное решение, поскольку некоторые другие решения аналогичного вопроса (о Recursive Select в mysql) довольно сложны, так как для этого требуется создать таблицу & amp; вставлять в него данные. Это решение очень элегантно. – Tum 13 May 2013 в 05:18
  • 2
    Просто позаботьтесь о его решении, нет зависимости от типа цикла, тогда он перейдет в бесконечный цикл, и еще одна вещь, что он найдет только 1 запись этого типа col3, поэтому, если есть несколько записей, то это не сработает. – Meherzad 13 May 2013 в 05:54
  • 3
    @HamidSarfraz теперь работает sqlfiddle.com / #! 2 / 74f457 / 14 . Это будет работать для вас. Поскольку для последовательного поиска и id всегда будет иметь большее значение, чем родительский, поскольку сначала необходимо создать родительский элемент. Pl сообщите, если вам нужны дополнительные данные. – Meherzad 1 February 2015 в 06:39
  • 4
  • 5
    Tum Я знаю, как работает рекурсивный SQL. MySQL не реализовал рекурсивные CTE, поэтому один из возможных вариантов - тот, который вы указали (используя хранимые процедуры / функции). В другом случае используются переменные mysql. Однако ответ здесь не изящный, а наоборот, просто ужасный. Он не отображает рекурсивный SQL. Если это сработало в вашем случае, это произошло только случайно, как правильно указал @jaehung. И я не против ужасных ответов. Я просто опускаю их. Но ужасный ответ на +50, я не против. – ypercubeᵀᴹ 30 November 2015 в 12:02
15
ответ дан BoB3K 21 August 2018 в 22:15
поделиться

Принимаемый ответ @Meherzad работает только в том случае, если данные находятся в определенном порядке. Это происходит с данными из вопроса ОП. В моем случае мне пришлось изменить его для работы с моими данными.

Примечание. Это работает только тогда, когда каждый идентификатор записи (col1 в вопросе) имеет значение БОЛЬШЕ, чем этот «родительский идентификатор» записи ( col3 в вопросе). Это часто бывает так, потому что обычно родитель должен быть создан первым. Однако, если ваше приложение допускает изменения в иерархии, где элемент может быть повторно зарегистрирован в другом месте, тогда вы не можете полагаться на это.

Это мой запрос, если он кому-то помогает; обратите внимание, что это не работает с данным вопросом, потому что данные не соответствуют требуемой структуре, описанной выше.

select t.col1, t.col2, @pv := t.col3 col3
from (select * from table1 order by col1 desc) t
join (select @pv := 1) tmp
where t.col1 = @pv

Разница в том, что table1 упорядочивается по col1, так что родитель будет после него (поскольку значение родительского col1 меньше, чем у ребенка).

49
ответ дан leftclickben 21 August 2018 в 22:15
поделиться
  • 1
    u right, также если у ребенка есть 2 родителя, тогда он может не выбрать оба – Tum 23 July 2014 в 16:25
  • 2
    Спасибо чувак. Командная работа сделала свой поступок в этом посте! Я получил его для работы, когда я изменил значение @pv. Именно этого я и искал. – Mohamed Ennahdi El Idrissi 14 September 2015 в 12:55
  • 3
    Что делать, если я хочу использовать это как столбец group_concat родительских идентификаторов для каждой строки большего размера (что означает, что значение переменной @pv должно быть динамическим для каждой строки). Присоединение в подзапросе не знает главный столбец (к которому я пытаюсь подключиться), используя другую переменную, в которой он тоже не работает (всегда возвращает NULL) – qdev 22 October 2015 в 12:27
  • 4
  • 5
    Что вы думаете о новом ответе, который я написал? Не то, чтобы ваше не было хорошо, но я хотел иметь только SELECT, который мог бы поддерживать родительский id & gt; дочерний идентификатор. – Master DJon 23 June 2016 в 13:29

Если вы хотите иметь SELECT без проблем с идентификатором родительского идентификатора, который должен быть ниже id ребенка, можно использовать функцию. Он поддерживает также несколько дочерних элементов (как дерево должно делать), и дерево может иметь несколько головок. Он также обеспечивает разрыв, если в данных существует цикл.

Я хотел использовать динамический SQL, чтобы иметь возможность передавать имена таблиц / столбцов, но функции в MySQL не поддерживают это.

DELIMITER $$

CREATE FUNCTION `isSubElement`(pParentId INT, pId INT) RETURNS int(11)
DETERMINISTIC    
READS SQL DATA
BEGIN
DECLARE isChild,curId,curParent,lastParent int;
SET isChild = 0;
SET curId = pId;
SET curParent = -1;
SET lastParent = -2;

WHILE lastParent <> curParent AND curParent <> 0 AND curId <> -1 AND curParent <> pId AND isChild = 0 DO
    SET lastParent = curParent;
    SELECT ParentId from `test` where id=curId limit 1 into curParent;

    IF curParent = pParentId THEN
        SET isChild = 1;
    END IF;
    SET curId = curParent;
END WHILE;

RETURN isChild;
END$$

Здесь таблица test должна быть изменена до имени реальной таблицы, а столбцы (ParentId, Id), возможно, придется скорректировать для ваших настоящих имен.

Использование:

SET @wantedSubTreeId = 3;
SELECT * FROM test WHERE isSubElement(@wantedSubTreeId,id) = 1 OR ID = @wantedSubTreeId;

Результат:

3   7   k
5   3   d
9   3   f
1   5   a

SQL для создания теста:

CREATE TABLE IF NOT EXISTS `test` (
  `Id` int(11) NOT NULL,
  `ParentId` int(11) DEFAULT NULL,
  `Name` varchar(300) NOT NULL,
  PRIMARY KEY (`Id`)
) ENGINE=InnoDB  DEFAULT CHARSET=latin1;

insert into test (id, parentid, name) values(3,7,'k');
insert into test (id, parentid, name) values(5,3,'d');
insert into test (id, parentid, name) values(9,3,'f');
insert into test (id, parentid, name) values(1,5,'a');
insert into test (id, parentid, name) values(6,2,'o');
insert into test (id, parentid, name) values(2,8,'c');

EDIT: здесь скрипт проверить его самостоятельно. Это заставило меня изменить разделитель с помощью предопределенного, но он работает.

6
ответ дан Master DJon 21 August 2018 в 22:15
поделиться

Сохраненная процедура - лучший способ сделать это. Поскольку решение Мехерзада будет работать только в том случае, если данные будут следовать одному и тому же порядку.

Если у нас есть такая структура таблицы

col1 | col2 | col3
-----+------+------
 3   | k    | 7
 5   | d    | 3
 1   | a    | 5
 6   | o    | 2
 2   | 0    | 8

, она не будет работать. SQL Fiddle Demo

Вот пример кода процедуры для достижения того же.

delimiter //
CREATE PROCEDURE chainReaction 
(
    in inputNo int
) 
BEGIN 
    declare final_id int default NULL;
    SELECT col3 
    INTO final_id 
    FROM table1
    WHERE col1 = inputNo;
    IF( final_id is not null) THEN
        INSERT INTO results(SELECT col1, col2, col3 FROM table1 WHERE col1 = inputNo);
        CALL chainReaction(final_id);   
    end if;
END//
delimiter ;

call chainReaction(1);
SELECT * FROM results;
DROP TABLE if exists results;
7
ответ дан shA.t 21 August 2018 в 22:15
поделиться
  • 1
    Это надежное решение, и я использую его без проблем. Можете ли вы мне помочь, когда идете в другом направлении, то есть вниз по дереву - я нахожу все строки, где родительский идентификатор == inputNo, но у многих идентификаторов может быть один родительский идентификатор. – mils 26 October 2015 в 00:55
Другие вопросы по тегам:

Похожие вопросы: