Hướng dẫn đệ quy trong mysql

Mình sẽ để nguyên bài viết cũ, và làm thêm 1 phần này để follow up. Ngoài việc giữ nguyên hiện trường, thật sự đây cũng là 1 minh chứng rất tuyệt vời cho lợi ích của việc thảo luận và trao đổi kiến thức. Nếu như không có comment của bạn @Midoriniji , chẳng những mình sẽ không biết được thêm một kiến thức mới, mà nhiều khả năng chắc cũng sẽ không quay lại nhìn để tự nhận ra cái sai của mình được. Spoiler trước như thế cho các bạn nào có hứng thú : Trong bài viết trước, cách làm thứ 2 mà mình đã đưa ra đã cho kết quả không chính xác. Giống như đọc truyện trinh thám vậy, một khi đã đọc qua đoạn phá án, cái cảm giác thích thú khi đi tìm câu trả lời sẽ không bao giờ đến với bạn được nữa. Vì thế, nếu bạn muốn tự mình mày mò 1 chút, xin hãy đừng vội đọc bài viết này. Spoiler ahead. You've been warned.

Recursive Query

Trong MySql thì đây là một tính năng mới, mới được đưa vào chỉ từ phiên bản 8.0.1 trờ đi. Có thể vì còn tương đối mới và cũng không phải để phục vụ cho những use-case phổ biến nhất, nên thật sự có vẻ như cũng không có nhiều bài viết dạng guide hay tutorial giúp ta hiểu tính năng này một cách dễ dàng. Đó là tin xấu, còn tin tốt là, ơn giời, trong ngành lập trình này của chúng ta thì muốn tìm hiểu về cái gì cũng bắt nguồn từ documents của nó mà ra hết, nên cũng không có gì đáng ngại lắm. Và càng may mắn hơn, là chúng ta đang có công cụ sẵn trên tay để nghịch rồi, đọc được điều gì, đem ra thử luôn xem hiểu đúng hay sai, đây là cách học [ đủ để sử dụng ] một cách nhanh nhất. Thế thì cùng bắt tay vào làm nào.

Vẫn lại là : Cần lao vi tiên thủ

Người bạn bí ẩn để lại cho chúng ta một câu query

with recursive cte [id, name, sub_id] as [
  select     id,
             name,
             sub_id
  from       users
  where      id = 1
  union all
  select     p.id,
             p.name,
             p.sub_id
  from       users p
  inner join cte
          on p.id = cte.sub_id
]
select * from cte;

rất đáng tiếc là cứ thế vứt vào bộ dữ liệu chúng ta đang có thì nó không chạy được. Hơi đen, nhưng thôi, đời có mấy khi được ăn sẵn dễ thế. Nhìn qua một chút thì có thể thấy, trong bộ dữ liệu mình tạo ra, bảng users không hề có trường sub_id . Tuy nhiên, viết psuedo code [ code để biểu thị suy nghĩ của mình, không phải code để paste vào chạy ] thì chuyện này xảy ra là bình thường. Hình dung của bạn ấy về database đang hơi khác, nếu thích chúng ta có thể sau này quay lại, làm 1 bộ data để áp code trên vào chạy ngon cũng được, cái chính ở đây cần hiểu là ý tứ của người viết. Tuy nhiên thật đáng tiếc, như đã nói mình chưa sử dụng recursive query bao giờ hết, nhưng vẫn còn cái link Wiki ở đó : //en.wikipedia.org/wiki/Hierarchical_and_recursive_queries_in_SQL#Common_table_expression, ít nhất hiểu được cái khái niệm đang nói đến ở đây đã, rồi mới có thể nói chuyện với nhau được. Thông tin thì chắc chắn từ wiki đi ra, cần thứ gì cũng có rồi, có điều đọc hết mớ tài liệu lí thuyết thì chắc chắn là tốt nhất rồi, nhưng nghe có vẻ không khả thi lắm. Ngôn từ trong đó cũng nhiều, may mắn là họ có sẵn một ví dụ cho ta nghiên cứu. Muốn hiểu một chức năng nó thực hiện cái gì, cứ nhìn cách nó hoạt động là dễ hiểu nhất. Đem câu query mẫu ra chạy thử thì, cảm giác đầu tiên phải nói là "Magic !"

Cũng mất một hồi để nghiền ngẫm, tuy nhiên mình diễn dịch tàm tạm cái ví dụ trên ra thì thế này

WITH RECURSIVE temp [n, fact] AS 

syntax thôi, nhưng ở đây ngoài việc khai báo dùng recursive thì ngoài ra chúng ta còn thông báo thêm chuyện, ta sẽ dùng tên gọi temp để trỏ tới bộ dữ liệu tạm được đem vào thực hiện đệ quy, và bảng dữ liệu tạm [ trong mắt mình thì mọi thứ của SQL đều quy về là bảng hết được, cũng tương tự như mọi thứ trong JavaScript đều là object ] sẽ có 2 trường với tên gọi nfact.

UNION ALL

trong Wiki thì không thấy nói đây là yêu cầu bắt buộc, nhưng ngẫm một chút thì : chúng ta sẽ cần một truy vấn để đem đi đệ quy, và chẳng lẽ chỉ có mỗi thế, thường thì cũng sẽ cần một query nào khác đem thông tin đệ quy đó vào sử dụng chứ. Nghe có vẻ hợp hợp với trường hợp chúng ta đang cần, nhiều khả năng query ta cần viết ra cũng sẽ có dạng kiểu này, tạm note lại một điều là muốn sử dụng được UNION ALL thì 2 query sẽ phải có số lượng cột bằng nhau. Tiếp

SELECT n+1, [n+1]*fact FROM temp -- Recursive Subquery 
        WHERE n 

Chủ Đề