艰难的T-SQL显示组织结构图(层次结构/递归)

时间:2010-07-22 13:36:37

标签: sql-server sql-server-2005 hierarchical-data

请不要指向我一篇关于如何在SQL中创建树结构或CTE的文章我已经阅读了很多内容!我觉得这对于t-sql来说可能并不那么难,但对我来说这绝对是艰难的:)。

在这种情况下,我必须创建一个如下所示的报告:

alt text http://img85.imageshack.us/img85/6372/70337249.png

当我的存储过程(SQL Server sproc)的参数设置为“All”时,这很有效,因为这只会抓取所有数据,最终用户可以展开/折叠项目以查看层次结构。例如,当我运行报告并选择一个名称时,例如在这种情况下,“Kevin Bicking”会看到结果:

alt text http://img69.imageshack.us/img69/8398/46964880.png

这个问题是我只得到kevin的直接报告,但实际上我需要查看所有子指示。例如,在第一张图片中,我希望我的报告显示所有低于凯文,低于开尔文和低于蒂姆等人的人。

我理解这个问题,但我不知道如何在T-SQL中处理它。这是我的存储过程:

CREATE PROCEDURE [dbo].[rptContactsHierarchy]
@ContactID varchar(100)='All'
AS
BEGIN
  SET NOCOUNT ON;    

 SELECT 
  c1.id AS EmployeeID,
  c2.id as ManagerID, 
  c1.first_name + ' ' + c1.last_name AS [EmployeeName], 
  c1.title AS Title, 
  c2.first_name + ' ' + c2.last_name AS [ReportsTo]
FROM 
  Contacts c1
INNER JOIN 
  Contacts c2
ON 
  c1.reports_to_id = c2.id
WHERE 
  c1.deleted=0
  AND (@ContactID='All' OR (c2.first_name + ' ' + c2.last_name = @ContactID OR (c1.first_name + ' ' + c1.last_name = @ContactID)))
END

sproc工作得很好,没有错误,但我的问题是使用我在这里列出的字段如何更改它以获得其他名称下的直接报告,如上所述。基本上,EmployeeName字段每次都是顶级(即报表参数),ReportsTo别名是您在图像中看到的报表上的字段。

我没有关于SSRS报告的问题,只是关于如何修改查询,以便在这种情况下我选择Kevin Bicking并将其传递给我的存储过程。它目前只返回直接员工Kelvin Squires。但我想要它回归的不仅仅是开尔文,而是向凯尔文报告的所有人,以及所有可能是开尔文下的老板但也有直接报告的人。

非常感谢任何帮助。谢谢你的时间!

编辑部分

我正在使用sql server 2005.有人要求表定义,请注意我没有创建这个表,它是一个自动生成的基于CRM的系统:

