Вот один подход; очень процедурный. К сожалению, в SQL Server 2000 я не думаю, что вы сможете уйти от курсоров, если не будете использовать решение, такое как решение Питера, которое ограничено 5 уровнями и жестко кодирует типы уровней в самом запросе (смешивая данные и метаданные ). Вам придется взвесить эти ограничения с любой заметной разницей в производительности.
Обратите внимание, что я не добавил никакой обработки для циклических ссылок, так что, надеюсь, вы не допустите, чтобы это произошло другими способами.
SET NOCOUNT ON;
GO
DECLARE @foo TABLE
(
AreaID INT PRIMARY KEY,
[Level] SYSNAME,
ParentAreaID INT
);
INSERT @foo
SELECT 562, 'Campus', 0
UNION ALL SELECT 86, 'Area', 1
UNION ALL SELECT 87, 'Area', 1
UNION ALL SELECT 88, 'Area', 1
UNION ALL SELECT 90, 'Sub-Area', 86
UNION ALL SELECT 91, 'Sub-Area', 86
UNION ALL SELECT 92, 'Sub-Area', 87
UNION ALL SELECT 93, 'Sub-Area', 87
UNION ALL SELECT 94, 'Sub-Area', 88
UNION ALL SELECT 95, 'Sub-Area', 88
UNION ALL SELECT 3, 'Unit', 90
UNION ALL SELECT 16, 'Unit', 90
UNION ALL SELECT 4, 'Unit', 91
UNION ALL SELECT 6, 'Unit', 91
UNION ALL SELECT 1, 'Building', 562;
DECLARE @nest TABLE
(
NestID INT IDENTITY(1,1) PRIMARY KEY,
AreaID INT,
[Level] INT,
ParentNestID INT,
AreaIDPath VARCHAR(4000)
);
DECLARE @rc INT, @l INT;
SET @l = 0;
INSERT @nest(AreaID, [Level], AreaIDPath)
SELECT AreaID, 0, CONVERT(VARCHAR(12), AreaID)
FROM @foo
WHERE ParentAreaID = 0;
SELECT @rc = @@ROWCOUNT;
WHILE @rc >= 1
BEGIN
SELECT @l = @l + 1;
INSERT @nest(AreaID, [Level], ParentNestID)
SELECT f.AreaID, @l, n.NestID
FROM @foo AS f
INNER JOIN @nest AS n
ON f.ParentAreaID = n.AreaID
AND n.[Level] = @l - 1;
SET @rc = @@ROWCOUNT;
UPDATE n
SET n.AreaIDPath = COALESCE(n2.AreaIDPath, '')
+ '\' + CONVERT(VARCHAR(12), n.AreaID) + '\'
FROM @nest AS n
INNER JOIN @nest AS n2
ON n.ParentNestID = n2.NestID
WHERE n.[Level] = @l
AND n2.AreaIDPath NOT LIKE '%\' + CONVERT(VARCHAR(12), n.AreaID) + '\%';
END
SELECT
structure = REPLICATE(' - ', n.[Level]) + RTRIM(f.AreaID),
f.AreaID, f.[Level], f.ParentAreaID
FROM @nest AS n
INNER JOIN @foo AS f
ON n.AreaID = f.AreaID
ORDER BY n.AreaIDPath;
Это действительно то, для чего были созданы рекурсивные CTE в SQL Server 2005. (По сути, это по-прежнему курсор, но синтаксис гораздо чище, чем в описанном выше беспорядке.) До тех пор, пока вы не сможете перейти на SQL Server 2005, вам может повезти, если вы просто используете уровень представления для циклического перебора результирующего набора и упорядочивания объектов соответствующим образом, если это слишком сложно представить в операциях с запросами.