For MySQL 8+: özyinelemeli kullanmak with
sözdizimi.
For MySQL 5.x: kullanım satır içi değişkenler, yol kimlikleri veya kendinden katılır.
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;
İçinde belirtilen değer parent_id = 19
, id
tüm alt öğelerini seçmek istediğiniz üst öğeye ayarlanmalıdır .
MySQL 5.x
Ortak Tablo İfadelerini desteklemeyen MySQL sürümleri için (5.7 sürümüne kadar), bunu aşağıdaki sorgu ile elde edersiniz:
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))
İşte bir keman .
Burada, belirtilen değerin, tüm alt öğelerini seçmek istediğiniz üst öğeye @pv := '19'
ayarlanması gerekir id
.
Bu, bir ebeveynin birden fazla çocuğu varsa da işe yarayacaktır . Ancak, her kaydın koşulu yerine getirmesi gerekir parent_id < id
, aksi takdirde sonuçlar tamamlanmaz.
Bir sorgu içindeki değişken atamalar
Bu sorgu belirli MySQL sözdizimini kullanır: değişkenler yürütme sırasında atanır ve değiştirilir. Yürütme sırası hakkında bazı varsayımlar yapılır:
- Önce
from
madde değerlendirilir. İşte bu noktada @pv
başlatılır.
where
Maddesi ile ilgili alma sırasına göre, her kayıt için değerlendirilir from
takma ad. Bu nedenle, bir koşulun yalnızca üst öğenin zaten soy ağacında olduğu tespit edilen kayıtları içerdiği yer burasıdır (birincil ana öğenin tüm alt öğeleri aşamalı olarak eklenir @pv
).
- Bu
where
fıkradaki koşullar sırayla değerlendirilir ve toplam sonuç belli olduğunda değerlendirme yarıda kesilir. Bu nedenle, ikinci koşul id
, üst listeye eklediği için ikinci sırada olmalıdır ve bu yalnızca id
ilk koşulu geçerse gerçekleşmelidir . Bu length
işlev, pv
dize herhangi bir nedenden dolayı yanlış bir değer verse bile, bu koşulun her zaman doğru olduğundan emin olmak için çağrılır.
Sonuçta, bu varsayımlara güvenilemeyecek kadar riskli olabilir. Dokümantasyon uyarıyor:
beklediğiniz sonuçları alabilirsiniz, ancak bu garanti edilmez [...] kullanıcı değişkenlerini içeren ifadelerin değerlendirme sırası tanımlanmamıştır.
Bu nedenle, yukarıdaki sorgu ile tutarlı bir şekilde çalışmasına rağmen, örneğin koşullar eklediğinizde veya bu sorguyu daha büyük bir sorguda bir görünüm veya alt sorgu olarak kullandığınızda, değerlendirme sırası değişebilir. Gelecekteki bir MySQL sürümünde kaldırılacak bir "özellik" dir :
MySQL'in önceki sürümleri, kullanıcı değişkenlerine, dışındaki ifadelerde bir değer atamayı mümkün kıldı SET
. Bu işlevsellik MySQL 8.0'da geriye dönük uyumluluk için desteklenir, ancak gelecekteki bir MySQL sürümünde kaldırılmaya tabidir.
Yukarıda belirtildiği gibi, MySQL 8.0'dan itibaren özyinelemeli with
sözdizimini kullanmalısınız .
verim
Çok büyük veri kümeleri için bu çözüm yavaşlayabilir, çünkü find_in_set
işlem bir listede bir numara bulmanın en ideal yolu değildir, kesinlikle döndürülen kayıt sayısıyla aynı büyüklükte bir boyuta ulaşan bir listede değil.
Alternatif 1: with recursive
,connect by
Giderek daha fazla veritabanı , özyinelemeli sorgular için SQL: 1999 ISO standart WITH [RECURSIVE]
sözdizimini uygular (örneğin 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+ ). Ve sürüm 8.0 itibariyle, MySQL de bunu destekliyor . Kullanılacak sözdizimi için bu cevabın üst kısmına bakın.
Bazı veritabanları CONNECT BY
, Oracle , DB2 , Informix , CUBRID ve diğer veritabanlarında bulunan hükmü gibi hiyerarşik aramalar için alternatif, standart olmayan bir sözdizimine sahiptir .
MySQL sürüm 5.7 böyle bir özellik sunmuyor. Veritabanı motorunuz bu sözdizimini sağladığında veya bu sözdizimine geçebildiğinizde, bu kesinlikle gitmek için en iyi seçenektir. Değilse, aşağıdaki alternatifleri de göz önünde bulundurun.
Alternatif 2: Yol Stili Tanımlayıcıları
id
Hiyerarşik bilgileri içeren bir değer atamanız durumunda işler çok daha kolay hale gelir : bir yol. Örneğin, sizin durumunuzda bu şöyle görünebilir:
ID | NAME
19 | category1
19/1 | category2
19/1/1 | category3
19/1/1/1 | category4
O zaman select
şöyle görüneceksin:
select id,
name
from products
where id like '19/%'
Alternatif 3: Tekrarlanan Birleşimler
Hiyerarşi ağacınızın ne kadar derin olabileceğinin üst sınırını biliyorsanız, aşağıdaki sql
gibi standart bir sorgu kullanabilirsiniz :
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;
Bu kemanı gör
where
Hangi ana koşulu belirtir sen soyundan almak istiyor. Bu sorguyu gerektiğinde daha fazla düzeyle genişletebilirsiniz.