У меня есть таблица MySQL, которая выглядит следующим образом:
id | name | parent_id
19 | category1 | 0
20 | category2 | 19
21 | category3 | 20
22 | category4 | 21
......
Теперь я хочу иметь один запрос MySQL, для которого я просто предоставляю идентификатор [например, скажем 'id = 19'], тогда я должен получить все его дочерние идентификаторы [т.е. результат должен иметь идентификаторы '20, 21,22 ']. ... Кроме того, иерархия детей неизвестна, она может варьироваться ....
Кроме того, у меня уже есть решение с использованием цикла for ..... Дайте мне знать, как добиться того же, используя один запрос MySQL, если это возможно.
mysql
sql
hierarchical-data
recursive-query
Тарун Парсвани
источник
источник
Ответы:
Для MySQL 8+: используйте рекурсивный
with
синтаксис.Для MySQL 5.x: используйте встроенные переменные, идентификаторы пути или самостоятельные соединения.
MySQL 8+
Значение, указанное в,
parent_id = 19
должно быть установлено наid
родительского элемента, для которого вы хотите выбрать всех потомков.MySQL 5.x
Для версий MySQL, которые не поддерживают Common Table Expressions (до версии 5.7), этого можно достичь с помощью следующего запроса:
Вот скрипка .
Здесь значение, указанное в,
@pv := '19'
должно быть установлено наid
родительского элемента, для которого вы хотите выбрать всех потомков.Это также будет работать, если у родителя несколько детей. Однако требуется, чтобы каждая запись удовлетворяла условию
parent_id < id
, иначе результаты не будут полными.Переменные в запросе
Этот запрос использует определенный синтаксис MySQL: переменные назначаются и изменяются во время его выполнения. Некоторые предположения сделаны относительно порядка исполнения:
from
оценивается первым. Так вот где@pv
инициализируется.where
оценивается для каждой записи в порядке извлечения изfrom
псевдонимов. Таким образом, именно здесь ставится условие включения только тех записей, для которых родительский объект уже был идентифицирован как находящийся в дереве потомков (все потомки первичного родителя постепенно добавляются@pv
).where
пункте оцениваются по порядку, и оценка прерывается, как только общий результат определен. Поэтому второе условие должно быть на втором месте, так как оно добавляетid
к родительскому списку, и это должно произойти, только еслиid
проходит первое условие.length
Функция вызывается только , чтобы убедиться , что это условие всегда истинно, даже еслиpv
строка будет по какой - то причине дают falsy значение.В целом, эти предположения могут оказаться слишком рискованными, чтобы на них можно было положиться. Документация предупреждает:
Таким образом, даже несмотря на то, что он работает в соответствии с вышеуказанным запросом, порядок оценки может все еще изменяться, например, когда вы добавляете условия или используете этот запрос в качестве представления или подзапроса в большем запросе. Это «особенность», которая будет удалена в будущем выпуске MySQL :
Как указано выше, начиная с MySQL 8.0, вы должны использовать рекурсивный
with
синтаксис.КПД
Для очень больших наборов данных это решение может стать медленным, поскольку
find_in_set
операция не является наиболее идеальным способом поиска числа в списке, конечно же, не в списке, размер которого достигает такого же порядка, как число возвращаемых записей.Альтернатива 1:
with recursive
,connect by
Все больше и больше баз данных реализуют стандартный
WITH [RECURSIVE]
синтаксис SQL: 1999 ISO для рекурсивных запросов (например, Postgres 8.4+ , SQL Server 2005+ , DB2 , Oracle 11gR2 + , SQLite 3.8.4+ , Firebird 2.1+ , H2 , HyperSQL 2.1.0+ , Teradata , MariaDB 10.2.2+ ). Начиная с версии 8.0, MySQL также поддерживает это . Смотрите верхнюю часть этого ответа для синтаксиса, чтобы использовать.Некоторые базы данных имеют альтернативный нестандартный синтаксис для иерархического поиска, такой как
CONNECT BY
предложение, доступное в Oracle , DB2 , Informix , CUBRID и других базах данных.MySQL версии 5.7 не предлагает такую функцию. Когда ваш движок базы данных предоставляет этот синтаксис или вы можете перейти на тот, который это делает, тогда это, безусловно, лучший вариант. Если нет, то также рассмотрите следующие альтернативы.
Альтернатива 2: Идентификаторы стиля пути
Все станет намного проще, если вы назначите
id
значения, которые содержат иерархическую информацию: путь. Например, в вашем случае это может выглядеть так:Тогда ваш
select
будет выглядеть так:Альтернатива 3: повторное самостоятельное соединение
Если вы знаете верхний предел того, насколько глубоким может стать ваше дерево иерархии, вы можете использовать стандартный
sql
запрос, подобный следующему:Смотрите эту скрипку
В
where
состояние указывает , какой родитель вы хотите получить потомков. Вы можете расширить этот запрос с большим количеством уровней по мере необходимости.источник
parent_id > id
вы не можете использовать это решение.WITH RECURSIVE
метод, я нашел следующую статью действительно полезной с различными сценариями, такими как глубина рекурсии, различия, циклы обнаружения и закрытияИз блога Управление иерархическими данными в MySQL
Структура таблицы
Запрос:
Вывод
Большинство пользователей в то или иное время имели дело с иерархическими данными в базе данных SQL и, несомненно, узнали, что управление иерархическими данными - это не то, для чего предназначена реляционная база данных. Таблицы реляционной базы данных не являются иерархическими (например, XML), а представляют собой просто плоский список. Иерархические данные имеют родительско-дочерние отношения, которые естественным образом не представлены в таблице реляционной базы данных. Читать далее
Обратитесь к блогу для более подробной информации.
РЕДАКТИРОВАТЬ:
Вывод:
Ссылка: Как сделать рекурсивный запрос SELECT в Mysql?
источник
Попробуйте эти:
Определение таблицы:
Экспериментальные ряды:
Хранимая рекурсивная процедура:
Функция обертки для хранимой процедуры:
Выберите пример:
Вывод:
Фильтрация строк по определенному пути:
Вывод:
источник
(20, 'category2', 19), (21, 'category3', 20), (22, 'category4', 20),
Лучший подход, который я придумал,
Линейный подход descr. можно найти где угодно, например здесь или здесь . Что касается функции - это то, что поразило меня.
В итоге - получилось более-менее простое, относительно быстрое и ПРОСТОЕ решение.
Тело функции
И тогда ты просто
Надеюсь, это поможет кому-нибудь :)
источник
Сделал то же самое для другой очереди здесь
Mysql выберите рекурсивный получить все дочерние с несколькими уровнями
Запрос будет:
источник
SELECT idFolder, (SELECT GROUP_CONCAT(lv SEPARATOR ',') FROM ( SELECT @pv:=(SELECT GROUP_CONCAT(idFolder SEPARATOR ',') FROM Folder WHERE idFolderParent IN (@pv)) AS lv FROM Folder JOIN (SELECT @pv:= F1.idFolder )tmp WHERE idFolderParent IN (@pv)) a) from folder F1 where id > 10
; Я не могу отослать F1.idFolder для @pvNULL
. Вы знаете, почему это может быть? Существуют ли предпосылки с точки зрения механизма базы данных или что-то изменилось с тех пор, как вы сделали этот ответ, что делает этот запрос устаревшим?Если вам нужна быстрая скорость чтения, лучше всего использовать закрывающую таблицу. Закрывающая таблица содержит строку для каждой пары предок / потомок. Так что в вашем примере таблица закрытия будет выглядеть
Если у вас есть эта таблица, иерархические запросы становятся очень простыми и быстрыми. Чтобы получить всех потомков категории 20:
Конечно, есть большие недостатки, когда вы используете денормализованные данные, как это. Вы должны поддерживать таблицу закрытия рядом с таблицей категорий. Лучше всего, вероятно, использовать триггеры, но довольно сложно правильно отслеживать вставки / обновления / удаления для таблиц закрытия. Как и во всем, вам нужно посмотреть на ваши требования и решить, какой подход лучше для вас.
редактировать : см. Вопрос Каковы варианты хранения иерархических данных в реляционной базе данных? для большего количества вариантов. Существуют разные оптимальные решения для разных ситуаций.
источник
Простой запрос для перечисления потомков первой рекурсии:
Результат:
... с левым соединением:
Решение @tincot перечислить все детские:
Протестируйте его онлайн с Sql Fiddle и посмотрите все результаты.
http://sqlfiddle.com/#!9/a318e3/4/0
источник
Вы можете сделать это таким же образом в других базах данных с помощью рекурсивного запроса (YMMV по производительности).
Другой способ сделать это - сохранить два дополнительных бита данных, левое и правое значение. Левое и правое значение получаются из предварительного обхода древовидной структуры, которую вы представляете.
Это называется измененным обходом дерева предзаказа и позволяет вам выполнить простой запрос, чтобы получить все родительские значения одновременно. Он также называется «вложенный набор».
источник
Просто используйте класс php BlueM / tree для создания дерева таблицы отношений в mysql.
Вот пример использования BlueM / tree:
источник
Это таблица категорий .
Вывод::
источник
Это немного сложно, проверьте, работает ли он на вас
Ссылка на скрипку SQL http://www.sqlfiddle.com/#!2/e3cdf/2
Замените на ваше поле и имя таблицы соответственно.
источник
Кое-что, не упомянутое здесь, хотя и немного похожее на второй вариант принятого ответа, но отличающееся и дешевое для большого запроса иерархии и простых (вставка, удаление, удаление) элементов, будет добавлять столбец постоянного пути для каждого элемента.
некоторые как:
Пример:
Оптимизация длины пути и
ORDER BY path
использование кодировки base36 вместо реального числового идентификатора путиhttps://en.wikipedia.org/wiki/Base36
Подавление также разделителя косой черты '/' с помощью фиксированной длины и дополнения к закодированному идентификатору
Подробное описание оптимизации здесь: https://bojanz.wordpress.com/2014/04/25/storing-hierarchical-data-materialized-path/
ДЕЛАТЬ
построение функции или процедуры для разделения пути для предков-ретриверов одного элемента
источник
base36
Это работает для меня, надеюсь, это будет работать и для вас. Это даст вам Record set Root to Child для любого конкретного меню. Измените имя поля в соответствии с вашими требованиями.
источник
Мне было легче:
1) создать функцию, которая будет проверять, находится ли элемент где-либо в родительской иерархии другого элемента. Примерно так (я не буду писать функцию, сделайте это с помощью WHILE DO):
в вашем примере
2) использовать суб-выбор, что-то вроде этого:
источник
Я сделал запрос для вас. Это даст вам рекурсивную категорию с одним запросом:
Вот скрипка .
источник