วิธีที่มีประสิทธิภาพ / สง่างามที่สุดในการวิเคราะห์ตารางแบนเป็นต้นไม้คืออะไร?


517

สมมติว่าคุณมีตารางแบนที่จัดเก็บลำดับชั้นต้นไม้ที่สั่งซื้อ:

Id   Name         ParentId   Order
 1   'Node 1'            0      10
 2   'Node 1.1'          1      10
 3   'Node 2'            0      20
 4   'Node 1.1.1'        2      10
 5   'Node 2.1'          3      10
 6   'Node 1.2'          1      20

[id] Nameนี่คือแผนภาพที่เรามี รูตโหนด 0 เป็นแบบสมมติ

                       [0] รูท
                          / \ 
              [1] โหนด 1 [3] โหนด 2
              / \ \
    [2] โหนด 1.1 [6] โหนด 1.2 [5] โหนด 2.1
          /          
 [4] โหนด 1.1.1

คุณจะใช้วิธีการที่เรียบง่ายแบบใดในการแสดงผลออกเป็น HTML (หรือข้อความสำหรับเรื่องนั้น) ในรูปแบบต้นไม้ที่เรียงลำดับอย่างถูกต้องและเยื้องถูกต้อง

สมมติว่าคุณมีโครงสร้างข้อมูลพื้นฐานเท่านั้น (อาร์เรย์และ hashmaps) ไม่มีวัตถุแฟนซีที่มีการอ้างอิงผู้ปกครอง / เด็กไม่มี ORM ไม่มีกรอบเพียงมือสองข้างของคุณ ตารางจะแสดงเป็นชุดผลลัพธ์ซึ่งสามารถเข้าถึงได้แบบสุ่ม

รหัสเทียมหรือภาษาอังกฤษธรรมดาก็โอเคนี่เป็นคำถามเชิงแนวคิดอย่างแท้จริง

คำถามโบนัส: มีวิธีที่ดีกว่าในการจัดเก็บโครงสร้างต้นไม้แบบนี้ใน RDBMS หรือไม่?


แก้ไขและเพิ่มเติม

ในการตอบคำถามของ commenter ( Mark Bessey ) หนึ่งคำถาม: ไม่จำเป็นต้องมีโหนดรูทเพราะจะไม่ปรากฏต่อไป ParentId = 0 เป็นแบบแผนเพื่อแสดง "เหล่านี้เป็นระดับบนสุด" คอลัมน์คำสั่งกำหนดวิธีการเรียงลำดับโหนดที่มีพาเรนต์เดียวกัน

"ชุดผลลัพธ์" ที่ฉันพูดถึงสามารถวาดเป็นชุดแฮชแมพ (เพื่ออยู่ในคำศัพท์นั้น) สำหรับตัวอย่างของฉันตั้งใจจะอยู่ที่นั่นแล้ว คำตอบบางข้อใช้ไมล์พิเศษและสร้างขึ้นก่อน แต่ก็ไม่เป็นไร

ต้นไม้สามารถลึกโดยพลการ แต่ละโหนดสามารถมีลูกได้ แต่ฉันไม่ได้มีต้นไม้ "รายการนับล้าน" ในใจ

อย่าเข้าใจผิดว่าตัวเลือกการตั้งชื่อโหนดของฉัน ('Node 1.1.1') สำหรับบางสิ่งที่ต้องพึ่งพา โหนดสามารถเรียกว่า 'Frank' หรือ 'Bob' ได้อย่างเท่าเทียมกันโดยไม่มีโครงสร้างการตั้งชื่อโดยนัยนี่เป็นเพียงการทำให้สามารถอ่านได้

ฉันได้โพสต์โซลูชันของฉันเองเพื่อให้คุณสามารถดึงมันออกมา


2
"ไม่มีวัตถุแฟนซีที่มีการอ้างอิงผู้ปกครอง / เด็ก" - ทำไมล่ะ การสร้างวัตถุโหนดพื้นฐานด้วย. addChild (), .getParent () วิธีการช่วยให้คุณสามารถสร้างแบบจำลองความสัมพันธ์โหนดค่อนข้างดี
matt b

2
มันเป็นปกติ (n ลูกที่ n สามารถเป็น 2) ต้นไม้หรือต้นไม้ไบนารี (โหนดสามารถมี 0, 1 หรือ 2 ลูก)?
BKimmel

เนื่องจากคุณสามารถใช้โครงสร้างข้อมูลโหนดที่เหมาะสมด้วย hashmap จึงไม่มีข้อ จำกัด ที่แท้จริงที่นี่ทำงานได้มากขึ้น
Svante

... และนั่นคือสิ่งที่คุณทำ
Svante

คำตอบ:


451

ตอนนี้ที่MySQL 8.0 รองรับการสืบค้นแบบเรียกซ้ำเราสามารถพูดได้ว่าฐานข้อมูล SQL ที่เป็นที่นิยมทั้งหมดรองรับการสืบค้นแบบเรียกซ้ำในไวยากรณ์มาตรฐาน

WITH RECURSIVE MyTree AS (
    SELECT * FROM MyTable WHERE ParentId IS NULL
    UNION ALL
    SELECT m.* FROM MyTABLE AS m JOIN MyTree AS t ON m.ParentId = t.Id
)
SELECT * FROM MyTree;

ฉันทดสอบคิวรีแบบเรียกซ้ำใน MySQL 8.0 ในงานนำเสนอของฉันRecursive Query Throwdownในปี 2560

ด้านล่างนี้เป็นคำตอบเดิมของฉันจากปี 2008:


มีหลายวิธีในการจัดเก็บข้อมูลที่มีโครงสร้างแบบต้นไม้ในฐานข้อมูลเชิงสัมพันธ์ สิ่งที่คุณแสดงในตัวอย่างของคุณใช้สองวิธี:

  • รายการ Adjacency (คอลัมน์ "พาเรนต์") และ
  • Path Enumeration (หมายเลขประในคอลัมน์ชื่อของคุณ)

อีกวิธีหนึ่งเรียกว่าชุดแบบซ้อนและสามารถจัดเก็บในตารางเดียวกันได้ อ่าน " ต้นไม้และลำดับชั้นใน SQL สำหรับ Smarties " โดย Joe Celko สำหรับข้อมูลเพิ่มเติมเกี่ยวกับการออกแบบเหล่านี้

ฉันมักจะชอบการออกแบบที่เรียกว่าClosed Table (aka "Adjacency Relation") สำหรับการจัดเก็บข้อมูลที่มีโครงสร้างแบบต้นไม้ มันต้องใช้อีกตารางหนึ่ง แต่จากการค้นหาต้นไม้นั้นค่อนข้างง่าย

