2008-09-12 50 views
7

tôi có các bảng tiếp theo. nhóm bảng chứa các nhóm được sắp xếp theo thứ bậc và group_member lưu trữ những nhóm mà người dùng thuộc về.SQL đệ quy

groups 
--------- 
id 
parent_id 
name 

group_member 
--------- 
id 
group_id 
user_id 

ID PARENT_ID NAME 
--------------------------- 
1 NULL  Cerebra 
2 1   CATS 
3 2   CATS 2.0 
4 1   Cerepedia 
5 4   Cerepedia 2.0 
6 1   CMS 

ID GROUP_ID USER_ID 
--------------------------- 
1 1  3 
2 1  4 
3 1  5 
4 2  7 
5 2  6 
6 4  6 
7 5  12 
8 4  9 
9 1  10 

Tôi muốn truy xuất các nhóm hiển thị cho một người dùng cụ thể. Đó là để nói các nhóm người dùng thuộc về và con của các nhóm này. Ví dụ: với dữ liệu ở trên:

USER VISIBLE_GROUPS 
9  4, 5 
3  1,2,4,5,6 
12 5 

Tôi nhận được các giá trị này bằng cách sử dụng đệ quy và một số truy vấn cơ sở dữ liệu. Nhưng tôi muốn biết nếu nó có thể làm điều này với một truy vấn SQL duy nhất để cải thiện hiệu suất ứng dụng của tôi. Tôi đang sử dụng MySQL.

Trả lời

0

Tôi nghĩ rằng bạn sẽ cần con trỏ cho điều này, có thể giúp this link

+0

Liên kết mã hóa được viết cho SQL Server. Có thể không dễ dàng điều chỉnh mã đó cho mySQL, nhưng đây là tài liệu tham khảo mySQL trên con trỏ: http://dev.mysql.com/doc/refman/5.0/en/cursors.html – Prestaul

0

Tôi không nghĩ rằng điều này có thể được thực hiện mà không sử dụng đệ quy. Bạn có thể thực hiện nó với một thủ tục được lưu trữ duy nhất bằng cách sử dụng mySQL, nhưng đệ quy không được phép trong các thủ tục được lưu trữ theo mặc định. This article có thông tin về cách kích hoạt đệ quy. Tôi không chắc chắn về tác động của điều này đến câu trả lời của câu hỏi hiệu suất. mySQL có thể làm một số tối ưu hóa các thủ tục được lưu trữ, nhưng nếu không tôi sẽ mong đợi hiệu suất là tương tự.

0

Không biết nếu bạn đã có một bảng người dùng, vì vậy tôi có được danh sách thông qua lưu trữ trong bảng Group_Member của user_id ...

SELECT GroupUsers.User_ID, 
     (
     SELECT 
      STUFF((SELECT ',' + 
      Cast(Group_ID As Varchar(10)) 
      FROM Group_Member Member (nolock) 
      WHERE Member.User_ID=GroupUsers.User_ID 
     FOR XML PATH('')),1,1,'') 
     ) As Groups 
FROM (SELECT User_ID FROM Group_Member GROUP BY User_ID) GroupUsers 

Đó trả:

User_ID Groups 
3   1 
4   1 
5   1 
6   2,4 
7   2 
9   4 
10   1 
12   5 

Mà dường như ngay theo dữ liệu trong bảng của bạn. Nhưng không khớp với danh sách giá trị dự kiến ​​của bạn (ví dụ: Người dùng 9 chỉ trong một nhóm trong dữ liệu bảng của bạn nhưng bạn hiển thị nó trong kết quả là thuộc về hai)

EDIT: Dang. Chỉ cần nhận thấy rằng bạn đang sử dụng MySQL. Giải pháp của tôi là cho SQL Server. Lấy làm tiếc.

- Kevin Fairchild

-1

Không có cách nào để làm điều này trong tiêu chuẩn SQL, nhưng bạn thường có thể tìm thấy phần mở rộng nhà cung cấp cụ thể, ví dụ, CONNECT BY trong Oracle.

UPDATE: Như các ý kiến ​​chỉ ra, điều này đã được bổ sung trong SQL 99.

+0

Sai. Tiêu chuẩn ISO SQL đã chỉ định SQL đệ quy từ chuẩn SQL: 1999. DB2 và các phiên bản gần đây của MSSQL thực hiện nó. SQL đệ quy của SQL tiêu chuẩn là khá khác với Oracle CONNECT BY, bằng cách này. –

+0

Tôi không nhận ra điều đó. Có một tài liệu tham khảo miễn phí cho các tiêu chuẩn mới nhất, vì ISO dường như nghĩ rằng các nhà phát triển phải trả tiền cho bản thân tiêu chuẩn? –

6

Hai điều tôi suy nghĩ:

1 - Bạn có thể lặp đi lặp lại bên ngoài-join bảng để bản thân đệ quy đi lên cây của bạn, như trong:

SELECT * 
FROM 
    MY_GROUPS MG1 
