2009-07-06 49 views
9

Tôi đã có một loại cây giống như điều đang diễn ra trong cơ sở dữ liệu MySQL của tôi.Dữ liệu phân cấp trong MySQL

Tôi có cơ sở dữ liệu có danh mục và mỗi danh mục có một danh mục con. Tôi giữ tất cả các loại trong một bảng, vì vậy các cột là như thế này:

*categories table* 
id | name | parent_id 
1 | Toys | 0 
2 | Dolls | 1 
3 | Bikes | 1 

Mỗi mục trong cơ sở dữ liệu của tôi được gán cho một trong những hạng mục:

*items table* 
item | category_id 
barbie | 2 
schwinn| 3 

Vấn đề là nếu ai đó muốn xem tất cả các ĐỒ CHƠI (danh mục gốc) cách tốt nhất để lấy thông tin từ cơ sở dữ liệu mặt hàng là gì? Cách duy nhất tôi biết làm thế nào để làm một cái gì đó như

SELECT * 
FROM items 
WHERE category_id = 2 
JOIN SELECT * 
    FROM items 
    WHERE category_id = 3 
    etc... 

Nhưng nếu tôi có 10 loại dưới Đồ chơi, thì tôi phải làm điều này và tham gia 10 lần.

Có cách nào tốt hơn để xử lý việc này không?

+0

Tham gia đó là cú pháp SQL không hợp lệ (và nếu bạn sửa cú pháp, ví dụ: dấu ngoặc đơn quanh giây, bạn sẽ nhận được tập kết quả trống); có lẽ bạn có nghĩa là UNION? –

Trả lời

0

Giả sử bạn biết id của thể loại Đồ chơi, và không có gì là trong hạng mục cấp cao nhất Đồ chơi:

SELECT * FROM items WHERE category_id IN (SELECT id FROM categories WHERE parent_id = 1) 
+0

Và btw không có gì NÊN được trong một loại không lá - cho chính xác những lý do tương tự như Haahr cho "không bao giờ subclassing một lớp bê tông"; nếu bạn cần một danh mục con khác/misc của đồ chơi, hãy làm nó như một đứa trẻ của Đồ chơi, vì vậy nó ở cùng mức với các tiểu thể loại khác. –

3

Tôi giả sử bạn biết cách lấy số ID và đó không phải là điểm của câu hỏi. Ngoài ra, parent_id cũng phải là tham chiếu FK id và tôi sẽ sử dụng NULL cho lớp trên cùng, không phải 0.

Nếu các danh mục hàng đầu của bạn có nhiều nhất một danh mục phụ, bạn có thể sử dụng truy vấn này nhận tất cả Đồ chơi:

SELECT * 
FROM items 
WHERE items.category_id IN (SELECT id FROM categories 
          WHERE categories.parent_id = 1 
          OR categories.id = 1); 

Nếu danh mục của bạn có thể có danh mục phụ lồng nhau, bạn sẽ phải sử dụng thủ tục lưu trữ và gọi nó theo cách đệ quy. Giả:

Procedure getItemsInCategory 
Input: @category_id integer 
Output: items rows 
{ 
    For each item in (SELECT * 
         FROM items 
         WHERE items.category_id = @category_id): 
     return the row; 

    For each id in (SELECT id 
        FROM categories 
        WHERE categories.parent_id = @category_id): 
     return the rows in getItemsInCategory(id); 
} 
4

Nhưng nếu tôi có như 10 loại dưới Đồ chơi, sau đó tôi sẽ phải làm điều này tham gia và truy vấn 10 lần. Có cách nào tốt hơn để xử lý việc này không?

Có, có cách lưu trữ dữ liệu có tên "nested sets". Khó chèn dữ liệu hơn một chút, nhưng đơn giản để chọn toàn bộ nhánh đa cấp sử dụng câu lệnh select.

Ngoài ra, Celko đã viết a book về chủ đề này, với một chương về các bộ lồng nhau và các chương khác về các phương pháp khác.

+0

+1 Vì lý do nào đó mà tôi chưa từng thấy trước đây và nó thật tuyệt vời. Kết quả đầu tiên (http://dev.mysql.com/tech-resources/articles/hierarchical-data.html) cho đến nay là giải thích tốt nhất mà tôi đã tìm thấy và nó hoàn hảo cho một mô hình phân cấp dữ liệu. –

0

Tôi không quen với MySQL, nhưng đây là cách tôi sẽ làm điều đó trong TSQL (SQL SERVER), có thể cố gắng tìm một cách tương đương để làm điều đó trong MySQL?

1) Vòng qua tất cả các loại để có được những đứa trẻ cho mục cụ thể, trong trường hợp mục này id = 1