ฉันครอบคลุมปิดตารางในงานนำเสนอรุ่นสำหรับข้อมูลลำดับชั้นกับ SQL และ PHPและในหนังสือของฉันSQL Antipatterns: หลีกเลี่ยงการผิดพลาดของการเขียนโปรแกรมฐานข้อมูล

CREATE TABLE ClosureTable (
  ancestor_id   INT NOT NULL REFERENCES FlatTable(id),
  descendant_id INT NOT NULL REFERENCES FlatTable(id),
  PRIMARY KEY (ancestor_id, descendant_id)
);

จัดเก็บเส้นทางทั้งหมดในตารางการปิดที่มีบรรพบุรุษโดยตรงจากโหนดหนึ่งไปยังอีก รวมแถวสำหรับแต่ละโหนดเพื่ออ้างอิงเอง ตัวอย่างเช่นการใช้ชุดข้อมูลที่คุณแสดงในคำถามของคุณ:

INSERT INTO ClosureTable (ancestor_id, descendant_id) VALUES
  (1,1), (1,2), (1,4), (1,6),
  (2,2), (2,4),
  (3,3), (3,5),
  (4,4),
  (5,5),
  (6,6);

ทีนี้คุณสามารถทำให้ tree เริ่มต้นที่ node 1 ดังนี้:

SELECT f.* 
FROM FlatTable f 
  JOIN ClosureTable a ON (f.id = a.descendant_id)
WHERE a.ancestor_id = 1;

ผลลัพธ์ (ในไคลเอนต์ MySQL) มีลักษณะดังนี้:

+----+
| id |
+----+
|  1 | 
|  2 | 
|  4 | 
|  6 | 
+----+

กล่าวอีกนัยหนึ่งโหนด 3 และ 5 ไม่รวมอยู่เนื่องจากเป็นส่วนหนึ่งของลำดับชั้นที่แยกต่างหากไม่ใช่จากโหนด 1


Re: ความคิดเห็นจาก e-พอใจเกี่ยวกับเด็กทันที (หรือผู้ปกครองทันที) คุณสามารถเพิ่มpath_lengthคอลัมน์ "" ลงในClosureTableเพื่อทำให้ง่ายต่อการค้นหาโดยเฉพาะสำหรับเด็กหรือผู้ปกครองในทันที (หรือระยะทางอื่น ๆ )

INSERT INTO ClosureTable (ancestor_id, descendant_id, path_length) VALUES
  (1,1,0), (1,2,1), (1,4,2), (1,6,1),
  (2,2,0), (2,4,1),
  (3,3,0), (3,5,1),
  (4,4,0),
  (5,5,0),
  (6,6,0);

จากนั้นคุณสามารถเพิ่มคำในการค้นหาของคุณเพื่อค้นหาคิวลูกของโหนดที่กำหนด เหล่านี้เป็นทายาทที่path_lengthมี 1

SELECT f.* 
FROM FlatTable f 
  JOIN ClosureTable a ON (f.id = a.descendant_id)
WHERE a.ancestor_id = 1
  AND path_length = 1;

+----+
| id |
+----+
|  2 | 
|  6 | 
+----+

ความคิดเห็นจาก @ashraf: "วิธีการเรียงลำดับต้นไม้ทั้งหมด [โดยชื่อ]?"

ต่อไปนี้เป็นตัวอย่างแบบสอบถามเพื่อส่งคืนโหนดทั้งหมดที่เป็นสืบทอดของโหนด 1 เข้าร่วมกับ FlatTable ที่มีแอ็ตทริบิวต์โหนดอื่นเช่นnameและเรียงลำดับตามชื่อ

SELECT f.name
FROM FlatTable f 
JOIN ClosureTable a ON (f.id = a.descendant_id)
WHERE a.ancestor_id = 1
ORDER BY f.name;

ความคิดเห็นอีกครั้งจาก @Nate:

SELECT f.name, GROUP_CONCAT(b.ancestor_id order by b.path_length desc) AS breadcrumbs
FROM FlatTable f 
JOIN ClosureTable a ON (f.id = a.descendant_id) 
JOIN ClosureTable b ON (b.descendant_id = a.descendant_id) 
WHERE a.ancestor_id = 1 
GROUP BY a.descendant_id 
ORDER BY f.name

+------------+-------------+
| name       | breadcrumbs |
+------------+-------------+
| Node 1     | 1           |
| Node 1.1   | 1,2         |
| Node 1.1.1 | 1,2,4       |
| Node 1.2   | 1,6         |
+------------+-------------+

ผู้ใช้แนะนำการแก้ไขวันนี้ ผู้ดูแลจึงอนุมัติการแก้ไข แต่ฉันกลับด้าน

การแก้ไขแนะนำว่า ORDER BY ในแบบสอบถามสุดท้ายข้างต้นควรจะORDER BY b.path_length, f.nameเป็นเพื่อให้แน่ใจว่าการสั่งซื้อนั้นตรงกับลำดับชั้น แต่วิธีนี้ใช้ไม่ได้เพราะจะสั่ง "Node 1.1.1" หลังจาก "Node 1.2"

หากคุณต้องการให้การเรียงลำดับตรงกับลำดับชั้นในแบบที่เป็นไปได้ แต่ไม่ใช่เพียงแค่สั่งตามความยาวพา ธ ตัวอย่างเช่นดูคำตอบของฉันฐานข้อมูลแบบลำดับชั้น MySQL ปิดตาราง - วิธีการดึงข้อมูลออกมาในลำดับที่ถูกต้อง


6
นี่คือความสง่างามมากขอบคุณ ได้รับคะแนนโบนัส ;-) ฉันเห็นข้อเสียเปรียบเล็กน้อยเพียงข้อเดียว - เนื่องจากมันเก็บความสัมพันธ์ของเด็กไว้อย่างชัดเจนและโดยปริยายคุณจำเป็นต้องทำการอัปเดตอย่างระมัดระวังเพื่อให้ได้การเปลี่ยนแปลงเล็กน้อยในโครงสร้างต้นไม้
Tomalak

16
จริงทุกวิธีในการจัดเก็บโครงสร้างต้นไม้ในฐานข้อมูลต้องใช้งานบางอย่างไม่ว่าจะเป็นเมื่อสร้างหรือปรับปรุงต้นไม้หรือเมื่อสอบถามต้นไม้และต้นไม้ย่อย เลือกการออกแบบตามที่คุณต้องการง่ายกว่า: การเขียนหรือการอ่าน
Bill Karwin

