Sql 继承人数据的递归查询

Sql 继承人数据的递归查询,sql,sql-server,tsql,sql-server-2014,Sql,Sql Server,Tsql,Sql Server 2014,我将数据存储在层次结构中。我需要一个可以显示层次结构的查询。所以,一张床是在一间牢房里,在一层楼里,在一栋楼里,在一个街区里 我需要做的是根据给定的位置ID来显示整个位置 一个人可能在床上,或者在牢房里,或者在地板上。。。或者在一栋楼里。我有那个位置ID。我需要做的是显示那个人所在位置的描述 例如,此人位于一个单元格中,该单元格在数据库中可能是ID350。我需要能够建立一个他的位置字符串,以显示如下内容: 2座1号楼1层2号单元 或者,用户在1号楼的某个地方,因此1号楼的ID可能是3。所以我需要

我将数据存储在层次结构中。我需要一个可以显示层次结构的查询。所以,一张床是在一间牢房里,在一层楼里,在一栋楼里,在一个街区里

我需要做的是根据给定的位置ID来显示整个位置

一个人可能在床上,或者在牢房里,或者在地板上。。。或者在一栋楼里。我有那个位置ID。我需要做的是显示那个人所在位置的描述

例如,此人位于一个单元格中,该单元格在数据库中可能是ID350。我需要能够建立一个他的位置字符串,以显示如下内容:

2座1号楼1层2号单元

或者,用户在1号楼的某个地方,因此1号楼的ID可能是3。所以我需要展示1号楼,2号楼

这两张桌子。一个保存位置,另一个只是查找类型

有没有一种有效的方法来查询这类数据?我可能还有另一个要求,显示大约数百人的where,因此查询基本上运行了很多次

我尝试了递归CTE,但我发现的示例只处理一个级别

也许函数返回一个表是最好的方法? 但问题是——根据您开始的“级别”,列可能会更少

我需要一个方法来处理这件事

CREATE TABLE [dbo].[Location]
(
 [ID] Int IDENTITY(1,1) NOT NULL,
 [ParentID] Int NULL,
 [LocationTypeID] Int NOT NULL,
 [Description] Varchar(100) NOT NULL
)
ON [PRIMARY]
go

-- Add keys for table dbo.Location

ALTER TABLE [dbo].[Location] ADD CONSTRAINT [pk_location] PRIMARY KEY ([ID])
 ON [PRIMARY]
go


CREATE TABLE [LocationType]
(
 [ID] Int IDENTITY(1,1) NOT NULL,
 [Description] Varchar(100) NOT NULL
)
ON [PRIMARY]
go

ALTER TABLE [LocationType] ADD CONSTRAINT [pk_location] PRIMARY KEY ([ID])
 ON [PRIMARY]
go
现在,我们将填充一些查找数据:

INSERT INTO LocationType (Description) VALUES ('Bed')
INSERT INTO LocationType (Description) VALUES ('Cell')
INSERT INTO LocationType (Description) VALUES ('Floor')
INSERT INTO LocationType (Description) VALUES ('Building')
INSERT INTO LocationType (Description) VALUES ('Block')
然后创建一些位置数据

-- Create the root items, which are the blocks. We'll have 2.

INSERT INTO Location (ParentId, LocationTypeID, Description) VALUES (NULL, 5, 'Block A') -- 1
INSERT INTO Location (ParentId, LocationTypeID, Description) VALUES (NULL, 5, 'Block B') -- 2

-- Now add 3 buildings per block.

-- Block A
INSERT INTO Location (ParentId, LocationTypeID, Description) VALUES (1, 4, 'Building 1') -- 3
INSERT INTO Location (ParentId, LocationTypeID, Description) VALUES (1, 4, 'Building 2') -- 4
INSERT INTO Location (ParentId, LocationTypeID, Description) VALUES (1, 4, 'Building 3') -- 5


-- Block B
INSERT INTO Location (ParentId, LocationTypeID, Description) VALUES (2, 4, 'Building 1') -- 6
INSERT INTO Location (ParentId, LocationTypeID, Description) VALUES (2, 4, 'Building 2') -- 7
INSERT INTO Location (ParentId, LocationTypeID, Description) VALUES (2, 4, 'Building 3') -- 8

-- Now add two floors per building.

-- Building 1 Block A
INSERT INTO Location (ParentId, LocationTypeID, Description) VALUES (3, 3, '1st Floor') -- 9
INSERT INTO Location (ParentId, LocationTypeID, Description) VALUES (3, 3, '2nd Floor') -- 10

-- Building 2 Block A
INSERT INTO Location (ParentId, LocationTypeID, Description) VALUES (4, 3, '1st Floor') -- 11
INSERT INTO Location (ParentId, LocationTypeID, Description) VALUES (4, 3, '2nd Floor') -- 12

