SQL和Delphi:用于从表创建树的递归机制

Mik*_*ail 12 mysql sql delphi recursion

我正在使用的DBMS是MySQL,编程环境是Delphi 7(这个例子并不重要).

我有一个名为'subject'的表格,我将所有书籍主题存储在系统中.受试者可以有亲子关系,比如科学可以分为数学和物理,而数学可以细分为微积分,代数,几何和我们去.

我想要的是创建一个填充了该表中日期的树.拜托,帮帮我吧.它甚至与您用于说明目的的语言无关,它只是伪代码.

Subject表的数据库图如下所示:

在此输入图像描述

Subject表定义:

DROP TABLE IF EXISTS subject;
CREATE TABLE IF NOT EXISTS subject (                  # Comment
    subject_id  INT UNSIGNED NOT NULL AUTO_INCREMENT, # Subject ID
    subject     VARCHAR(25)  NOT NULL,                # Subject name
    parent_id   INT UNSIGNED     NULL DEFAULT NULL,   # Parent ID as seen from
    PRIMARY KEY (subject_id),                         # the diagram refers to
    UNIQUE (subject),                                 # the subject_id field
    INDEX (parent_id),
    CONSTRAINT fk_subject_parent
    FOREIGN KEY (parent_id)
        REFERENCES subject (subject_id)
            ON DELETE RESTRICT
            ON UPDATE CASCADE
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
Run Code Online (Sandbox Code Playgroud)

使用一些虚拟数据填充Subject表:

INSERT INTO subject (subject, parent_id) VALUES
                    ('Science',    NULL),
                    ('Mathematics',   1),
                    ('Calculus',      2),
                    ('Algebra',       2),
                    ('Geometry',      2),
                    ('Languages',  NULL),
                    ('English',       6),
                    ('Latin',         6);
Run Code Online (Sandbox Code Playgroud)

SELECT语句返回:

SELECT * FROM subject;
????????????????????????????????????????
? subject_id ?   subject   ? parent_id ?
????????????????????????????????????????
?          1 ? Science     ?      NULL ?
?          2 ? Mathematics ?         1 ?
?          3 ? Calculus    ?         2 ?
?          4 ? Algebra     ?         2 ?
?          5 ? Geometry    ?         2 ?
?          6 ? Languages   ?      NULL ?
?          7 ? English     ?         6 ?
?          8 ? Latin       ?         6 ?
????????????????????????????????????????
Run Code Online (Sandbox Code Playgroud)

存储过程:

DELIMITER$$

DROP PROCEDURE IF EXISTS get_parent_subject_list;
CREATE PROCEDURE get_parent_subject_list ()
BEGIN
    SELECT subject_id, subject
    FROM subject
    WHERE parent_id IS NULL
    ORDER BY subject ASC;
END$$


DROP PROCEDURE IF EXISTS get_child_subject_list;
CREATE PROCEDURE get_child_subject_list (IN parentID INT)
BEGIN
    SELECT subject_id, subject
    FROM subject
    WHERE parent_id = parentID
    ORDER BY subject ASC;
END$$

DELIMITER ;
Run Code Online (Sandbox Code Playgroud)

接下来是我的Delphi程序,它试图用数据填充树视图,但是可以进一步看出,它不能比第二级更深入:

procedure TForm1.CreateSubjectTreeView(Sender: TObject);
var
    i : integer;
begin
    i := 0;

    q1.SQL.Clear;
    q1.SQL.Add('CALL get_parent_subject_list()');
    q1.Open;
    q1.First;

    while not q1.EOF do
    begin
        TreeView.Items.Add(nil, q1.Fields[1].Value);

        q2.SQL.Clear;
        q2.SQL.Add('CALL get_child_subject_list(' +
                    VarToStr(q1.Fields[0].Value) + ')');
        q2.Open;
        q2.First;

        while not q2.EOF do
        begin
            TreeView.Items.AddChild(TreeView.Items.Item[i], q2.Fields[1].Value);
            q2.Next;
        end;

        i := TreeView.Items.Count;
        q1.Next;
    end;
end;
Run Code Online (Sandbox Code Playgroud)

这就是这段代码的作用:

+- Science
|   |
|   +- Mathematics
|
+- Languages
    |
    +- English
    +- Latin
Run Code Online (Sandbox Code Playgroud)

但我希望它看起来像这样:

+- Science
|   |
|   +- Mathematics
|       |
|       +- Calculus
|       +- Algebra
|       +- Geometry
|
+- Languages
    |
    +- English
    +- Latin
Run Code Online (Sandbox Code Playgroud)

Ari*_*The 5

我建议你不要一次装满整棵树,为什么要这样?目前没有人可以查看上千种物品.它可能很长,你的程序看起来很冷.它在网络和服务器上产生了巨大的负载.

您最好使用VirtualTreeView方法,其中每个项目根据请求加载其子项目.这将需要一个参数化准备的查询,如

 Select ID, Title, This, That from TREE where Parent_ID = :ID
Run Code Online (Sandbox Code Playgroud)

是的,不要为每个项目创建新的SQL文本.它既危险又缓慢(您需要删除为旧请求收集的所有数据并解析新请求)

您应该进行一个参数化查询,Prepare然后关闭/更改参数值/打开.

请访问http://bobby-tables.com/查看原因和Delphi示例


"一次加载所有"的一个例子就是从Delphi中的sql server表动态创建弹出菜单树 - 虽然我不认为对于或多或少的大树来说是匆忙的好方法.

关于这种方法的注意事项:你填写根元素,然后你找到一种方式来填充元素,但尚未填充,但已被其他人引用,直到最后没有这样的元素.

当然,你可以递归地执行它,遍历树到它的结尾 - 但这会要求许多嵌套的数据库查询.

您可以创建递归SQL请求,但它可能非常依赖于服务器,并且RDBMS引擎通常会对递归深度施加限制.

树控制方法可能稍差一些,但RDBMS上更干净,更容易TQueue,只需添加一个专门添加的树项.加载一些元素 - 最初是所有根元素 - 你在队列中记住它.然后从队列中逐个删除并填写(加载和入队)其子项.直到队列变空.