2
@buffer มีโอกาสที่จะสร้างความไม่สอดคล้องกันในขณะที่คุณสร้างแถวทั้งหมดสำหรับลำดับชั้น Adjacency List ( parent_id) มีเพียงหนึ่งแถวที่จะแสดงความสัมพันธ์ระหว่างพ่อแม่และลูกแต่ละคน แต่ตารางปิดมีมากมาย
Bill Karwin

1
@BillKarwin อีกสิ่งหนึ่งคือตารางการปิดที่เหมาะสมสำหรับกราฟที่มีหลายเส้นทางไปยังโหนดใด ๆ (เช่นลำดับชั้นหมวดหมู่ที่โหนดใบไม้หรือโหนดที่ไม่ใช่ใบอาจเป็นของผู้ปกครองมากกว่าหนึ่งคน)
ผู้ใช้

2
@Reza ดังนั้นหากคุณเพิ่มโหนดชายด์ใหม่คุณสามารถเคียวรีสืบทอดทั้งหมดของ (1) และสิ่งเหล่านั้นเป็นบรรพบุรุษของลูกใหม่
Bill Karwin

58

หากคุณใช้ชุดซ้อน (บางครั้งเรียกว่า Modified Pre-order Traversal Tree) คุณสามารถแยกโครงสร้างต้นไม้ทั้งหมดหรือทรีย่อยใด ๆ ในลำดับต้นไม้ด้วยแบบสอบถามเดียวโดยมีค่าใช้จ่ายของการแทรกที่สูงขึ้นตามที่คุณต้องการ จัดการคอลัมน์ที่อธิบายเส้นทางตามลำดับผ่านโครงสร้างต้นไม้

สำหรับ django-mpttฉันใช้โครงสร้างแบบนี้:

id parent_id tree_id ระดับ lft rght
- --------- ------- ----- --- ----
 1 null 1 0 1 14
 2 1 1 1 2 7
 3 2 1 2 3 4
 4 2 1 2 5 6
 5 1 1 1 8 8 13
 6 5 1 2 9 10
 7 5 1 2 11 12

ซึ่งอธิบายถึงต้นไม้ที่มีลักษณะเช่นนี้ (ที่idแสดงถึงแต่ละรายการ):

 1
 + - 2
 | + - 3
 | + - 4
 |
 + - 5
     + - 6
     + - 7

หรือเป็นไดอะแกรมชุดซ้อนซึ่งทำให้ชัดเจนมากขึ้นเกี่ยวกับการทำงานlftและrghtค่า

 __________________________________________________________________________
| รูต 1 |
| ________________________________ ________________________________ |
| | เด็ก 1.1 | | เด็ก 1.2 | |
| | ___________ ___________ | | ___________ ___________ | |
| | | C 1.1.1 | | C 1.1.2 | | | | C 1.2.1 | | C 1.2.2 | | |
1 2 3___________4 5___________6 7 8 9___________10 11__________12 13 14
| | ________________________________ | | ________________________________ | |
| __________________________________________________________________________ |

อย่างที่คุณเห็นเมื่อต้องการรับทรีย่อยทั้งหมดสำหรับโหนดที่กำหนดตามลำดับทรีคุณต้องเลือกแถวทั้งหมดที่มีlftและrghtค่าระหว่างlftและกับrghtค่า นอกจากนี้ยังง่ายต่อการเรียกต้นไม้ของบรรพบุรุษสำหรับโหนดที่กำหนด

levelคอลัมน์เป็นบิตของ denormalisation เพื่อความสะดวกสบายมากกว่าอะไรและtree_idคอลัมน์ที่ช่วยให้คุณสามารถเริ่มต้นlftและrghtหมายเลขสำหรับแต่ละโหนดระดับบนสุดซึ่งจะช่วยลดจำนวนของคอลัมน์ผลกระทบจากการแทรกย้ายและลบที่เป็นlftและrghtคอลัมน์จะต้องมีการ ปรับให้สอดคล้องเมื่อการดำเนินการเหล่านี้เกิดขึ้นเพื่อสร้างหรือปิดช่องว่าง ฉันทำบันทึกการพัฒนาในเวลาที่ฉันพยายามห่อหัวคำถามที่จำเป็นสำหรับการดำเนินการแต่ละครั้ง

ในแง่ของการทำงานกับข้อมูลนี้เพื่อแสดงทรีฉันได้สร้างtree_item_iteratorฟังก์ชั่นยูทิลิตี้ซึ่งสำหรับแต่ละโหนดควรให้ข้อมูลที่เพียงพอแก่คุณในการสร้างจอแสดงผลที่คุณต้องการ

ข้อมูลเพิ่มเติมเกี่ยวกับ MPTT:


9
ฉันหวังว่าเราจะหยุดใช้ตัวย่อเช่นlftและrghtสำหรับชื่อคอลัมน์ฉันหมายถึงจำนวนอักขระที่เราไม่ต้องพิมพ์? หนึ่ง?!
orustammanapov

21

มันเป็นคำถามที่ค่อนข้างเก่า แต่เมื่อมันมีหลายมุมมองฉันคิดว่ามันคุ้มค่าที่จะนำเสนอทางเลือกอื่น

ในการอ่านโครงสร้างต้นไม้คุณสามารถใช้Common Table Expressions (CTEs) แบบเรียกซ้ำได้ มันให้ความเป็นไปได้ที่จะดึงโครงสร้างต้นไม้ทั้งหมดในครั้งเดียวมีข้อมูลเกี่ยวกับระดับของโหนดโหนดแม่และลำดับภายในลูกของโหนดแม่