USE [sugarcrm]
GO
/****** Object:  Table [dbo].[contacts]    Script Date: 07/22/2010 10:44:31 ******/
SET ANSI_NULLS OFF
GO
SET QUOTED_IDENTIFIER ON
GO
SET ANSI_PADDING OFF
GO
CREATE TABLE [dbo].[contacts](
    [id] [varchar](36) COLLATE SQL_Latin1_General_CP1_CI_AS NOT NULL,
    [date_entered] [datetime] NULL,
    [date_modified] [datetime] NULL,
    [modified_user_id] [varchar](36) COLLATE SQL_Latin1_General_CP1_CI_AS NULL,
    [created_by] [varchar](36) COLLATE SQL_Latin1_General_CP1_CI_AS NULL,
    [description] [text] COLLATE SQL_Latin1_General_CP1_CI_AS NULL,
    [deleted] [bit] NULL DEFAULT ('0'),
    [assigned_user_id] [varchar](36) COLLATE SQL_Latin1_General_CP1_CI_AS NULL,
    [team_id] [varchar](36) COLLATE SQL_Latin1_General_CP1_CI_AS NULL,
    [salutation] [varchar](5) COLLATE SQL_Latin1_General_CP1_CI_AS NULL,
    [first_name] [varchar](100) COLLATE SQL_Latin1_General_CP1_CI_AS NULL,
    [last_name] [varchar](100) COLLATE SQL_Latin1_General_CP1_CI_AS NULL,
    [title] [varchar](100) COLLATE SQL_Latin1_General_CP1_CI_AS NULL,
    [department] [varchar](255) COLLATE SQL_Latin1_General_CP1_CI_AS NULL,
    [do_not_call] [bit] NULL DEFAULT ('0'),
    [phone_home] [varchar](25) COLLATE SQL_Latin1_General_CP1_CI_AS NULL,
    [phone_mobile] [varchar](25) COLLATE SQL_Latin1_General_CP1_CI_AS NULL,
    [phone_work] [varchar](25) COLLATE SQL_Latin1_General_CP1_CI_AS NULL,
    [phone_other] [varchar](25) COLLATE SQL_Latin1_General_CP1_CI_AS NULL,
    [phone_fax] [varchar](25) COLLATE SQL_Latin1_General_CP1_CI_AS NULL,
    [primary_address_street] [varchar](150) COLLATE SQL_Latin1_General_CP1_CI_AS NULL,
    [primary_address_city] [varchar](100) COLLATE SQL_Latin1_General_CP1_CI_AS NULL,
    [primary_address_state] [varchar](100) COLLATE SQL_Latin1_General_CP1_CI_AS NULL,
    [primary_address_postalcode] [varchar](20) COLLATE SQL_Latin1_General_CP1_CI_AS NULL,
    [primary_address_country] [varchar](255) COLLATE SQL_Latin1_General_CP1_CI_AS NULL,
    [alt_address_street] [varchar](150) COLLATE SQL_Latin1_General_CP1_CI_AS NULL,
    [alt_address_city] [varchar](100) COLLATE SQL_Latin1_General_CP1_CI_AS NULL,
    [alt_address_state] [varchar](100) COLLATE SQL_Latin1_General_CP1_CI_AS NULL,
    [alt_address_postalcode] [varchar](20) COLLATE SQL_Latin1_General_CP1_CI_AS NULL,
    [alt_address_country] [varchar](255) COLLATE SQL_Latin1_General_CP1_CI_AS NULL,
    [assistant] [varchar](75) COLLATE SQL_Latin1_General_CP1_CI_AS NULL,
    [assistant_phone] [varchar](25) COLLATE SQL_Latin1_General_CP1_CI_AS NULL,
    [lead_source] [varchar](100) COLLATE SQL_Latin1_General_CP1_CI_AS NULL,
    [reports_to_id] [varchar](36) COLLATE SQL_Latin1_General_CP1_CI_AS NULL,
    [birthdate] [datetime] NULL,
    [portal_name] [varchar](255) COLLATE SQL_Latin1_General_CP1_CI_AS NULL,
    [portal_active] [bit] NOT NULL DEFAULT ('0'),
    [portal_password] [varchar](32) COLLATE SQL_Latin1_General_CP1_CI_AS NULL,
    [portal_app] [varchar](255) COLLATE SQL_Latin1_General_CP1_CI_AS NULL,
    [campaign_id] [varchar](36) COLLATE SQL_Latin1_General_CP1_CI_AS NULL,
 CONSTRAINT [pk_contacts] PRIMARY KEY CLUSTERED 
(
    [id] ASC
)WITH (IGNORE_DUP_KEY = OFF) ON [PRIMARY]
) ON [PRIMARY] TEXTIMAGE_ON [PRIMARY]

GO
SET ANSI_PADDING OFF

解决方案

在你们的帮助下,这里是我的解决方案

set ANSI_NULLS ON
set QUOTED_IDENTIFIER ON
go





-- =============================================
-- Author:      <Author,,Name>
-- Create date: <Create Date,,>
-- Description: <Description,,>
-- =============================================
ALTER PROCEDURE [dbo].[rptContactsHierarchy]
@ContactID varchar(100)='All'
AS
BEGIN
SET NOCOUNT ON;

