Mysql
 sql >> база данни >  >> RDS >> Mysql

Как да създадете MySQL йерархична рекурсивна заявка?

За MySQL 8+: използвайте рекурсивния with синтаксис.
За MySQL 5.x: използвайте вградени променливи, идентификатори на пътеки или самообединявания.

MySQL 8+

with recursive cte (id, name, parent_id) as (
  select     id,
             name,
             parent_id
  from       products
  where      parent_id = 19
  union all
  select     p.id,
             p.name,
             p.parent_id
  from       products p
  inner join cte
          on p.parent_id = cte.id
)
select * from cte;

Стойността, посочена в parent_id =19 трябва да бъде зададен на id на родителя, на когото искате да изберете всички потомци.

MySQL 5.x

За версиите на MySQL, които не поддържат общи изрази за таблица (до версия 5.7), ще постигнете това със следната заявка:

select  id,
        name,
        parent_id 
from    (select * from products
         order by parent_id, id) products_sorted,
        (select @pv := '19') initialisation
where   find_in_set(parent_id, @pv)
and     length(@pv := concat(@pv, ',', id))

Ето цигулка .

Тук стойността, посочена в @pv :='19' трябва да бъде зададен на id на родителя, на когото искате да изберете всички потомци.

Това ще работи и ако един родител има множество деца. Изисква се обаче всеки запис да отговаря на условието parent_id , в противен случай резултатите няма да бъдат пълни.

Присвояване на променливи вътре в заявка

Тази заявка използва специфичен MySQL синтаксис:променливите се присвояват и променят по време на нейното изпълнение. Направени са някои предположения относно реда на изпълнение:

  • от клаузата се оценява първо. И така, това е мястото, където @pv се инициализира.
  • къде Клаузата се оценява за всеки запис в реда на извличане от от псевдоними. Така че тук е поставено условие да включва само записи, за които родителят вече е идентифициран като в дървото на потомците (всички потомци на основния родител постепенно се добавят към @pv ).
  • Условията в това къде клаузите се оценяват по ред и оценката се прекъсва, след като общият резултат е сигурен. Следователно второто условие трябва да бъде на второ място, тъй като добавя id към родителския списък и това трябва да се случи само ако id преминава първото условие. дължината функцията се извиква само, за да се увери, че това условие винаги е вярно, дори ако pv низ по някаква причина ще даде фалшива стойност.

Като цяло тези предположения могат да се намерят за твърде рискови, за да се разчита. документацията предупреждава:

може да получите резултатите, които очаквате, но това не е гарантирано [...] редът на оценка за изрази, включващи потребителски променливи, е недефиниран.

Така че, въпреки че работи последователно с горната заявка, редът за оценка все още може да се промени, например, когато добавите условия или използвате тази заявка като изглед или подзаявка в по-голяма заявка. Това е „функция“, която ще бъде премахната в бъдеще Издание на MySQL :

Предишните версии на MySQL направиха възможно присвояването на стойност на потребителска променлива в изрази, различни от SET . Тази функционалност се поддържа в MySQL 8.0 за обратна съвместимост, но подлежи на премахване в бъдеща версия на MySQL.

Както беше посочено по-горе, от MySQL 8.0 нататък трябва да използвате рекурсивния with синтаксис.

Ефективност

За много големи набори от данни това решение може да стане бавно, тъй като намери_в_набор операцията не е най-идеалният начин за намиране на число в списък, със сигурност не в списък, който достига размер от същия порядък като броя на върнатите записи.

Алтернатива 1:с рекурсивно , свързване чрез

Все повече и повече бази данни прилагат SQL:1999 ISO стандарт WITH [RECURSIVE] синтаксис за рекурсивни заявки (напр. 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 стойности, които съдържат йерархичната информация:път. Например във вашия случай това може да изглежда така:

ID NAME
19 категория1
19/1 категория 2
19/1/1 категория3
19/1/1/1 категория 4

След това изберете ще изглежда така:

select  id,
        name 
from    products
where   id like '19/%'

Алтернатива 3:Многократно самоприсъединяване

Ако знаете горна граница за това колко дълбоко може да стане вашето йерархично дърво, можете да използвате стандартен sql заявка като тази:

select      p6.parent_id as parent6_id,
            p5.parent_id as parent5_id,
            p4.parent_id as parent4_id,
            p3.parent_id as parent3_id,
            p2.parent_id as parent2_id,
            p1.parent_id as parent_id,
            p1.id as product_id,
            p1.name
from        products p1
left join   products p2 on p2.id = p1.parent_id 
left join   products p3 on p3.id = p2.parent_id 
left join   products p4 on p4.id = p3.parent_id  
left join   products p5 on p5.id = p4.parent_id  
left join   products p6 on p6.id = p5.parent_id
where       19 in (p1.parent_id, 
                   p2.parent_id, 
                   p3.parent_id, 
                   p4.parent_id, 
                   p5.parent_id, 
                   p6.parent_id) 
order       by 1, 2, 3, 4, 5, 6, 7;

Вижте тази цигулка

къде условието посочва кой родител искате да извлечете потомците. Можете да разширите тази заявка с повече нива, ако е необходимо.



  1. Database
  2.   
  3. Mysql
  4.   
  5. Oracle
  6.   
  7. Sqlserver
  8.   
  9. PostgreSQL
  10.   
  11. Access
  12.   
  13. SQLite
  14.   
  15. MariaDB
  1. SQLAlchemy ПРИ АКТУАЛИЗИРАНЕ НА ДУБЛИРАН КЛЮЧ

  2. Таблицата е посочена два пъти, и като цел за 'UPDATE' и като отделен източник за данни в mysql

  3. Как да промените паролата на базата данни

  4. Използването на псевдоним на колона в клаузата WHERE на MySQL заявката води до грешка

  5. Създайте временна таблица в израз SELECT без отделна CREATE TABLE