ให้ฉันแสดงให้คุณเห็นว่ามันจะทำงานอย่างไรใน PostgreSQL 9.1

  1. สร้างโครงสร้าง

    CREATE TABLE tree (
        id int  NOT NULL,
        name varchar(32)  NOT NULL,
        parent_id int  NULL,
        node_order int  NOT NULL,
        CONSTRAINT tree_pk PRIMARY KEY (id),
        CONSTRAINT tree_tree_fk FOREIGN KEY (parent_id) 
          REFERENCES tree (id) NOT DEFERRABLE
    );
    
    
    insert into tree values
      (0, 'ROOT', NULL, 0),
      (1, 'Node 1', 0, 10),
      (2, 'Node 1.1', 1, 10),
      (3, 'Node 2', 0, 20),
      (4, 'Node 1.1.1', 2, 10),
      (5, 'Node 2.1', 3, 10),
      (6, 'Node 1.2', 1, 20);
  2. เขียนแบบสอบถาม

    WITH RECURSIVE 
    tree_search (id, name, level, parent_id, node_order) AS (
      SELECT 
        id, 
        name,
        0,
        parent_id, 
        1 
      FROM tree
      WHERE parent_id is NULL
    
      UNION ALL 
      SELECT 
        t.id, 
        t.name,
        ts.level + 1, 
        ts.id, 
        t.node_order 
      FROM tree t, tree_search ts 
      WHERE t.parent_id = ts.id 
    ) 
    SELECT * FROM tree_search 
    WHERE level > 0 
    ORDER BY level, parent_id, node_order;

    นี่คือผลลัพธ์:

     id |    name    | level | parent_id | node_order 
    ----+------------+-------+-----------+------------
      1 | Node 1     |     1 |         0 |         10
      3 | Node 2     |     1 |         0 |         20
      2 | Node 1.1   |     2 |         1 |         10
      6 | Node 1.2   |     2 |         1 |         20
      5 | Node 2.1   |     2 |         3 |         10
      4 | Node 1.1.1 |     3 |         2 |         10
    (6 rows)

    โหนดต้นไม้ถูกจัดเรียงตามระดับความลึก ในผลลัพธ์สุดท้ายเราจะแสดงในบรรทัดถัดไป

    สำหรับแต่ละระดับจะถูกเรียงลำดับโดย parent_id และ node_order ภายในพาเรนต์ สิ่งนี้บอกเราถึงวิธีการนำเสนอพวกเขาในโหนดเอาต์พุต - ลิงก์ลิงก์ไปยังพาเรนต์ตามลำดับนี้

    การมีโครงสร้างดังกล่าวจะไม่เป็นการยากที่จะสร้างการนำเสนอที่ดีจริงๆใน HTML

    CTEs ซ้ำที่มีอยู่ในPostgreSQL, IBM DB2, MS SQL Server และ Oracle

    หากคุณต้องการอ่านเพิ่มเติมเกี่ยวกับแบบสอบถาม SQL แบบเรียกซ้ำคุณสามารถตรวจสอบเอกสารของ DBMS ที่คุณชื่นชอบหรืออ่านบทความทั้งสองของฉันที่ครอบคลุมหัวข้อนี้:


18

ตั้งแต่ Oracle 9i คุณสามารถใช้ CONNECT BY

SELECT LPAD(' ', (LEVEL - 1) * 4) || "Name" AS "Name"
FROM (SELECT * FROM TMP_NODE ORDER BY "Order")
CONNECT BY PRIOR "Id" = "ParentId"
START WITH "Id" IN (SELECT "Id" FROM TMP_NODE WHERE "ParentId" = 0)

ตั้งแต่ SQL Server 2005 คุณสามารถใช้นิพจน์ตารางแบบเรียกซ้ำ (CTE) แบบเรียกซ้ำได้

WITH [NodeList] (
  [Id]
  , [ParentId]
  , [Level]
  , [Order]
) AS (
  SELECT [Node].[Id]
    , [Node].[ParentId]
    , 0 AS [Level]
    , CONVERT([varchar](MAX), [Node].[Order]) AS [Order]
  FROM [Node]
  WHERE [Node].[ParentId] = 0
  UNION ALL
  SELECT [Node].[Id]
    , [Node].[ParentId]
    , [NodeList].[Level] + 1 AS [Level]
    , [NodeList].[Order] + '|'
      + CONVERT([varchar](MAX), [Node].[Order]) AS [Order]
  FROM [Node]
    INNER JOIN [NodeList] ON [NodeList].[Id] = [Node].[ParentId]
) SELECT REPLICATE(' ', [NodeList].[Level] * 4) + [Node].[Name] AS [Name]
FROM [Node]
  INNER JOIN [NodeList] ON [NodeList].[Id] = [Node].[Id]
ORDER BY [NodeList].[Order]

ทั้งสองจะแสดงผลลัพธ์ต่อไปนี้

ชื่อ
'โหนด 1'
'โหนด 1.1'
'โหนด 1.1.1'
'โหนด 1.2'
'โหนด 2'
'โหนด 2.1'

cte สามารถใช้ได้ทั้ง sqlserver และ oracle @Eric Weilnau
Nisar

9

คำตอบของ Bill ค่อนข้างดีเอ้ยดีคำตอบนี้เพิ่มบางสิ่งลงไปซึ่งทำให้ฉันต้องการคำตอบที่สนับสนุนดังนั้นเธรด

อย่างไรก็ตามฉันต้องการสนับสนุนโครงสร้างต้นไม้และคุณสมบัติคำสั่งซื้อ ฉันรวมคุณสมบัติเดียวในแต่ละโหนดที่เรียกleftSiblingว่าทำสิ่งเดียวกันOrderเพื่อให้ทำในคำถามเดิม (คงลำดับจากซ้ายไปขวา)

mysql> โหนดล่าง;
+ + ------------- -------------- + ------ + ----- + ------- - + + ----------------
| ฟิลด์ | ประเภท | ว่างเปล่า คีย์ | เริ่มต้น | พิเศษ |
+ + ------------- -------------- + ------ + ----- + ------- - + + ----------------
| id | int (11) | ไม่ PRI | NULL | auto_increment |
| ชื่อ | varchar (255) | ใช่ | | NULL | |
| ซ้าย int (11) | ไม่ | 0 | |
+ + ------------- -------------- + ------ + ----- + ------- - + + ----------------
3 แถวในชุด (0.00 วินาที)

mysql> การเติมคำลงมา;
+ ------------ + --------- + ------ + ----- + --------- + --- ------------- +
| ฟิลด์ | ประเภท | ว่างเปล่า คีย์ | เริ่มต้น | พิเศษ |
+ ------------ + --------- + ------ + ----- + --------- + --- ------------- +
| ความสัมพันธ์รหัส | int (11) | ไม่ PRI | NULL | auto_increment |
| parent | int (11) | ไม่ | NULL | |
| เด็ก | int (11) | ไม่ | NULL | |
| pathLen | int (11) | ไม่ | NULL | |
+ ------------ + --------- + ------ + ----- + --------- + --- ------------- +
4 แถวในชุด (0.00 วินาที)

รายละเอียดเพิ่มเติมและ SQL รหัสบนบล็อกของฉัน

ขอบคุณบิลคำตอบของคุณมีประโยชน์ในการเริ่มต้น!


7