-- Building 3 Block A
INSERT INTO Location (ParentId, LocationTypeID, Description) VALUES (5, 3, '1st Floor') -- 13
INSERT INTO Location (ParentId, LocationTypeID, Description) VALUES (5, 3, '2nd Floor') -- 14




-- Building 1 Block B
INSERT INTO Location (ParentId, LocationTypeID, Description) VALUES (6, 3, '1st Floor') -- 15
INSERT INTO Location (ParentId, LocationTypeID, Description) VALUES (6, 3, '2nd Floor') -- 16

-- Building 2 Block B
INSERT INTO Location (ParentId, LocationTypeID, Description) VALUES (7, 3, '1st Floor') -- 17
INSERT INTO Location (ParentId, LocationTypeID, Description) VALUES (7, 3, '2nd Floor') -- 18

-- Building 3 Block B
INSERT INTO Location (ParentId, LocationTypeID, Description) VALUES (8, 3, '1st Floor') -- 19
INSERT INTO Location (ParentId, LocationTypeID, Description) VALUES (8, 3, '2nd Floor') -- 20
-现在,只需一个光标即可填充单元格和床

DECLARE @ThisID INT
DECLARE @Cntr INT  = 0

DECLARE mycursor CURSOR FOR
SELECT id from Location where LocationTypeID = 3

OPEN mycursor

FETCH NEXT FROM mycursor
INTO @ThisID

WHILE @@FETCH_STATUS = 0
BEGIN

    SET @Cntr = 1
    WHILE(@Cntr < 20)
    BEGIN
        INSERT INTO Location (ParentId, LocationTypeID, Description) VALUES (@ThisID, 4, 'Cell ' + CAST(@Cntr AS VARCHAR))  
        SET @Cntr = @Cntr + 1
    END

    FETCH NEXT FROM mycursor
    INTO @ThisID

END

CLOSE mycursor
DEALLOCATE mycursor


-- Now add two beds per cell
DECLARE my_cursor CURSOR FOR
SELECT id from Location where LocationTypeID = 4

OPEN my_cursor

FETCH NEXT FROM my_cursor
INTO @ThisID

WHILE @@FETCH_STATUS = 0
BEGIN

    INSERT INTO Location (ParentId, LocationTypeID, Description) VALUES (@ThisID, 5, 'Bed 1')   
    INSERT INTO Location (ParentId, LocationTypeID, Description) VALUES (@ThisID, 5, 'Bed 2')   

    FETCH NEXT FROM my_cursor
    INTO @ThisID

END

CLOSE my_cursor
DEALLOCATE my_cursor

解决这个问题最简单的方法是使用左连接。但当层次结构未知或可能变得非常深时,它会变得很棘手。。如果层次结构级别可以预先确定,比如说6,那么这个SQL代码就可以工作了

SELECT
    L0.ID,
    L0.[Description] + 
    ISNULL(', ' + L1.[Description], '')  +
    ISNULL(', ' + L2.[Description], '')  +
    ISNULL(', ' + L3.[Description], '')  +
    ISNULL(', ' + L4.[Description], '')  +
    ISNULL(', ' + L5.[Description], '') FullLocation
FROM Location L0
    LEFT JOIN Location L1 ON L1.ID = L0.[ParentID]
    LEFT JOIN Location L2 ON L2.ID = L1.[ParentID]
    LEFT JOIN Location L3 ON L3.ID = L2.[ParentID]
    LEFT JOIN Location L4 ON L4.ID = L3.[ParentID]
    LEFT JOIN Location L5 ON L5.ID = L4.[ParentID]
WHERE
    L0.ID IN (21, 111, 190)
结果

╔═════╦═════════════════════════════════════════╗
║ ID  ║              FullLocation               ║
╠═════╬═════════════════════════════════════════╣
║  21 ║ Cell 1, 1st Floor, Building 1, Block A  ║
║ 111 ║ Cell 15, 1st Floor, Building 3, Block A ║
║ 190 ║ Cell 18, 1st Floor, Building 2, Block B ║
╚═════╩═════════════════════════════════════════╝

下面是一个递归CTE解决方案:

WITH rCTE AS(
    SELECT
        l.*, CAST(l.Description AS VARCHAR(MAX)) AS FullLoc
    FROM Location l
    INNER JOIN LocationType  lt ON lt.ID = l.LocationTypeID
    WHERE l.ParentID IS NULL

    UNION ALL

    SELECT
        l.*, CAST(l.Description AS VARCHAR(MAX)) + ', ' + r.FullLoc
    FROM Location l
    INNER JOIN LocationType  lt ON lt.ID = l.LocationTypeID
    INNER JOIN rCTE r
        ON r.ID = l.ParentID
)
SELECT
    ID, FullLoc
FROM rCTE 
WHERE ID IN(21, 111, 190)
ORDER BY ID
OPTION (MAXRECURSION 0)

在Oracle中,您可以使用connect by来解决此问题,但在sql server中,我不确定是否有一种简单的方法来解决此问题,我听说过CTE,但对此并不熟悉