,MY_GROUPS MG2 
,MY_GROUPS MG3 
,MY_GROUPS MG4 
,MY_GROUPS MG5 
,MY_GROUP_MEMBERS MGM 
WHERE MG1.PARENT_ID = MG2.UNIQID (+) 
    AND MG1.UNIQID = MGM.GROUP_ID (+) 
    AND MG2.PARENT_ID = MG3.UNIQID (+) 
    AND MG3.PARENT_ID = MG4.UNIQID (+) 
    AND MG4.PARENT_ID = MG5.UNIQID (+) 
    AND MGM.USER_ID = 9 

Đó sẽ cho bạn kết quả như thế này:

UNIQID PARENT_ID NAME  UNIQID_1 PARENT_ID_1 NAME_1 UNIQID_2 PARENT_ID_2 NAME_2 UNIQID_3 PARENT_ID_3 NAME_3 UNIQID_4 PARENT_ID_4 NAME_4 UNIQID_5 GROUP_ID USER_ID 
4  2   Cerepedia 2  1   CATS 1  null  Cerebra null  null  null null  null  null 8  4  9 

Giới hạn ở đây là bạn phải thêm một kết nối mới cho mỗi "cấp độ" bạn muốn đi bộ lên cây. Nếu cây của bạn có ít hơn, nói rằng, 20 cấp độ, sau đó bạn có thể có thể nhận được đi với nó bằng cách tạo ra một cái nhìn cho thấy 20 cấp độ từ mỗi người dùng.

2 - Cách tiếp cận duy nhất khác mà tôi biết là tạo một hàm cơ sở dữ liệu đệ quy và gọi hàm đó từ mã. Bạn vẫn sẽ có một số chi phí tra cứu theo cách đó (nghĩa là số truy vấn của bạn vẫn bằng với số cấp bạn đang đi trên cây), nhưng tổng thể nó sẽ nhanh hơn vì tất cả đều diễn ra bên trong cơ sở dữ liệu.

Tôi không chắc chắn về MySql, nhưng trong Oracle, một hàm như vậy sẽ tương tự như hàm này (bạn sẽ phải thay đổi tên bảng và trường; tôi chỉ sao chép thứ mà tôi đã làm trong quá khứ) :

CREATE OR REPLACE FUNCTION GoUpLevel(WO_ID INTEGER, UPLEVEL INTEGER) RETURN INTEGER 
IS 
BEGIN 
    DECLARE 
    iResult INTEGER; 
    iParent INTEGER; 
BEGIN 
    IF UPLEVEL <= 0 THEN 
    iResult := WO_ID; 
    ELSE 
    SELECT PARENT_ID 
    INTO iParent 
    FROM WOTREE 
    WHERE ID = WO_ID;  
    iResult := GoUpLevel(iParent,UPLEVEL-1); --recursive 
    END; 
    RETURN iResult; 
    EXCEPTION WHEN NO_DATA_FOUND THEN 
    RETURN NULL; 
    END; 
END GoUpLevel; 
/
3

Sách của Joe Cleko "SQL cho Smarties" và "Cây và phân cấp trong SQL cho Smarties" mô tả các phương pháp tránh hoàn toàn bằng cách sử dụng các tập lồng nhau. Điều đó làm phức tạp việc cập nhật, nhưng làm cho các truy vấn khác (thường sẽ cần đệ quy) tương đối đơn giản. Có some examples in this article được viết bởi Joe vào năm 1996.

0

Đã có mức tăng tương tự question.

Dưới đây là câu trả lời của tôi (một chút thay đổi nội dung):

Tôi không chắc chắn tôi hiểu đúng câu hỏi của bạn, nhưng điều này có thể làm việc My take on trees in SQL.

Bài đăng được liên kết mô tả phương pháp lưu trữ cây trong cơ sở dữ liệu - PostgreSQL trong trường hợp đó - nhưng phương pháp đủ rõ ràng, vì vậy nó có thể được áp dụng dễ dàng cho bất kỳ cơ sở dữ liệu nào.

Với phương pháp này bạn có thể dễ dàng cập nhật tất cả các nút phụ thuộc vào nút K đã sửa đổi với khoảng N truy vấn SELECT đơn giản trong đó N là khoảng cách K từ nút gốc.

Chúc may mắn!

0

Tôi không nhớ câu hỏi SO nào tôi tìm thấy liên kết dưới, nhưng this article on sitepoint.com (trang thứ hai) cho thấy cách lưu trữ cây phân cấp khác trong bảng giúp dễ dàng tìm thấy tất cả các nút con hoặc đường dẫn đến đầu , những thứ như thế. Giải thích tốt với mã ví dụ.


PS. Mới mẻ với StackOverflow, là ok ở trên như là một câu trả lời, hoặc nó thực sự có được một bình luận về câu hỏi vì nó chỉ là một con trỏ đến một giải pháp khác nhau (không chính xác trả lời các câu hỏi riêng của mình)?