เมื่อได้รับเลือกฉันจะใช้วัตถุ ฉันจะสร้างวัตถุสำหรับแต่ละระเบียนที่แต่ละวัตถุมีการchildrenรวบรวมและเก็บไว้ในอาร์เรย์ assoc (/ hashtable) โดยที่ Id เป็นกุญแจสำคัญ และสายฟ้าแลบผ่านคอลเลกชันครั้งเดียวเพิ่มเด็ก ๆ ลงในช่องเด็กที่เกี่ยวข้อง ง่าย

แต่เนื่องจากคุณไม่ได้สนุกด้วยการ จำกัด การใช้ OOP ที่ดีบางอย่างฉันอาจทำซ้ำตาม:

function PrintLine(int pID, int level)
    foreach record where ParentID == pID
        print level*tabs + record-data
        PrintLine(record.ID, level + 1)

PrintLine(0, 0)

แก้ไข: นี่คล้ายกับรายการอื่นสองสามรายการ แต่ฉันคิดว่ามันสะอาดกว่าเล็กน้อย สิ่งหนึ่งที่ฉันจะเพิ่ม: นี่เป็น SQL-intensive อย่างยิ่ง มันเป็นเรื่องที่น่ารังเกียจ หากคุณมีทางเลือกให้ไปที่เส้นทาง OOP


นั่นคือสิ่งที่ฉันหมายถึงด้วย "ไม่มีกรอบ" - คุณกำลังใช้งาน LINQ ใช่ไหม? เกี่ยวกับย่อหน้าแรกของคุณ: ชุดผลลัพธ์มีอยู่แล้วทำไมคัดลอกข้อมูลทั้งหมดไปยังโครงสร้างวัตถุใหม่ก่อน (ฉันไม่ชัดเจนพอเกี่ยวกับข้อเท็จจริงนั้นขอโทษ)
Tomalak

Tomalak - ไม่มีรหัสหลอกรหัส แน่นอนว่าคุณต้องแบ่งสิ่งต่าง ๆ ออกเป็นตัวเลือกและตัววนซ้ำที่เหมาะสม ... และไวยากรณ์จริง! ทำไมต้อง OOP เพราะคุณสามารถสะท้อนโครงสร้าง มันช่วยให้สิ่งต่าง ๆ ดีขึ้นและมันก็จะมีประสิทธิภาพมากขึ้น (เลือกได้เพียงตัวเดียวเท่านั้น)
344 Oli

ฉันไม่ได้เลือกซ้ำแล้วซ้ำอีกในใจ เกี่ยวกับ OOP: Mark Bessey กล่าวในคำตอบของเขาว่า: "คุณสามารถเลียนแบบโครงสร้างข้อมูลอื่นด้วย hashmap ดังนั้นมันจึงไม่ใช่ข้อ จำกัด ที่แย่มาก" วิธีแก้ปัญหาของคุณถูกต้อง แต่ฉันคิดว่ามีการปรับปรุงบางอย่างแม้ไม่มี OOP
Tomalak

5

สิ่งนี้ถูกเขียนขึ้นอย่างรวดเร็วและไม่ได้สวยและมีประสิทธิภาพ (รวมทั้ง autoboxes มากการแปลงระหว่างintและIntegerน่ารำคาญ!) แต่ก็ใช้งานได้

อาจแตกกฎตั้งแต่ฉันสร้างวัตถุของฉันเอง แต่เดี๋ยวก่อนฉันทำสิ่งนี้เพื่อเบี่ยงเบนจากการทำงานจริง :)

สิ่งนี้ยังอนุมานว่า resultSet / table ถูกอ่านอย่างสมบูรณ์ในโครงสร้างบางประเภทก่อนที่คุณจะเริ่มสร้างโหนดซึ่งจะไม่เป็นทางออกที่ดีที่สุดหากคุณมีหลายแสนแถว

public class Node {

    private Node parent = null;

    private List<Node> children;

    private String name;

    private int id = -1;

    public Node(Node parent, int id, String name) {
        this.parent = parent;
        this.children = new ArrayList<Node>();
        this.name = name;
        this.id = id;
    }

    public int getId() {
        return this.id;
    }

    public String getName() {
        return this.name;
    }

    public void addChild(Node child) {
        children.add(child);
    }

    public List<Node> getChildren() {
        return children;
    }

    public boolean isRoot() {
        return (this.parent == null);
    }

    @Override
    public String toString() {
        return "id=" + id + ", name=" + name + ", parent=" + parent;
    }
}

public class NodeBuilder {

    public static Node build(List<Map<String, String>> input) {

        // maps id of a node to it's Node object
        Map<Integer, Node> nodeMap = new HashMap<Integer, Node>();

        // maps id of a node to the id of it's parent
        Map<Integer, Integer> childParentMap = new HashMap<Integer, Integer>();

        // create special 'root' Node with id=0
        Node root = new Node(null, 0, "root");
        nodeMap.put(root.getId(), root);

        // iterate thru the input
        for (Map<String, String> map : input) {

            // expect each Map to have keys for "id", "name", "parent" ... a
            // real implementation would read from a SQL object or resultset
            int id = Integer.parseInt(map.get("id"));
            String name = map.get("name");
            int parent = Integer.parseInt(map.get("parent"));

            Node node = new Node(null, id, name);
            nodeMap.put(id, node);

            childParentMap.put(id, parent);
        }

        // now that each Node is created, setup the child-parent relationships
        for (Map.Entry<Integer, Integer> entry : childParentMap.entrySet()) {
            int nodeId = entry.getKey();
            int parentId = entry.getValue();

            Node child = nodeMap.get(nodeId);
            Node parent = nodeMap.get(parentId);
            parent.addChild(child);
        }

        return root;
    }
}

public class NodePrinter {

    static void printRootNode(Node root) {
        printNodes(root, 0);
    }

    static void printNodes(Node node, int indentLevel) {

        printNode(node, indentLevel);
        // recurse
        for (Node child : node.getChildren()) {
            printNodes(child, indentLevel + 1);
        }
    }

    static void printNode(Node node, int indentLevel) {
        StringBuilder sb = new StringBuilder();
        for (int i = 0; i < indentLevel; i++) {
            sb.append("\t");
        }
        sb.append(node);

        System.out.println(sb.toString());
    }

    public static void main(String[] args) {

        // setup dummy data
        List<Map<String, String>> resultSet = new ArrayList<Map<String, String>>();
        resultSet.add(newMap("1", "Node 1", "0"));
        resultSet.add(newMap("2", "Node 1.1", "1"));
        resultSet.add(newMap("3", "Node 2", "0"));
        resultSet.add(newMap("4", "Node 1.1.1", "2"));
        resultSet.add(newMap("5", "Node 2.1", "3"));
        resultSet.add(newMap("6", "Node 1.2", "1"));

        Node root = NodeBuilder.build(resultSet);
        printRootNode(root);

    }

