SQL 从 table 查询父子完整路径
SQL Query Parent Child Full Path from table
我有一个 table 列出每个元素的父子关系,如下所示:
ParentID ParentTitle ChildId ChildTitle
----------------------------------------------
843 Documents 38737 Jobs
843 Documents 52537 Tools
843 Documents 5763 SecondOps
843 Documents 4651 Materials
38737 Jobs 16619 Job001
38737 Jobs 16620 Job002
38737 Jobs 16621 Job003
38737 Jobs 16622 Job004
38737 Jobs 16623 Job005
52537 Tools 1952 HandTools
52537 Tools 1953 Automated
52537 Tools 1957 Custom
1952 HandTools 12 Cordless10mm
1952 HandTools 13 Cordless8mm
1952 HandTools 14 CableCrimp
1952 HandTools 15 Cutter
1952 HandTools 16 EdgePlane
5763 SecondOps 101 Procedure001
5763 SecondOps 102 Procedure002
5763 SecondOps 103 Procedure003
4651 Materials 33576 Raw
4651 Materials 33577 Mixed
4651 Materials 33578 Hybrid
4651 Materials 33579 Custom
16622 Job004 101 Procedure001
16622 Job004 14 CableCrimp
16622 Job004 15 Cutter
16622 Job004 4651 Mixed
16623 Job005 102 Procedure002
16623 Job005 103 Procedure003
16623 Job005 16619 Job001
16623 Job005 1953 Automated
16623 Job005 33579 Custom
16623 Job005 33576 Raw
我想使用 ID 获取每个组合的完整路径,例如
Documents\Jobs\Job003 = 843737621
另一个例子是 "Procedure001",它在 2 个地方列出
Documents\SecondOps\Procedure001 = 843631
这里也引用了相同的文档:
Documents\Jobs\Job004\Procedure001 = 8437376221
我想利用这个 table 并在 .NET 中构建一个 TreeView。因此,拥有每个项目的完整路径将使它变得轻而易举。
否则,我在想我可以从根页面开始,不断递归父级,构建子列表,然后递归那些,等等。
是否有更好的查询方法来构建这些路径?这个列表有 400,000 条记录,所以如果有更有效的方法可以节省时间
这一切最初都在 AS400 系统 DB 中,直到 2000 年左右才成为 MediaWiki 站点。我正在通过 api 提取数据,目的是为 SQL 服务器数据库构建一个接口。
我可以进行基本的 SQL 查询、连接、联合等
如果不清楚,请告诉我我可以提供哪些其他信息
如果您使用 SQL SERVER MS,您可以使用 INNER JOIN
和 LEFT JOIN
,下面是查询的样子,它将为您提供完整的结果(组合)根据您的要求:
SELECT A.ParentTitle + '\'+B.ParentTitle+
CASE WHEN C.ParentTitle IS NOT NULL THEN '\' +C.ParentTitle
ELSE ''
END
+
' =' + A.ParentID + '\'+B.ParentID+
CASE WHEN C.ParentID IS NOT NULL THEN '\' +C.ParentID
ELSE ''
END
FROM TABLE AS A
INNER JOIN TABLE AS B
ON B.ParentID = A.ChildId
LEFT JOIN TABLE AS C
ON C.ParentID = B.ChildId
不能 100% 确定它是否会像我预期的那样工作,请试一试 xD
树结构意味着通用解决方案的递归。
请不要在 sql 中尝试此操作。只需将 sql 中的数据行放入列表或类似的东西中,然后使用编程语言进行递归填充。
你的树 class 会像:
public class MyObj {
public int Id {get; set;}
public string Title {get; set;}
public List<MyObj> {get; set; } = null;
}
0.You table 大错特错。正确的方法是:
CREATE TABLE Jobs(
Id int not null primary key,
Title nvarchar(255) not null,
StartTime datetime,--optional maybe will help
ParentId int null --can be null root will have no parent
)
但我会尝试在您的 table 上解释它是如何完成的。
我假设您有某种数据上下文(DBML、EDMX 等)
- 求根或根。在您的情况下,root 将是那些在 ParentID 上但不在 ChildId 上的 nr。
将列出您的根的查询:
SELECT DISTINCT a.ParentId FROM
YourTable a LEFT JOIN
YourTable b ON a.ParentId=b.ChildId
WHERE b.ParentId is null
制作一个递归过程,将在 class 结构中检索您的数据,如上(MyObj)。
程序 MyObj GetTree(int id, db){
如果 (db.YourTable.Any(r => r.ParentId==Id)){
var q=db.YourTable.Select(r => r.ParentId==Id).ToList();
var result = new MyObj{
Id = q.ParentId,
Title = q.ParentTitle,
Children = new List<MyObj>()
}
foreach( var el in q) {
if (db.YourTable.Any(r => r.ParentId==el.ChildId))
result.Children.Add(GetTree(el.ChildId,db))
else
result.Children.Add( new MyObj{
Id = el.ChildId,
Title = el.ChildTitle,
Children = null
});
return result;
}
}
return null;
}
使用存储在列表中的第 1 点的列表 ID 创建树 假设 ListIds 你会做类似的事情:
List finaltrees = new List()</p>
<p>Ids.ForEach(id => finaltrees.Add(GetTree(id,dbcontext));
现在你在 finaltrees 中有了一个树结构。
PS:
我直接在浏览器(C#)中写代码,可能会有一些拼写错误。
所以为了详细说明我正在尝试做的事情,我正在使用不使用命名空间来建立文档路径的 wiki 版本。
例如,如果页面在这样的文档树中有 3 层深度
- 根页面
- 第01页
- 第02页
- 第03页
- 第04页
使用命名空间方法 Page03 的名称(路径)是 "RootPage:Page01:Page02:Page03"
我想对 PageID 做同样的事情
所以给定这个例子你会
- PageTitle PageId 路径
- 根页面 001 001
- Page01 101 001:101
- Page02 201 001:101:201
- Page03 301 001:101:201:301
- Page04 302 001:101:201:302
所以现在我要做的就是将 PagePath 放在一起。
这个 wiki 有几个挑战需要考虑
- 不能有 2 个文档具有相同的 TITLE
- 文档ID基本上是
无关紧要,但在这种情况下很方便(至少在我的版本中
正在处理)
- 谢天谢地,有一个页面列表及其 "Links" 或
子页面。我相信你会称它为 MANY to MANY
要记住的 要点 是,即使一个页面被列为许多其他页面的子页面,实际上也只有一个页面存在,我只需要其中一个出现在结果中。
所以使用 这里是我到达的地方
使用这个Table:
CREATE Table [dbo].[ExampleTable](
[RecordID] Int IDENTITY (1, 1) Not NULL,
[ParentID] Int Not NULL,
[ParentTitle] VARCHAR(800) NULL,
[ChildID] Int Not NULL,
[ChildTitle] VARCHAR(800) NULL,
PRIMARY KEY CLUSTERED ([RecordID] ASC),);
这个数据:
INSERT INTO [dbo].[ExampleTable]
([ParentID]
,[ParentTitle]
,[ChildID]
,[ChildTitle])
VALUES
(843,'Documents',38737,'Jobs'),
(843,'Documents',52537,'Tools'),
(843,'Documents',5763,'SecondOps'),
(843,'Documents',4651,'Materials'),
(38737,'Jobs',16619,'Job001'),
(38737,'Jobs',16620,'Job002'),
(38737,'Jobs',16621,'Job003'),
(38737,'Jobs',16622,'Job004'),
(38737,'Jobs',16623,'Job005'),
(52537,'Tools',1952,'HandTools'),
(52537,'Tools',1953,'Automated'),
(52537,'Tools',1957,'Custom'),
(1952,'HandTools',12,'Cordless10mm'),
(1952,'HandTools',13,'Cordless8mm'),
(1952,'HandTools',14,'CableCrimp'),
(1952,'HandTools',15,'Cutter'),
(1952,'HandTools',16,'EdgePlane'),
(5763,'SecondOps',101,'Procedure001'),
(5763,'SecondOps',102,'Procedure002'),
(5763,'SecondOps',103,'Procedure003'),
(4651,'Materials',33576,'Raw'),
(4651,'Materials',33577,'Mixed'),
(4651,'Materials',33578,'Hybrid'),
(4651,'Materials',33579,'Custom'),
(16622,'Job004',101,'Procedure001'),
(16622,'Job004',14,'CableCrimp'),
(16622,'Job004',15,'Cutter'),
(16622,'Job004',4651,'Mixed'),
(16623,'Job005',102,'Procedure002'),
(16623,'Job005',103,'Procedure003'),
(16623,'Job005',16619,'Job001'),
(16623,'Job005',1953,'Automated'),
(16623,'Job005',33579,'Custom'),
(16623,'Job005',33576,'Raw')
GO
还有这个查询,我从 LONG 的例子中修改而来:
SELECT DISTINCT C.ChildTitle as PageTitle, convert(varchar(20),A.ParentID) + ':' + convert(varchar(20),B.ParentID) +
CASE WHEN C.ParentID IS NOT NULL THEN ':' + convert(varchar(20),C.ParentID)
ELSE ''
END
+
CASE WHEN C.ChildID IS NOT NULL THEN ':' + convert(varchar(20),C.ChildID)
ELSE ''
END
FROM ExampleTable AS A
INNER JOIN ExampleTable AS B
ON B.ParentID = A.ChildId
LEFT JOIN ExampleTable AS C
ON C.ParentID = B.ChildId
ORDER By PageTitle
我得到这些结果:
PageTitle UnNamed
NULL 16622:4651
NULL 38737:16622
NULL 38737:16623
NULL 52537:1952
NULL 843:38737
NULL 843:4651
NULL 843:52537
NULL 843:5763
Automated 843:38737:16623:1953
CableCrimp 843:38737:16622:14
CableCrimp 843:52537:1952:14
Cordless10mm 843:52537:1952:12
Cordless8mm 843:52537:1952:13
Custom 38737:16622:4651:33579
Custom 843:38737:16623:33579
Cutter 843:38737:16622:15
Cutter 843:52537:1952:15
EdgePlane 843:52537:1952:16
Hybrid 38737:16622:4651:33578
Job001 843:38737:16623:16619
Mixed 38737:16622:4651:33577
Mixed 843:38737:16622:4651
Procedure001 843:38737:16622:101
Procedure002 843:38737:16623:102
Procedure003 843:38737:16623:103
Raw 38737:16622:4651:33576
Raw 843:38737:16623:33576
我想得到的是每个页面的单次出现,不管它恰好是哪个父页面
然后我可以使用这些路径将虚拟树结构变成实际的树结构。
最后一个问题是实际的 Link 列表与我创建的示例非常相似,只是它有 400,000 条记录。
当我 运行 这个查询针对实际 "Link List" 它 运行s 大约 17 分钟并且 运行s 内存不足 .
我一直在研究 MAXRECURSION 选项,但我仍在研究它,不知道这是否有问题。
我有一个 table 列出每个元素的父子关系,如下所示:
ParentID ParentTitle ChildId ChildTitle
----------------------------------------------
843 Documents 38737 Jobs
843 Documents 52537 Tools
843 Documents 5763 SecondOps
843 Documents 4651 Materials
38737 Jobs 16619 Job001
38737 Jobs 16620 Job002
38737 Jobs 16621 Job003
38737 Jobs 16622 Job004
38737 Jobs 16623 Job005
52537 Tools 1952 HandTools
52537 Tools 1953 Automated
52537 Tools 1957 Custom
1952 HandTools 12 Cordless10mm
1952 HandTools 13 Cordless8mm
1952 HandTools 14 CableCrimp
1952 HandTools 15 Cutter
1952 HandTools 16 EdgePlane
5763 SecondOps 101 Procedure001
5763 SecondOps 102 Procedure002
5763 SecondOps 103 Procedure003
4651 Materials 33576 Raw
4651 Materials 33577 Mixed
4651 Materials 33578 Hybrid
4651 Materials 33579 Custom
16622 Job004 101 Procedure001
16622 Job004 14 CableCrimp
16622 Job004 15 Cutter
16622 Job004 4651 Mixed
16623 Job005 102 Procedure002
16623 Job005 103 Procedure003
16623 Job005 16619 Job001
16623 Job005 1953 Automated
16623 Job005 33579 Custom
16623 Job005 33576 Raw
我想使用 ID 获取每个组合的完整路径,例如
Documents\Jobs\Job003 = 843737621
另一个例子是 "Procedure001",它在 2 个地方列出
Documents\SecondOps\Procedure001 = 843631
这里也引用了相同的文档:
Documents\Jobs\Job004\Procedure001 = 8437376221
我想利用这个 table 并在 .NET 中构建一个 TreeView。因此,拥有每个项目的完整路径将使它变得轻而易举。
否则,我在想我可以从根页面开始,不断递归父级,构建子列表,然后递归那些,等等。
是否有更好的查询方法来构建这些路径?这个列表有 400,000 条记录,所以如果有更有效的方法可以节省时间
这一切最初都在 AS400 系统 DB 中,直到 2000 年左右才成为 MediaWiki 站点。我正在通过 api 提取数据,目的是为 SQL 服务器数据库构建一个接口。
我可以进行基本的 SQL 查询、连接、联合等
如果不清楚,请告诉我我可以提供哪些其他信息
如果您使用 SQL SERVER MS,您可以使用 INNER JOIN
和 LEFT JOIN
,下面是查询的样子,它将为您提供完整的结果(组合)根据您的要求:
SELECT A.ParentTitle + '\'+B.ParentTitle+
CASE WHEN C.ParentTitle IS NOT NULL THEN '\' +C.ParentTitle
ELSE ''
END
+
' =' + A.ParentID + '\'+B.ParentID+
CASE WHEN C.ParentID IS NOT NULL THEN '\' +C.ParentID
ELSE ''
END
FROM TABLE AS A
INNER JOIN TABLE AS B
ON B.ParentID = A.ChildId
LEFT JOIN TABLE AS C
ON C.ParentID = B.ChildId
不能 100% 确定它是否会像我预期的那样工作,请试一试 xD
树结构意味着通用解决方案的递归。 请不要在 sql 中尝试此操作。只需将 sql 中的数据行放入列表或类似的东西中,然后使用编程语言进行递归填充。
你的树 class 会像:
public class MyObj {
public int Id {get; set;}
public string Title {get; set;}
public List<MyObj> {get; set; } = null;
}
0.You table 大错特错。正确的方法是:
CREATE TABLE Jobs(
Id int not null primary key,
Title nvarchar(255) not null,
StartTime datetime,--optional maybe will help
ParentId int null --can be null root will have no parent
)
但我会尝试在您的 table 上解释它是如何完成的。 我假设您有某种数据上下文(DBML、EDMX 等)
- 求根或根。在您的情况下,root 将是那些在 ParentID 上但不在 ChildId 上的 nr。
将列出您的根的查询:
SELECT DISTINCT a.ParentId FROM
YourTable a LEFT JOIN
YourTable b ON a.ParentId=b.ChildId
WHERE b.ParentId is null
制作一个递归过程,将在 class 结构中检索您的数据,如上(MyObj)。
程序 MyObj GetTree(int id, db){ 如果 (db.YourTable.Any(r => r.ParentId==Id)){
var q=db.YourTable.Select(r => r.ParentId==Id).ToList(); var result = new MyObj{ Id = q.ParentId, Title = q.ParentTitle, Children = new List<MyObj>() } foreach( var el in q) { if (db.YourTable.Any(r => r.ParentId==el.ChildId)) result.Children.Add(GetTree(el.ChildId,db)) else result.Children.Add( new MyObj{ Id = el.ChildId, Title = el.ChildTitle, Children = null }); return result; } } return null;
}
使用存储在列表中的第 1 点的列表 ID 创建树 假设 ListIds 你会做类似的事情:
List finaltrees = new List()</p> <p>Ids.ForEach(id => finaltrees.Add(GetTree(id,dbcontext));
现在你在 finaltrees 中有了一个树结构。
PS:
我直接在浏览器(C#)中写代码,可能会有一些拼写错误。
所以为了详细说明我正在尝试做的事情,我正在使用不使用命名空间来建立文档路径的 wiki 版本。
例如,如果页面在这样的文档树中有 3 层深度
- 根页面
- 第01页
- 第02页
- 第03页
- 第04页
- 第02页
- 第01页
使用命名空间方法 Page03 的名称(路径)是 "RootPage:Page01:Page02:Page03"
我想对 PageID 做同样的事情
所以给定这个例子你会
- PageTitle PageId 路径
- 根页面 001 001
- Page01 101 001:101
- Page02 201 001:101:201
- Page03 301 001:101:201:301
- Page04 302 001:101:201:302
所以现在我要做的就是将 PagePath 放在一起。
这个 wiki 有几个挑战需要考虑
- 不能有 2 个文档具有相同的 TITLE
- 文档ID基本上是 无关紧要,但在这种情况下很方便(至少在我的版本中 正在处理)
- 谢天谢地,有一个页面列表及其 "Links" 或 子页面。我相信你会称它为 MANY to MANY
要记住的 要点 是,即使一个页面被列为许多其他页面的子页面,实际上也只有一个页面存在,我只需要其中一个出现在结果中。
所以使用
使用这个Table:
CREATE Table [dbo].[ExampleTable](
[RecordID] Int IDENTITY (1, 1) Not NULL,
[ParentID] Int Not NULL,
[ParentTitle] VARCHAR(800) NULL,
[ChildID] Int Not NULL,
[ChildTitle] VARCHAR(800) NULL,
PRIMARY KEY CLUSTERED ([RecordID] ASC),);
这个数据:
INSERT INTO [dbo].[ExampleTable]
([ParentID]
,[ParentTitle]
,[ChildID]
,[ChildTitle])
VALUES
(843,'Documents',38737,'Jobs'),
(843,'Documents',52537,'Tools'),
(843,'Documents',5763,'SecondOps'),
(843,'Documents',4651,'Materials'),
(38737,'Jobs',16619,'Job001'),
(38737,'Jobs',16620,'Job002'),
(38737,'Jobs',16621,'Job003'),
(38737,'Jobs',16622,'Job004'),
(38737,'Jobs',16623,'Job005'),
(52537,'Tools',1952,'HandTools'),
(52537,'Tools',1953,'Automated'),
(52537,'Tools',1957,'Custom'),
(1952,'HandTools',12,'Cordless10mm'),
(1952,'HandTools',13,'Cordless8mm'),
(1952,'HandTools',14,'CableCrimp'),
(1952,'HandTools',15,'Cutter'),
(1952,'HandTools',16,'EdgePlane'),
(5763,'SecondOps',101,'Procedure001'),
(5763,'SecondOps',102,'Procedure002'),
(5763,'SecondOps',103,'Procedure003'),
(4651,'Materials',33576,'Raw'),
(4651,'Materials',33577,'Mixed'),
(4651,'Materials',33578,'Hybrid'),
(4651,'Materials',33579,'Custom'),
(16622,'Job004',101,'Procedure001'),
(16622,'Job004',14,'CableCrimp'),
(16622,'Job004',15,'Cutter'),
(16622,'Job004',4651,'Mixed'),
(16623,'Job005',102,'Procedure002'),
(16623,'Job005',103,'Procedure003'),
(16623,'Job005',16619,'Job001'),
(16623,'Job005',1953,'Automated'),
(16623,'Job005',33579,'Custom'),
(16623,'Job005',33576,'Raw')
GO
还有这个查询,我从 LONG 的例子中修改而来:
SELECT DISTINCT C.ChildTitle as PageTitle, convert(varchar(20),A.ParentID) + ':' + convert(varchar(20),B.ParentID) +
CASE WHEN C.ParentID IS NOT NULL THEN ':' + convert(varchar(20),C.ParentID)
ELSE ''
END
+
CASE WHEN C.ChildID IS NOT NULL THEN ':' + convert(varchar(20),C.ChildID)
ELSE ''
END
FROM ExampleTable AS A
INNER JOIN ExampleTable AS B
ON B.ParentID = A.ChildId
LEFT JOIN ExampleTable AS C
ON C.ParentID = B.ChildId
ORDER By PageTitle
我得到这些结果:
PageTitle UnNamed
NULL 16622:4651
NULL 38737:16622
NULL 38737:16623
NULL 52537:1952
NULL 843:38737
NULL 843:4651
NULL 843:52537
NULL 843:5763
Automated 843:38737:16623:1953
CableCrimp 843:38737:16622:14
CableCrimp 843:52537:1952:14
Cordless10mm 843:52537:1952:12
Cordless8mm 843:52537:1952:13
Custom 38737:16622:4651:33579
Custom 843:38737:16623:33579
Cutter 843:38737:16622:15
Cutter 843:52537:1952:15
EdgePlane 843:52537:1952:16
Hybrid 38737:16622:4651:33578
Job001 843:38737:16623:16619
Mixed 38737:16622:4651:33577
Mixed 843:38737:16622:4651
Procedure001 843:38737:16622:101
Procedure002 843:38737:16623:102
Procedure003 843:38737:16623:103
Raw 38737:16622:4651:33576
Raw 843:38737:16623:33576
我想得到的是每个页面的单次出现,不管它恰好是哪个父页面
然后我可以使用这些路径将虚拟树结构变成实际的树结构。
最后一个问题是实际的 Link 列表与我创建的示例非常相似,只是它有 400,000 条记录。
当我 运行 这个查询针对实际 "Link List" 它 运行s 大约 17 分钟并且 运行s 内存不足 .
我一直在研究 MAXRECURSION 选项,但我仍在研究它,不知道这是否有问题。