2) Lọc các mục để mà liên quan đến trẻ em trong Hierarchy CTE (Common Table Expression) .

With Hierarchy As 
(

SELECT id, name, parent_id 
from   categories 
where  id = 1 
UNION ALL 
SELECT  child.id, child.name, child.parent_id 
from   categories child 
inner join Hierarchy parent on child.parent_id = parent.id 
) 
SELECT * FROM items 
WHERE category_id IN 
(
    Select id 
    from Hierarchy 
) 
21

Bạn muốn được trao ID mẹ:

Vì vậy, giả sử bạn đang trao

set @parentId = 1 /*toys*/ 

select 
    * 
from 
    Items i 
inner join Categories c on c.id = i.categoryId 
where 
    c.parentId = @parentId 

này sẽ cung cấp cho bạn những mục bạn muốn - với một lỗ hổng thiết kế lớn: nó không xử lý nhiều cấp các danh mục phân cấp.

Hãy nói rằng bạn có Categories bảng này:

*Categories table* 
id | name | parentId 
1 | Toys | 0 
2 | Dolls | 1 
3 | Bikes | 1 
4 | Models | 2 
5 | Act.Fig.| 2 
6 | Mountain| 3 
7 | BMX  | 3 

Và Items:

*items table* 
item | category_id 
Barbie | 4 
GIJoe | 5 
Schwinn| 6 
Huffy | 7 

Cách duy nhất để có được tất cả các mục có liên quan là làm một tự tham gia:

select 
    * 
from 
    Items i 
inner join Categories c on c.id = i.categoryId 
inner join Categories c2 on c.parentId = c2.id 
where 
    c2.parentId = @parentId 

Mẫu này không thể mở rộng - vì bạn có thể có nhiều cấp bậc phân cấp.

Một cách phổ biến để xử lý phân cấp là xây dựng bảng "phẳng": một hàng liên kết mỗi nút với TẤT CẢ đó là con cháu của nó.

Ngoài một bảng Categories, bạn xây dựng một bảng thứ hai:

*CategoriesFlat table* The Name column is here only for readability 
id | name | parentId 
1 | Toys | 1 
----------------- 
2 | Dolls | 1 
2 | Dolls | 2 
----------------- 
4 | Models | 1 
4 | Models | 2 
4 | Models | 4 
5 | Act.Fig.| 1 
5 | Act.Fig.| 2 
5 | Act.Fig.| 5 
----------------- 
3 | Bikes | 1 
3 | Bikes | 3 
----------------- 
6 | Mountain| 1 
6 | Mountain| 3 
6 | Mountain| 6 
7 | BMX  | 1 
7 | BMX  | 3 
7 | BMX  | 7 

Vì vậy, bạn có thể viết:

select 
    * 
from 
    Items i 
inner join CategoriesFlat c on c.id = i.categoryId 
where 
    c.parentId = @parentId 

Và có được TẤT CẢ các hạng mục và mục có liên quan.

Đây là số great slideshow about SQL anti-patterns và giải pháp cho chúng. (Dữ liệu phân cấp trong SQL là một mô hình chống, nhưng đừng chán nản - tất cả chúng ta chạy vào cái này)

+0

Trình chiếu tốt. – ChrisW

+0

Câu trả lời tuyệt vời! Tôi sẽ upvote bạn nhiều hơn nếu tôi có thể. Thực sự tốt công cụ để biết. Tài nguyên tuyệt vời cũng vậy, cảm ơn bạn đã chia sẻ. – EvilChookie

0

Tôi muốn chia sẻ ý tưởng của tôi với bạn.

Giới hạn mô hình tiếp giáp: Làm theo Managing Hierarchical Data in MySQL Như bạn đã mô tả mô hình kề có giới hạn rằng bạn phải biết cấp trước khi bạn truy xuất đường dẫn.

Sử dụng mô hình lồng nhau: Nhưng nếu bạn chuyển cấu trúc dữ liệu cấu trúc dữ liệu sang mô hình tập hợp lồng nhau thì bạn vẫn có thể sử dụng tự tham gia để lấy cây.

Chuyển đổi mô hình phân cấp thành mô hình lồng nhau: Bây giờ, chúng tôi cần thuật toán di chuyển cây để lập chỉ mục mô hình lồng nhau. Điều này có thể được thực hiện trong hàm mysql (Xin lỗi chuyển đổi cần thực hiện một số thuật toán: Thuật toán duyệt cây.Không chắc chắn cái nào được trang bị tốt nhất).

Cảm ơn :)

Các vấn đề liên quan