    //convenience method for creating our dummy data
    private static Map<String, String> newMap(String id, String name, String parentId) {
        Map<String, String> row = new HashMap<String, String>();
        row.put("id", id);
        row.put("name", name);
        row.put("parent", parentId);
        return row;
    }
}

ฉันมักจะพบว่ามันยากที่จะกรองส่วนของอัลกอริทึมจากส่วนเฉพาะของการนำไปปฏิบัติเมื่อนำเสนอด้วยรหัสที่มาจำนวนมาก นั่นเป็นเหตุผลที่ฉันถามหาวิธีแก้ปัญหาที่ไม่เฉพาะภาษาในตอนแรก แต่มันทำงานได้ดังนั้นขอบคุณสำหรับเวลาของคุณ!
Tomalak

ฉันเห็นสิ่งที่คุณหมายถึงตอนนี้ถ้าไม่ชัดเจนว่าอัลกอริทึมหลักอยู่ใน NodeBuilder.build () - ฉันอาจทำผลงานได้ดีขึ้นในการสรุปเรื่องนี้
matt b

5

มีวิธีแก้ปัญหาที่ดีมากซึ่งใช้ประโยชน์จากการเป็นตัวแทน btree ภายในของดัชนี SQL สิ่งนี้มีพื้นฐานมาจากการวิจัยที่ยอดเยี่ยมซึ่งทำขึ้นเมื่อราวปี 2541

นี่คือตารางตัวอย่าง (ใน mysql)

CREATE TABLE `node` (
  `id` int(10) unsigned NOT NULL AUTO_INCREMENT,
  `name` varchar(255) NOT NULL,
  `tw` int(10) unsigned NOT NULL,
  `pa` int(10) unsigned DEFAULT NULL,
  `sz` int(10) unsigned DEFAULT NULL,
  `nc` int(11) GENERATED ALWAYS AS (tw+sz) STORED,
  PRIMARY KEY (`id`),
  KEY `node_tw_index` (`tw`),
  KEY `node_pa_index` (`pa`),
  KEY `node_nc_index` (`nc`),
  CONSTRAINT `node_pa_fk` FOREIGN KEY (`pa`) REFERENCES `node` (`tw`) ON DELETE CASCADE
)

ฟิลด์เดียวที่จำเป็นสำหรับการแสดงต้นไม้คือ:

  • tw: ดัชนีการสั่งซื้อล่วงหน้า DFS จากซ้ายไปขวาโดยที่ root = 1
  • pa: การอ้างอิง (โดยใช้ tw) ไปยังโหนดพาเรนต์ root มีค่าเป็น null
  • sz: ขนาดของสาขาของโหนดรวมถึงตัวมันเอง
  • nc: ใช้เป็นน้ำตาลทราย มันเป็น tw + nc และแสดงถึง tw ของโหนด "ลูกถัดไป"

นี่คือตัวอย่างประชากร 24 โหนดเรียงลำดับโดย tw:

+-----+---------+----+------+------+------+
| id  | name    | tw | pa   | sz   | nc   |
+-----+---------+----+------+------+------+
|   1 | Root    |  1 | NULL |   24 |   25 |
|   2 | A       |  2 |    1 |   14 |   16 |
|   3 | AA      |  3 |    2 |    1 |    4 |
|   4 | AB      |  4 |    2 |    7 |   11 |
|   5 | ABA     |  5 |    4 |    1 |    6 |
|   6 | ABB     |  6 |    4 |    3 |    9 |
|   7 | ABBA    |  7 |    6 |    1 |    8 |
|   8 | ABBB    |  8 |    6 |    1 |    9 |
|   9 | ABC     |  9 |    4 |    2 |   11 |
|  10 | ABCD    | 10 |    9 |    1 |   11 |
|  11 | AC      | 11 |    2 |    4 |   15 |
|  12 | ACA     | 12 |   11 |    2 |   14 |
|  13 | ACAA    | 13 |   12 |    1 |   14 |
|  14 | ACB     | 14 |   11 |    1 |   15 |
|  15 | AD      | 15 |    2 |    1 |   16 |
|  16 | B       | 16 |    1 |    1 |   17 |
|  17 | C       | 17 |    1 |    6 |   23 |
| 359 | C0      | 18 |   17 |    5 |   23 |
| 360 | C1      | 19 |   18 |    4 |   23 |
| 361 | C2(res) | 20 |   19 |    3 |   23 |
| 362 | C3      | 21 |   20 |    2 |   23 |
| 363 | C4      | 22 |   21 |    1 |   23 |
|  18 | D       | 23 |    1 |    1 |   24 |
|  19 | E       | 24 |    1 |    1 |   25 |
+-----+---------+----+------+------+------+

ผลของต้นไม้ทุกต้นสามารถทำได้แบบไม่วนซ้ำ ตัวอย่างเช่นเพื่อรับรายการบรรพบุรุษของโหนดที่ tw = '22 '

บรรพบุรุษ

select anc.* from node me,node anc 
where me.tw=22 and anc.nc >= me.tw and anc.tw <= me.tw 
order by anc.tw;
+-----+---------+----+------+------+------+
| id  | name    | tw | pa   | sz   | nc   |
+-----+---------+----+------+------+------+
|   1 | Root    |  1 | NULL |   24 |   25 |
|  17 | C       | 17 |    1 |    6 |   23 |
| 359 | C0      | 18 |   17 |    5 |   23 |
| 360 | C1      | 19 |   18 |    4 |   23 |
| 361 | C2(res) | 20 |   19 |    3 |   23 |
| 362 | C3      | 21 |   20 |    2 |   23 |
| 363 | C4      | 22 |   21 |    1 |   23 |
+-----+---------+----+------+------+------+

พี่น้องและเด็กเป็นเรื่องสำคัญ - เพียงใช้ฟิลด์สั่งซื้อโดย tw

ลูกหลาน

ตัวอย่างเช่นชุด (สาขา) ของโหนดที่ถูกรูทที่ tw = 17

select des.* from node me,node des 
where me.tw=17 and des.tw < me.nc and des.tw >= me.tw 
order by des.tw;
+-----+---------+----+------+------+------+
| id  | name    | tw | pa   | sz   | nc   |
+-----+---------+----+------+------+------+
|  17 | C       | 17 |    1 |    6 |   23 |
| 359 | C0      | 18 |   17 |    5 |   23 |
| 360 | C1      | 19 |   18 |    4 |   23 |
| 361 | C2(res) | 20 |   19 |    3 |   23 |
| 362 | C3      | 21 |   20 |    2 |   23 |
| 363 | C4      | 22 |   21 |    1 |   23 |
+-----+---------+----+------+------+------+

