Mysql
 sql >> Cơ Sở Dữ Liệu >  >> RDS >> Mysql

Làm thế nào để tạo một truy vấn đệ quy phân cấp MySQL?

Đối với MySQL 8+: sử dụng đệ quy with cú pháp.
Đối với MySQL 5.x: sử dụng các biến nội tuyến, ID đường dẫn hoặc tự tham gia.

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;

Giá trị được chỉ định trong parent_id = 19 nên được đặt thành id của cha mẹ mà bạn muốn chọn tất cả các con của.

MySQL 5.x

Đối với các phiên bản MySQL không hỗ trợ Biểu thức bảng chung (lên đến phiên bản 5.7), bạn sẽ đạt được điều này với truy vấn sau:

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))

Đây là fiddle .

Đây, giá trị được chỉ định trong @pv := '19' nên được đặt thành id của cha mẹ mà bạn muốn chọn tất cả các con của.

Điều này cũng sẽ hoạt động nếu cha mẹ có nhiều bọn trẻ. Tuy nhiên, mỗi bản ghi phải đáp ứng điều kiện parent_id < id , nếu không kết quả sẽ không đầy đủ.

Các phép gán biến bên trong một truy vấn

Truy vấn này sử dụng cú pháp MySQL cụ thể:các biến được gán và sửa đổi trong quá trình thực thi của nó. Một số giả định được đưa ra về thứ tự thực hiện:

  • from mệnh đề được đánh giá đầu tiên. Vì vậy, đó là nơi @pv được khởi tạo.
  • where mệnh đề được đánh giá cho mỗi bản ghi theo thứ tự truy xuất từ ​​from bí danh. Vì vậy, đây là nơi đặt một điều kiện để chỉ bao gồm các bản ghi mà cây mẹ đã được xác định là ở trong cây con (tất cả các con của cây mẹ chính được thêm dần vào @pv ).
  • Các điều kiện trong where này mệnh đề được đánh giá theo thứ tự và quá trình đánh giá bị gián đoạn khi kết quả tổng thể là chắc chắn. Do đó, điều kiện thứ hai phải ở vị trí thứ hai, vì nó thêm id vào danh sách mẹ và điều này chỉ xảy ra nếu id vượt qua điều kiện đầu tiên. length hàm chỉ được gọi để đảm bảo điều kiện này luôn đúng, ngay cả khi pv vì lý do nào đó chuỗi sẽ mang lại giá trị giả.

Nhìn chung, người ta có thể thấy những giả định này quá rủi ro để dựa vào. tài liệu cảnh báo:

bạn có thể nhận được kết quả như mong đợi, nhưng điều này không được đảm bảo [...] thứ tự đánh giá cho các biểu thức liên quan đến biến người dùng là không xác định.

Vì vậy, mặc dù nó hoạt động nhất quán với truy vấn trên, thứ tự đánh giá vẫn có thể thay đổi, chẳng hạn như khi bạn thêm điều kiện hoặc sử dụng truy vấn này làm dạng xem hoặc truy vấn phụ trong một truy vấn lớn hơn. Đây là một "tính năng" mà sẽ bị xóa trong tương lai Bản phát hành MySQL :

Các bản phát hành trước của MySQL giúp có thể gán giá trị cho biến người dùng trong các câu lệnh khác với SET . Chức năng này được hỗ trợ trong MySQL 8.0 để tương thích ngược nhưng có thể bị xóa trong bản phát hành MySQL trong tương lai.

Như đã nêu ở trên, từ MySQL 8.0 trở đi, bạn nên sử dụng with cú pháp.

Hiệu quả

Đối với các tập dữ liệu rất lớn, giải pháp này có thể chạy chậm, vì find_in_set hoạt động không phải là cách lý tưởng nhất để tìm một số trong danh sách, chắc chắn không phải trong danh sách đạt kích thước theo cùng thứ tự độ lớn với số lượng bản ghi được trả về.

Phương án 1:with đệ quy , connect by

Ngày càng nhiều cơ sở dữ liệu triển khai tiêu chuẩn ISO SQL:1999 WITH [RECURSIVE] cú pháp cho các truy vấn đệ quy (ví dụ: 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+ ). Và kể từ phiên bản 8.0, MySQL cũng hỗ trợ nó . Xem phần trên cùng của câu trả lời này để biết cú pháp sử dụng.

Một số cơ sở dữ liệu có cú pháp thay thế, không chuẩn để tra cứu thứ bậc, chẳng hạn như CONNECT BY mệnh đề có sẵn trên Oracle , DB2 , Informix , CUBRID và các cơ sở dữ liệu khác.

MySQL phiên bản 5.7 không cung cấp một tính năng như vậy. Khi công cụ cơ sở dữ liệu của bạn cung cấp cú pháp này hoặc bạn có thể chuyển sang một cú pháp có, thì đó chắc chắn là lựa chọn tốt nhất để sử dụng. Nếu không, hãy xem xét các lựa chọn thay thế sau.

Thay thế 2:Số nhận dạng kiểu đường dẫn

Mọi thứ trở nên dễ dàng hơn rất nhiều nếu bạn chỉ định id các giá trị chứa thông tin phân cấp:một đường dẫn. Ví dụ:trong trường hợp của bạn, nó có thể trông như thế này:

ID TÊN
19 category1
19/1 category2
19/1/1 category3
19/1/1/1 category4

Sau đó, select của bạn sẽ giống như thế này:

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

Phương án 3:Tự tham gia lặp lại

Nếu bạn biết giới hạn trên về độ sâu của cây phân cấp, bạn có thể sử dụng sql tiêu chuẩn truy vấn như thế này:

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;

Xem fiddle này

where điều kiện chỉ định cha mẹ nào bạn muốn truy xuất con cháu của. Bạn có thể mở rộng truy vấn này với nhiều cấp hơn nếu cần.



  1. Database
  2.   
  3. Mysql
  4.   
  5. Oracle
  6.   
  7. Sqlserver
  8.   
  9. PostgreSQL
  10.   
  11. Access
  12.   
  13. SQLite
  14.   
  15. MariaDB
  1. Cách xác minh bản sao lưu MySQL của bạn với ClusterControl

  2. Truy vấn SELECT trả về 1 hàng từ mỗi nhóm

  3. LINQ to Entities không nhận dạng được phương thức 'System.String ToString ()' của phương thức và phương thức này không thể được dịch thành một biểu thức lưu trữ

  4. Chuỗi được phân tách bằng dấu phẩy trong MySQL thành bảng tạm thời

  5. FIND_IN_SET () so với IN ()