--grab id of @contactid
DECLARE @Test varchar(36)
SELECT @Test = (SELECT id FROM contacts c1 WHERE c1.first_name + ' ' + c1.last_name = @ContactID)


;WITH StaffTree AS 
( 
    SELECT  
        c.id, 
        c.Title, 
        c.first_name, 
        c.last_name, 
        c.reports_to_id, 
        c.reports_to_id as Manager_id, 
        cc.first_name AS Manager_first_name, 
        cc.last_name as Manager_last_name, 
        cc.first_name + ' ' + cc.last_name AS [ReportsTo], 
        c.first_name + ' ' + c.last_name as EmployeeName,  
        1 AS LevelOf 
        FROM Contacts                  c 
            LEFT OUTER JOIN Contacts  cc ON c.reports_to_id=cc.id 
        WHERE c.id=@Test OR (@Test IS NULL AND c.reports_to_id IS NULL) 
    UNION ALL 
        SELECT  
        s.id, 
        s.Title, 
        s.first_name, 
        s.last_name, 
        s.reports_to_id, 
        t.id, 
        t.first_name, 
        t.last_name, 
        t.first_name + ' ' + t.last_name, 
        s.first_name + ' ' + s.last_name,
        t.LevelOf+1 
        FROM StaffTree            t 
            INNER JOIN Contacts  s ON t.id=s.reports_to_id 
    WHERE s.reports_to_id=@Test OR @Test IS NULL OR t.LevelOf>1 
)
SELECT * FROM StaffTree 

END

2 个答案:

答案 0 :(得分:5)

使用递归查询。关于该主题的MSDN article使用了与您的相似的示例。在您的情况下,您将选择Kevin的条目作为锚定义。试试这个(完全未经测试):

CREATE PROCEDURE [dbo].[rptContactsHierarchy]
@ContactID varchar(100)='All'
AS
BEGIN
    WITH ManagerEmployee (ManagerID, EmployeeID, first_name, last_name, title)
    AS
    (
        -- Anchor
        SELECT ManagerID, EmployeeID, first_name, last_name, title
          FROM Contacts
         WHERE EmployeeID = @ContactID

        UNION ALL

        -- Recursion
        SELECT ManagerID, EmployeeID, first_name, last_name, title
          FROM Contacts c
          JOIN ManagerEmployee me ON (me.EmployeeID = c.ManagerID)
    )
    SELECT ManagerID,
           EmployeeID,
           first_name + ' ' + last_name AS EmployeeName,
           title as Title
    FROM ManagerEmployee
END

答案 1 :(得分:5)

编辑基于OP的表格:

这是一个使用OP表定义中的列的示例,我的示例数据如下:

              1-Jerome
                |
              2-Joe
            /       \
     3-Paul          6-David
    /      \            /    \
 4-Jack  5-Daniel   7-Ian    8-Helen


--I only included the needed columns from the OP's table here
DECLARE @Contacts table (id varchar(36), first_name varchar(100), reports_to_id varchar(36))
INSERT @Contacts VALUES ('1','Jerome', NULL )
INSERT @Contacts VALUES ('2','Joe'   ,'1')
INSERT @Contacts VALUES ('3','Paul'  ,'2')
INSERT @Contacts VALUES ('4','Jack'  ,'3')
INSERT @Contacts VALUES ('5','Daniel','3')
INSERT @Contacts VALUES ('6','David' ,'2')
INSERT @Contacts VALUES ('7','Ian'   ,'6')
INSERT @Contacts VALUES ('8','Helen' ,'6')

DECLARE @Root_id  char(4)