หมายเหตุเพิ่มเติม

วิธีการนี้มีประโยชน์อย่างยิ่งเมื่อมีจำนวนการอ่านมากกว่าที่มีการแทรกหรืออัพเดท

เนื่องจากการแทรกการเคลื่อนไหวหรือการอัปเดตของโหนดในทรีต้องมีการปรับแต่งทรีจึงจำเป็นต้องล็อคตารางก่อนเริ่มดำเนินการ

ค่าใช้จ่ายในการแทรก / ลบสูงเนื่องจากค่าดัชนี tw และ sz (ขนาดสาขา) จะต้องได้รับการอัปเดตบนโหนดทั้งหมดหลังจากจุดแทรกและบรรพบุรุษทั้งหมดตามลำดับ

การย้ายสาขาเกี่ยวข้องกับการย้ายค่า tw ของสาขาออกนอกระยะดังนั้นจึงจำเป็นต้องปิดการใช้งานข้อ จำกัด กุญแจต่างประเทศเมื่อทำการย้ายสาขา มีคำถามสี่ข้อที่จำเป็นในการย้ายสาขา:

  • ย้ายสาขาออกจากระยะ
  • ปิดช่องว่างที่เหลือ (ต้นไม้ที่เหลืออยู่จะถูกทำให้เป็นมาตรฐาน)
  • เปิดช่องว่างที่จะไป
  • ย้ายสาขาไปสู่ตำแหน่งใหม่

ปรับการสืบค้นต้นไม้

การเปิด / ปิดช่องว่างในทรีเป็นฟังก์ชันย่อยที่สำคัญที่ใช้โดยวิธีการสร้าง / อัปเดต / ลบดังนั้นฉันจึงรวมไว้ที่นี่

เราต้องการสองพารามิเตอร์ - ค่าสถานะแสดงว่าเราลดขนาดหรือลดขนาดและดัชนี tw ของโหนดหรือไม่ ตัวอย่างเช่น tw = 18 (ซึ่งมีขนาดสาขาเท่ากับ 5) สมมติว่าเรากำลังลดขนาด (ลบ tw) - ซึ่งหมายความว่าเรากำลังใช้ '-' แทน '+' ในการอัปเดตตัวอย่างต่อไปนี้

ก่อนอื่นเราใช้ฟังก์ชั่นบรรพบุรุษ (เปลี่ยนแปลงเล็กน้อย) เพื่ออัปเดตค่า sz

update node me, node anc set anc.sz = anc.sz - me.sz from 
node me, node anc where me.tw=18 
and ((anc.nc >= me.tw and anc.tw < me.pa) or (anc.tw=me.pa));

จากนั้นเราจำเป็นต้องปรับ tw สำหรับผู้ที่ tw สูงกว่ากิ่งที่จะลบออก

update node me, node anc set anc.tw = anc.tw - me.sz from 
node me, node anc where me.tw=18 and anc.tw >= me.tw;

จากนั้นเราต้องปรับพาเรนต์สำหรับผู้ที่ tw ของ pa สูงกว่ากิ่งที่จะถูกลบ

update node me, node anc set anc.pa = anc.pa - me.sz from 
node me, node anc where me.tw=18 and anc.pa >= me.tw;

3

สมมติว่าคุณรู้ว่าองค์ประกอบรากเป็นศูนย์นี่คือรหัสเทียมที่ส่งออกไปยังข้อความ:

function PrintLevel (int curr, int level)
    //print the indents
    for (i=1; i<=level; i++)
        print a tab
    print curr \n;
    for each child in the table with a parent of curr
        PrintLevel (child, level+1)


for each elementID where the parentid is zero
    PrintLevel(elementID, 0)

3

คุณสามารถเลียนแบบโครงสร้างข้อมูลอื่น ๆ ด้วย hashmap ดังนั้นจึงไม่มีข้อ จำกัด อย่างยิ่ง การสแกนจากด้านบนไปด้านล่างคุณสร้าง hashmap สำหรับแต่ละแถวของฐานข้อมูลโดยมีรายการสำหรับแต่ละคอลัมน์ เพิ่ม hashmaps แต่ละรายการเหล่านี้ลงใน hashmap "master" โดยพิมพ์รหัส หากโหนดใดมี "พาเรนต์" ที่คุณยังไม่เห็นให้สร้างรายการตัวยึดสำหรับมันในต้นแบบ hashmap และเติมเมื่อคุณเห็นโหนดจริง

หากต้องการพิมพ์ออกให้ทำขั้นตอนแรกในเชิงลึกอย่างง่าย ๆ ผ่านข้อมูลติดตามระดับเยื้องไปพร้อมกัน คุณสามารถทำให้ง่ายขึ้นโดยเก็บรายการ "children" สำหรับแต่ละแถวและเติมข้อมูลเมื่อคุณสแกนข้อมูล

สำหรับว่ามีวิธี "ดีกว่า" ในการจัดเก็บแผนภูมิในฐานข้อมูลหรือไม่นั้นขึ้นอยู่กับว่าคุณจะใช้ข้อมูลอย่างไร ฉันเคยเห็นระบบที่มีความลึกสูงสุดที่รู้จักซึ่งใช้ตารางที่แตกต่างกันสำหรับแต่ละระดับในลำดับชั้น นั่นทำให้รู้สึกว่าถ้าระดับในต้นไม้ไม่เท่ากันหลังจากทั้งหมด (หมวดหมู่ระดับบนสุดจะแตกต่างจากใบไม้)


1

หากสามารถสร้างแผนที่แฮชหรืออาร์เรย์แบบซ้อนได้ฉันสามารถลงตารางจากจุดเริ่มต้นและเพิ่มแต่ละรายการลงในอาร์เรย์ที่ซ้อนกันได้ ฉันต้องติดตามแต่ละบรรทัดไปยังโหนดรูทเพื่อที่จะทราบว่าระดับใดในอาเรย์ซ้อนที่จะแทรกเข้าไป ฉันสามารถใช้การบันทึกช่วยจำเพื่อที่ฉันไม่จำเป็นต้องค้นหาผู้ปกครองเดิมซ้ำแล้วซ้ำอีก

แก้ไข: ฉันจะอ่านทั้งตารางลงในอาร์เรย์ก่อนดังนั้นมันจะไม่สืบค้น DB ซ้ำ ๆ แน่นอนว่านี่จะไม่เป็นประโยชน์หากโต๊ะของคุณมีขนาดใหญ่มาก

