2010-07-22 114 views
3

請不要指向我關於如何創建樹結構或SQL中的CTE的文章我已閱讀了大量文章!我認爲這對t-sql來說可能並不是那麼艱難,但對我來說這絕對是困難的:)。艱難的T-SQL顯示組織結構圖(層次結構/遞歸)

這裏的情況,我要創建一個報告,如下所示:

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

當參數到我的存儲過程(SQL Server的存儲過程)設置爲「所有」這個偉大的工程因爲這隻會抓取所有數據,最終用戶可以展開/摺疊項目以查看層次結構。出現問題時,比如我運行報告並選擇一個名稱,如在這種情況下,「凱文Bicking」看到的結果是:

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

這樣做的問題是,我只得到凱文的直接報告但我實際上需要看到所有的子指導。例如,在第一張圖片中,我希望我的報告能夠顯示凱文以下的所有人,以及凱爾文以下和蒂姆等等。

我理解這個問題,但我不知道如何在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 

存儲過程的正常工作,在它裏面沒有錯誤,但我的問題是用我的,我在這裏列出我如何可以改變它來獲取下彼此直接下屬領域名字,如上所述。基本上,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 [email protected] 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 [email protected] OR @Test IS NULL OR t.LevelOf>1 
) 
SELECT * FROM StaffTree 

END 
+0

哪個版本的SQL Server是你使用? – 2010-07-22 14:09:53

+0

@Tom H. Im使用SQL Server 2005 – oJM86o 2010-07-22 14:11:59

回答

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(''''[email protected]_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 [email protected]_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 [email protected]_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(''''[email protected]_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 [email protected]_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 [email protected]_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(''''[email protected]_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 [email protected]_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 [email protected]_id OR @Root_id IS NULL OR t.LevelOf>1 
) 
SELECT * FROM StaffTree 

OUTPUT:

@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) 
+0

@KM - 我發佈了我的表定義在編輯這個問題我感謝你的幫助,但我試過你的例子,但似乎不能將它應用於我。 defing(唯一相關的基本上是名稱字段和id,注意到表本身的加入)。再次感謝KM! – oJM86o 2010-07-22 14:46:47

+1

@KM - 現在感覺真棒謝謝你,先生。 – oJM86o 2010-07-22 16:00:50

5

使用遞歸查詢。有關此主題的MSDN的article使用與您的示例類似的示例。在你的情況下,你會選擇凱文的條目作爲定位。試試這個(完全未經測試):

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 
+0

我已經閱讀了幾次,有沒有什麼方法可以使用我的查詢,並告訴我如何做這樣的事情。不幸的是我不是T-SQL的精明人員:( – oJM86o 2010-07-22 14:13:25

+0

我已經用我最好的猜測修正了你需要的SQL的答案 – 2010-07-22 14:27:09

+0

肯定'遞歸CTE不需要'UNION ALL',而且你需要第二個級別來限制列名以避免加入的名稱錯誤 – 2010-07-22 14:28:23