如何创建一个将列的条目作为输入逐个输入的SQL过程?

时间:2013-06-17 09:38:15

标签: sql sql-server

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

/*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。

3 个答案:

答案 0 :(得分:1)

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

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.

答案 1 :(得分: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 FIDDLE DEMO

答案 2 :(得分: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的更高版本中不是问题。

另外 - You shouldn't name stored procedures beginning 'sp_'.