0

这是我创建的表和一些初始值。

/*Make the table*/

CREATE TABLE PEOPLE(
ID int PRIMARY KEY,
NAME varchar(100) NOT NULL,
SUPERIOR_NAME varchar(100)
);

/*Give it some initial values*/

INSERT INTO PEOPLE VALUES(1, 'A',NULL), (2, 'B', 'E'), (3, 'C', 'A'), 
(4, 'D', 'A'), (5, 'E',NULL), (6, 'F', 'D');

我需要编写一个 SQL 过程,它将返回一个人的所有下属,包括所有下属等。在这个例子中,如果我输入 A,我应该得到 C、D 和 F(D 的下属,是 A 的下属)作为输出。但是我只能达到一个级别,即 C 和 D。如何使它适用于层次结构中的任意数量的级别?我看错了吗?

这是我为一个级别编写的程序:

USE DB
GO

CREATE PROCEDURE SP_GETSUBS @NAME VARCHAR(100)

AS
BEGIN

IF @NAME IN (SELECT SUPERIOR_NAME FROM PEOPLE)
SELECT SUPERIOR_NAME AS "NAME", NAME AS "SUBORDINATE" FROM PEOPLE WHERE
SUPERIOR_NAME=@NAME;     
END

我正在考虑将第一级结果推送到一个临时表中并使用递归,但我不知道制作过程是如何逐个通过列的条目运行的。有任何想法吗?我使用 SQL Server Management Studio 2012。

4

3 回答 3

1

使用自引用公用表表达式并在您的选择中保留顶级经理(老板):

WITH OrganisationChart (Id, [Name], [Level], superior_name, [Boss]) 
AS
(
      SELECT 
            Id, [Name], 0 AS [Level], superior_name, name
      FROM
            dbo.people
      WHERE
            superior_name IS NULL
      UNION ALL
      SELECT
            emp.Id,
            emp.[Name],
            [Level] + 1,
            emp.superior_name,
            [Boss]
      FROM
            dbo.people emp
      INNER JOIN 
            OrganisationChart 
      ON
            emp.superior_name = OrganisationChart.name

)
SELECT 
    * 
FROM 
    OrganisationChart
WHERE 
    name != [Boss]

感谢Simon Ince的文章Hierarchies WITH Common Table Expressions。

于 2013-06-17T10:15:43.813 回答
0

您应该将上级名称链接到 ID 而不是名称上。这是肯定的。

您可以通过拥有 2 个临时表并进行迭代来做到这一点,而无需递归或 CTE。我会假装你有SuperiorID而不是Superior_Name

CREATE PROCEDURE SP_GETSUBS 
    @ID INT
AS
BEGIN

    DECLARE @allsubs AS TABLE(ID INT)
    DECLARE @subs AS TABLE(ID INT)
    DECLARE @temp AS TABLE(ID INT)

    --Get immediate subordinates of the ID passed in
    INSERT INTO @subs(ID)
    SELECT ID FROM PEOPLE WHERE SUPERIORID = @ID

    WHILE EXISTS(SELECT * FROM @subs)
    BEGIN
        DELETE @temp

        --copy the subordinate list
        INSERT INTO @temp
        SELECT (ID) FROM @subs

        DELETE @subs

        --get the subordinates' subordinates
        INSERT INTO @subs (ID)
        SELECT ID FROM PEOPLE WHERE SUPERIORID IN (
            SELECT ID FROM @temp
        )

        --add the subordinates to the full list
        INSERT INTO @allsubs (ID)
        SELECT ID FROM @temp

    END

    --select IDs from the full list
    SELECT * FROM PEOPLE WHERE ID IN (
        SELECT ID FROM @allsubs
    )

END

如果发生太多递归,这不会引发问题——尽管这在 SQLServer 的更高版本中不是问题。

另外 -您不应该命名以“sp_”开头的存储过程。

于 2013-06-17T11:06:59.047 回答
0

尝试这个

CREATE PROCEDURE USP_GETSUBS
(
    @NAME VARCHAR(100)
) -- USP_GETSUBS 'A'
AS
BEGIN

    IF EXISTS (SELECT SUPERIOR_NAME FROM PEOPLE WHERE Name=@NAME)
    BEGIN
        WITH Subordinates AS
        (
           SELECT p.ID, p.Name, p.SUPERIOR_NAME
           FROM PEOPLE AS p 
           WHERE p.Name = @NAME
           UNION ALL
           SELECT p.ID, p.Name, p.SUPERIOR_NAME
           FROM PEOPLE AS p
           INNER JOIN Subordinates AS sub ON p.SUPERIOR_NAME = sub.Name
        )

        SELECT s.SUPERIOR_NAME AS "NAME",s.Name AS "SUBORDINATE"
        FROM Subordinates AS s
        WHERE s.SUPERIOR_NAME IS NOT NULL   
    END 

END

SQL 小提琴演示

于 2013-06-17T09:50:03.160 回答