--get complete tree---------------------------------------------------
SET @Root_id=null
PRINT '@Root_id='+COALESCE(''''+@Root_id+'''','null')
;WITH StaffTree AS
(
    SELECT 
        c.id, c.first_name, c.reports_to_id, c.reports_to_id as Manager_id, cc.first_name AS Manager_first_name, 1 AS LevelOf
        FROM @Contacts                  c
            LEFT OUTER JOIN @Contacts  cc ON c.reports_to_id=cc.id
        WHERE c.id=@Root_id OR (@Root_id IS NULL AND c.reports_to_id IS NULL)
    UNION ALL
        SELECT 
            s.id, s.first_name, s.reports_to_id, t.id, t.first_name, t.LevelOf+1
        FROM StaffTree            t
            INNER JOIN @Contacts  s ON t.id=s.reports_to_id
    WHERE s.reports_to_id=@Root_id OR @Root_id IS NULL OR t.LevelOf>1
)
SELECT * FROM StaffTree


--get all below 2---------------------------------------------------
SET @Root_id=2
PRINT '@Root_id='+COALESCE(''''+@Root_id+'''','null')
;WITH StaffTree AS
(
    SELECT 
        c.id, c.first_name, c.reports_to_id, c.reports_to_id as Manager_id, cc.first_name AS Manager_first_name, 1 AS LevelOf
        FROM @Contacts                  c
            LEFT OUTER JOIN @Contacts  cc ON c.reports_to_id=cc.id
        WHERE c.id=@Root_id OR (@Root_id IS NULL AND c.reports_to_id IS NULL)
    UNION ALL
        SELECT 
            s.id, s.first_name, s.reports_to_id, t.id, t.first_name, t.LevelOf+1
        FROM StaffTree            t
            INNER JOIN @Contacts  s ON t.id=s.reports_to_id
    WHERE s.reports_to_id=@Root_id OR @Root_id IS NULL OR t.LevelOf>1
)
SELECT * FROM StaffTree

--get all below 6---------------------------------------------------
SET @Root_id=6
PRINT '@Root_id='+COALESCE(''''+@Root_id+'''','null')
;WITH StaffTree AS
(
    SELECT 
        c.id, c.first_name, c.reports_to_id, c.reports_to_id as Manager_id, cc.first_name AS Manager_first_name, 1 AS LevelOf
        FROM @Contacts                  c
            LEFT OUTER JOIN @Contacts  cc ON c.reports_to_id=cc.id
        WHERE c.id=@Root_id OR (@Root_id IS NULL AND c.reports_to_id IS NULL)
    UNION ALL
        SELECT 
            s.id, s.first_name, s.reports_to_id, t.id, t.first_name, t.LevelOf+1
        FROM StaffTree            t
            INNER JOIN @Contacts  s ON t.id=s.reports_to_id
    WHERE s.reports_to_id=@Root_id OR @Root_id IS NULL OR t.LevelOf>1
)
SELECT * FROM StaffTree

输出:

@Root_id=null
id     first_name reports_to_id Manager_id Manager_first_name LevelOf
------ ---------- ------------- ---------- ------------------ -----------
1      Jerome     NULL          NULL       NULL               1
2      Joe        1             1          Jerome             2
3      Paul       2             2          Joe                3
6      David      2             2          Joe                3
7      Ian        6             6          David              4
8      Helen      6             6          David              4
4      Jack       3             3          Paul               4
5      Daniel     3             3          Paul               4

(8 row(s) affected)

@Root_id='2   '
id     first_name reports_to_id Manager_id Manager_first_name LevelOf
------ ---------- ------------- ---------- ------------------ -----------
2      Joe        1             1          Jerome             1
3      Paul       2             2          Joe                2
6      David      2             2          Joe                2
7      Ian        6             6          David              3
8      Helen      6             6          David              3
4      Jack       3             3          Paul               3
5      Daniel     3             3          Paul               3

(7 row(s) affected)

@Root_id='6   '
id     first_name reports_to_id Manager_id Manager_first_name LevelOf
------ ---------- ------------- ---------- ------------------ -----------
6      David      2             2          Joe                1
7      Ian        6             6          David              2
8      Helen      6             6          David              2

(3 row(s) affected)