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)
我建议你不要一次装满整棵树,为什么要这样?目前没有人可以查看上千种物品.它可能很长,你的程序看起来很冷.它在网络和服务器上产生了巨大的负载.
您最好使用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,只需添加一个专门添加的树项.加载一些元素 - 最初是所有根元素 - 你在队列中记住它.然后从队列中逐个删除并填写(加载和入队)其子项.直到队列变空.