Nếu đó là tất cả những gì bạn cần, bạn có thể sử dụng tìm kiếm LIKE
SELECT *
FROM Table1
WHERE CELL LIKE 'AEE%';
Với chỉ mục bắt đầu bằng CELL
đây là một kiểm tra phạm vi, nhanh chóng.
Nếu dữ liệu của bạn không giống như vậy, bạn có thể tạo path
cột trông giống như một đường dẫn thư mục và chứa tất cả các nút "trên đường / đường dẫn" từ gốc đến phần tử.
| id | CELL | parent_id | path |
|====|======|===========|==========|
| 1 | A | NULL | 1/ |
| 2 | AA | 1 | 1/2/ |
| 3 | AAA | 2 | 1/2/3/ |
| 4 | AAC | 2 | 1/2/4/ |
| 5 | AB | 1 | 1/5/ |
| 6 | AE | 1 | 1/6/ |
| 7 | AEA | 6 | 1/6/7/ |
| 8 | AEE | 6 | 1/6/8/ |
| 9 | AEEB | 8 | 1/6/8/9/ |
Để truy xuất tất cả các hậu duệ của 'AE' (bao gồm cả chính nó), truy vấn của bạn sẽ là
SELECT *
FROM tree t
WHERE path LIKE '1/6/%';
hoặc (nối cụ thể của MySQL)
SELECT t.*
FROM tree t
CROSS JOIN tree r -- root
WHERE r.CELL = 'AE'
AND t.path LIKE CONCAT(r.path, '%');
Kết quả:
| id | CELL | parent_id | path |
|====|======|===========|==========|
| 6 | AE | 1 | 1/6/ |
| 7 | AEA | 6 | 1/6/7/ |
| 8 | AEE | 6 | 1/6/8/ |
| 9 | AEEB | 8 | 1/6/8/9/ |
Hiệu suất
Tôi đã tạo 100 nghìn hàng dữ liệu giả trên MariaDB với plugin trình tự sử dụng tập lệnh sau:
drop table if exists tree;
CREATE TABLE tree (
`id` int primary key,
`CELL` varchar(50),
`parent_id` int,
`path` varchar(255),
unique index (`CELL`),
unique index (`path`)
);
DROP TRIGGER IF EXISTS `tree_after_insert`;
DELIMITER //
CREATE TRIGGER `tree_after_insert` BEFORE INSERT ON `tree` FOR EACH ROW BEGIN
if new.id = 1 then
set new.path := '1/';
else
set new.path := concat((
select path from tree where id = new.parent_id
), new.id, '/');
end if;
END//
DELIMITER ;
insert into tree
select seq as id
, conv(seq, 10, 36) as CELL
, case
when seq = 1 then null
else floor(rand(1) * (seq-1)) + 1
end as parent_id
, null as path
from seq_1_to_100000
;
DROP TRIGGER IF EXISTS `tree_after_insert`;
-- runtime ~ 4 sec.
Kiểm tra
Đếm tất cả các phần tử dưới gốc:
SELECT count(*)
FROM tree t
CROSS JOIN tree r -- root
WHERE r.CELL = '1'
AND t.path LIKE CONCAT(r.path, '%');
-- result: 100000
-- runtime: ~ 30 ms
Nhận các phần tử của cây con trong một nút cụ thể:
SELECT t.*
FROM tree t
CROSS JOIN tree r -- root
WHERE r.CELL = '3B0'
AND t.path LIKE CONCAT(r.path, '%');
-- runtime: ~ 30 ms
Kết quả:
| id | CELL | parent_id | path |
|=======|======|===========|=====================================|
| 4284 | 3B0 | 614 | 1/4/11/14/614/4284/ |
| 6560 | 528 | 4284 | 1/4/11/14/614/4284/6560/ |
| 8054 | 67Q | 6560 | 1/4/11/14/614/4284/6560/8054/ |
| 14358 | B2U | 6560 | 1/4/11/14/614/4284/6560/14358/ |
| 51911 | 141Z | 4284 | 1/4/11/14/614/4284/51911/ |
| 55695 | 16Z3 | 4284 | 1/4/11/14/614/4284/55695/ |
| 80172 | 1PV0 | 8054 | 1/4/11/14/614/4284/6560/8054/80172/ |
| 87101 | 1V7H | 51911 | 1/4/11/14/614/4284/51911/87101/ |
PostgreSQL
Điều này cũng hoạt động cho PostgreSQL. Chỉ cú pháp nối chuỗi phải được thay đổi:
SELECT t.*
FROM tree t
CROSS JOIN tree r -- root
WHERE r.CELL = 'AE'
AND t.path LIKE r.path || '%';
Bản trình diễn: sqlfiddle - trình sắp xếp lại
Tìm kiếm hoạt động như thế nào
Nếu bạn nhìn vào ví dụ thử nghiệm, bạn sẽ thấy rằng tất cả các đường dẫn trong kết quả đều bắt đầu bằng '1/4/11/14/614/4284 /'. Đó là đường dẫn của gốc cây con với CELL='3B0'
. Nếu path
được lập chỉ mục, công cụ sẽ tìm thấy tất cả chúng một cách hiệu quả, vì chỉ mục được sắp xếp theo path
. Nó giống như bạn muốn tìm tất cả các từ bắt đầu bằng 'pol' trong một từ điển với 100 nghìn từ. Bạn không cần phải đọc toàn bộ từ điển.