SQL 向上导航和 Return 顶部项目的递归查询
SQL Recursive Query to Navigate Up and Return Top Item
我有一个分层 table 用于存储零件构建。该模式很简单,table 存储一个组件及其父级。 |--Component--|--Parent--|
。我正在使用通用 Table 表达式从父部件构建部件列表,这没有问题。
我 运行 遇到的问题是试图从内部组件中确定顶级部分。我调整了 CTE 以从内部零件构建零件列表,但我 运行 遇到了返回 仅 父零件的麻烦。
使用示例可能更容易解释,因此请参阅下文。在示例中,汽车和摩托车是两个顶级部件。确定这一点的唯一方法是在 Parent
列中找到它们,但从不在 Component
列中(我无法控制架构或数据存储方式)。
CREATE TABLE Build
(
Parent VARCHAR(20),
Component VARCHAR(20)
);
INSERT INTO Build
VALUES
('Car', 'Door'),
('Door', 'Handle'),
('Handle', 'Screw'),
('Motorcycle', 'Frame'),
('Frame', 'Tank'),
('Tank', 'Internals'),
('Internals', 'Screw');
DECLARE @Comp VARCHAR(20) = 'Screw';
WITH UsedPart (Parent, Component, Level)
AS
(
SELECT
b.Parent,
b.Component,
1
FROM Build as b
WHERE b.Component = @Comp
UNION ALL
SELECT
b.Parent,
b.Component,
u.Level + 1
FROM Build as b
INNER JOIN UsedPart as u
ON b.Component = u.Parent
)
SELECT
*
FROM UsedPart
这个returns:
Parent Component Level
----------------------------------------
Handle Screw 1
Internals Screw 1
Tank Internals 2
Frame Tank 3
Motorcycle Frame 4 <-- Return this
Door Handle 2
Car Door 3 <-- Return this
我想出的唯一解决方案是 JOIN
上面的结果集带有一个像 SELECT fparent FROM Build WHERE fparent NOT IN Component
这样的子查询,但这似乎是解决问题的低效方法。
这是解决此问题的一种方法。我确定还有其他人。
DECLARE @Comp VARCHAR(20) = 'Screw';
WITH UsedPart
AS
(
SELECT
b.Parent,
b.Component,
Level = 1,
RowNum = ROW_NUMBER() over(order by parent)
FROM Build as b
WHERE b.Component = @Comp
UNION ALL
SELECT
b.Parent,
b.Component,
u.Level + 1,
u.RowNum
FROM Build as b
INNER JOIN UsedPart as u
ON b.Component = u.Parent
)
, SortedValues as
(
SELECT *
, MyVal = ROW_NUMBER() over(partition by RowNum order by Level desc)
FROM UsedPart
)
select *
from SortedValues
where MyVal = 1
您只需将 select 更改为:
SELECT u.*
FROM UsedPart u
WHERE NOT EXISTS(SELECT 1 FROM UsedPart p WHERE p.Component=u.Parent)
试试这个:
select Parent
from build
where Parent not in (select Component from build)
我有一个分层 table 用于存储零件构建。该模式很简单,table 存储一个组件及其父级。 |--Component--|--Parent--|
。我正在使用通用 Table 表达式从父部件构建部件列表,这没有问题。
我 运行 遇到的问题是试图从内部组件中确定顶级部分。我调整了 CTE 以从内部零件构建零件列表,但我 运行 遇到了返回 仅 父零件的麻烦。
使用示例可能更容易解释,因此请参阅下文。在示例中,汽车和摩托车是两个顶级部件。确定这一点的唯一方法是在 Parent
列中找到它们,但从不在 Component
列中(我无法控制架构或数据存储方式)。
CREATE TABLE Build
(
Parent VARCHAR(20),
Component VARCHAR(20)
);
INSERT INTO Build
VALUES
('Car', 'Door'),
('Door', 'Handle'),
('Handle', 'Screw'),
('Motorcycle', 'Frame'),
('Frame', 'Tank'),
('Tank', 'Internals'),
('Internals', 'Screw');
DECLARE @Comp VARCHAR(20) = 'Screw';
WITH UsedPart (Parent, Component, Level)
AS
(
SELECT
b.Parent,
b.Component,
1
FROM Build as b
WHERE b.Component = @Comp
UNION ALL
SELECT
b.Parent,
b.Component,
u.Level + 1
FROM Build as b
INNER JOIN UsedPart as u
ON b.Component = u.Parent
)
SELECT
*
FROM UsedPart
这个returns:
Parent Component Level
----------------------------------------
Handle Screw 1
Internals Screw 1
Tank Internals 2
Frame Tank 3
Motorcycle Frame 4 <-- Return this
Door Handle 2
Car Door 3 <-- Return this
我想出的唯一解决方案是 JOIN
上面的结果集带有一个像 SELECT fparent FROM Build WHERE fparent NOT IN Component
这样的子查询,但这似乎是解决问题的低效方法。
这是解决此问题的一种方法。我确定还有其他人。
DECLARE @Comp VARCHAR(20) = 'Screw';
WITH UsedPart
AS
(
SELECT
b.Parent,
b.Component,
Level = 1,
RowNum = ROW_NUMBER() over(order by parent)
FROM Build as b
WHERE b.Component = @Comp
UNION ALL
SELECT
b.Parent,
b.Component,
u.Level + 1,
u.RowNum
FROM Build as b
INNER JOIN UsedPart as u
ON b.Component = u.Parent
)
, SortedValues as
(
SELECT *
, MyVal = ROW_NUMBER() over(partition by RowNum order by Level desc)
FROM UsedPart
)
select *
from SortedValues
where MyVal = 1
您只需将 select 更改为:
SELECT u.*
FROM UsedPart u
WHERE NOT EXISTS(SELECT 1 FROM UsedPart p WHERE p.Component=u.Parent)
试试这个:
select Parent
from build
where Parent not in (select Component from build)