หลังจากสร้างโครงสร้างแล้วฉันจะต้องเจาะลึกเข้าไปก่อนแล้วจึงพิมพ์ HTML ออกมา

ไม่มีวิธีพื้นฐานที่ดีกว่าในการจัดเก็บข้อมูลนี้โดยใช้ตารางเดียว (ฉันอาจผิดได้;) และชอบที่จะเห็นวิธีแก้ปัญหาที่ดีกว่า) อย่างไรก็ตามหากคุณสร้างแบบแผนเพื่อใช้ตาราง db ที่สร้างขึ้นแบบไดนามิกคุณก็จะเปิดโลกใหม่ที่ต้องเสียสละความเรียบง่ายและความเสี่ยงของ SQL hell;)


1
ฉันไม่ต้องการเปลี่ยนเลย์เอาต์ DB เนื่องจากต้องการโหนดย่อยระดับใหม่ :-)
Tomalak

1

หากองค์ประกอบอยู่ในลำดับต้นไม้ดังที่แสดงในตัวอย่างของคุณคุณสามารถใช้บางอย่างเช่น Python ตัวอย่างต่อไปนี้:

delimiter = '.'
stack = []
for item in items:
  while stack and not item.startswith(stack[-1]+delimiter):
    print "</div>"
    stack.pop()
  print "<div>"
  print item
  stack.append(item)

สิ่งนี้จะรักษาสแต็คที่แสดงถึงตำแหน่งปัจจุบันในต้นไม้ สำหรับแต่ละองค์ประกอบในตารางจะปรากฏองค์ประกอบสแต็ก (ปิด divs ที่ตรงกัน) จนกว่าจะพบพาเรนต์ของรายการปัจจุบัน จากนั้นจะส่งเอาต์พุตจุดเริ่มต้นของโหนดนั้นและส่งไปยังสแต็ก

หากคุณต้องการส่งออกต้นไม้โดยใช้การเยื้องแทนองค์ประกอบที่ซ้อนกันคุณสามารถข้ามคำสั่งการพิมพ์เพื่อพิมพ์ divs และพิมพ์จำนวนช่องว่างเท่ากับขนาดของสแต็กบางส่วนก่อนแต่ละรายการ ตัวอย่างเช่นใน Python:

print "  " * len(stack)

คุณสามารถใช้วิธีนี้ในการสร้างรายการหรือพจนานุกรมที่ซ้อนกันได้อย่างง่ายดาย

แก้ไข: ฉันเห็นจากการชี้แจงของคุณว่าชื่อไม่ได้มีวัตถุประสงค์เพื่อเป็นเส้นทางโหนด นั่นแสดงให้เห็นวิธีการอื่น:

idx = {}
idx[0] = []
for node in results:
  child_list = []
  idx[node.Id] = child_list
  idx[node.ParentId].append((node, child_list))

สิ่งนี้สร้างทรีอาร์เรย์ของ tuples (!) idx [0] หมายถึงรากของต้นไม้ แต่ละองค์ประกอบในอาเรย์เป็น 2-tuple ซึ่งประกอบไปด้วยตัวโหนดเองและรายการลูกทั้งหมด เมื่อสร้างแล้วคุณสามารถยึด idx [0] และทิ้ง idx ยกเว้นว่าคุณต้องการเข้าถึงโหนดด้วย ID


1

ในการขยายโซลูชัน SQL ของ Bill คุณสามารถทำเช่นเดียวกันโดยใช้ flat array ยิ่งไปกว่านั้นถ้าสตริงทั้งหมดของคุณมีความยาวเท่ากันและคุณสามารถทราบจำนวนสูงสุดของลูกได้ (พูดในต้นไม้ไบนารี) คุณสามารถทำได้โดยใช้สตริงเดียว (อาร์เรย์อักขระ) หากคุณมีจำนวนเด็กโดยพลการสิ่งนี้ซับซ้อนเล็กน้อย ... ฉันจะต้องตรวจสอบบันทึกเก่าของฉันเพื่อดูสิ่งที่สามารถทำได้

จากนั้นเสียสละความทรงจำเล็กน้อยโดยเฉพาะอย่างยิ่งถ้าต้นไม้ของคุณกระจัดกระจายและ / หรือไม่สมดุลคุณสามารถใช้คณิตศาสตร์ดัชนีนิดหน่อยเข้าถึงสตริงทั้งหมดโดยการสุ่มโดยจัดเก็บต้นไม้ความกว้างเป็นอันดับแรกในอาร์เรย์ (เช่นสำหรับไบนารี ต้นไม้):

String[] nodeArray = [L0root, L1child1, L1child2, L2Child1, L2Child2, L2Child3, L2Child4] ...

คุณรู้ความยาวสตริงของคุณคุณรู้ไหม

ตอนนี้ฉันทำงานอยู่จึงไม่สามารถใช้เวลากับมันได้มากนัก แต่ด้วยความสนใจฉันสามารถเอาโค้ดมาทำสิ่งนี้ได้

เราใช้เพื่อค้นหาในต้นไม้ไบนารีที่ทำจาก DNA codons ซึ่งเป็นกระบวนการสร้างต้นไม้จากนั้นเราแบนเพื่อค้นหารูปแบบข้อความและเมื่อพบแม้ว่าคณิตศาสตร์ดัชนี (กลับด้านบน) เราได้รับโหนดกลับมา ... มาก รวดเร็วและมีประสิทธิภาพต้นไม้ของเราไม่ค่อยมีโหนดว่างเปล่า แต่เราสามารถใช้ข้อมูลขนาดกิกะไบต์ได้ในเวลาอันรวดเร็ว


0

คิดเกี่ยวกับการใช้เครื่องมือ nosql เช่น neo4j สำหรับโครงสร้างลำดับชั้น เช่นแอปพลิเคชันเครือข่ายเช่น linkedin ใช้ couchbase (โซลูชัน nosql อื่น)

แต่ใช้ nosql เฉพาะสำหรับการสอบถามระดับ data-mart และไม่เก็บ / รักษาธุรกรรม


เมื่ออ่านความซับซ้อนและสมบูรณ์ของ SQL และโครงสร้าง "ที่ไม่ใช่ตาราง" นี่เป็นความคิดแรกของฉันด้วย nosql แน่นอนว่ามีปัญหามากมายในการส่งออก ฯลฯ นอกจากนี้ OP ยังกล่าวถึงเฉพาะตาราง โอ้ดี ฉันไม่ใช่ผู้เชี่ยวชาญ DB เพราะเห็นได้ชัด
Josef.B
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.