Count Of Level in parent table

  • hello all.i have a parent table:

    subjectid subjectname parentid organizationid

    4058 x -1 576

    4059 x1 4058 576

    4060 x2 4058 576

    4061 x3 4058 576

    4062 x11 4059 576

    4063 x111 4062 576

    now i want to have count of level of my table?i want a query wich teturns 3 in this example

  • Something like this?

    WITH SampleData (subjectid, subjectname, parentid, organizationid) AS

    (

    SELECT 4058,'x', -1, 576

    UNION ALL SELECT 4059,'x1',4058, 576

    UNION ALL SELECT 4060,'x2',4058, 576

    UNION ALL SELECT 4061,'x3',4058, 576

    UNION ALL SELECT 4062,'x11',4059, 576

    UNION ALL SELECT 4063,'x111',4062, 576

    ),

    rCTE AS

    (

    SELECT n=0, * -- Assign parent to level=0

    FROM SampleData

    WHERE parentid = -1

    UNION ALL

    SELECT n+1, b.*

    FROM rCTE a

    JOIN SampleData b ON a.subjectid = b.parentid

    )

    SELECT * -- [Level]=MAX(n)

    FROM rCTE;


    My mantra: No loops! No CURSORs! No RBAR! Hoo-uh![/I]

    My thought question: Have you ever been told that your query runs too fast?

    My advice:
    INDEXing a poor-performing query is like putting sugar on cat food. Yeah, it probably tastes better but are you sure you want to eat it?
    The path of least resistance can be a slippery slope. Take care that fixing your fixes of fixes doesn't snowball and end up costing you more than fixing the root cause would have in the first place.

    Need to UNPIVOT? Why not CROSS APPLY VALUES instead?[/url]
    Since random numbers are too important to be left to chance, let's generate some![/url]
    Learn to understand recursive CTEs by example.[/url]
    [url url=http://www.sqlservercentral.com/articles/St

  • --SAMPLE DATA

    --NOTE: IF YOU LAY OUT YOUR SAMPLE DATA LIKE THIS, PEOPLE ARE MORE LIKELY TO HELP YOU!

    IF object_id('tempdb..#testEnvironment') IS NOT NULL

    BEGIN;

    DROP TABLE #testEnvironment;

    END;

    SELECT subjectid, subjectname, parentid, organizationid

    INTO #testEnvironment

    FROM (VALUES(4058, 'x', -1, 576),(4059, 'x1', 4058, 576),(4060, 'x2', 4058, 576),

    (4061, 'x3', 4058, 576),(4062, 'x11', 4059, 576),(4063, 'x111', 4062, 576)

    )a(subjectid, subjectname, parentid, organizationid);

    --OPTION 1

    WITH CTE AS

    (

    SELECT subjectid, subjectname, parentid, organizationid, 0 AS level

    FROM #testEnvironment

    WHERE parentid = -1

    UNION ALL

    SELECT b.subjectid, b.subjectname, b.parentid, b.organizationid, a.level+1

    FROM CTE a

    INNER JOIN #testEnvironment b ON a.subjectid = b.parentid

    )

    SELECT subjectid, subjectname, parentid, organizationid, level, MAX(level) OVER() AS MaxLevel

    FROM CTE;

    --OPTION 2

    SELECT subjectid, subjectname, parentid, organizationid, level, MAX(level) OVER() AS MaxLevel

    FROM (SELECT subjectid, subjectname, parentid, organizationid,

    DENSE_RANK() OVER(ORDER BY N)-1 AS level

    FROM #testEnvironment a

    OUTER APPLY (SELECT subjectid

    FROM #testEnvironment

    WHERE subjectid = a.parentid

    )oa(N)

    )innerQuery;

    Returns: -

    subjectid subjectname parentid organizationid level MaxLevel

    ----------- ----------- ----------- -------------- ----------- -----------

    4058 x -1 576 0 3

    4059 x1 4058 576 1 3

    4060 x2 4058 576 1 3

    4061 x3 4058 576 1 3

    4062 x11 4059 576 2 3

    4063 x111 4062 576 3 3

    subjectid subjectname parentid organizationid level MaxLevel

    ----------- ----------- ----------- -------------- -------------------- --------------------

    4058 x -1 576 0 3

    4059 x1 4058 576 1 3

    4060 x2 4058 576 1 3

    4061 x3 4058 576 1 3

    4062 x11 4059 576 2 3

    4063 x111 4062 576 3 3


    Forever trying to learn
    My blog - http://www.cadavre.co.uk/
    For better, quicker answers on T-SQL questions, click on the following...http://www.sqlservercentral.com/articles/Best+Practices/61537/
    For better, quicker answers on SQL Server performance related questions, click on the following...http://www.sqlservercentral.com/articles/SQLServerCentral/66909/

  • Cadavre (10/2/2013)


    --SAMPLE DATA

    --NOTE: IF YOU LAY OUT YOUR SAMPLE DATA LIKE THIS, PEOPLE ARE MORE LIKELY TO HELP YOU!

    +1

    Cadavre (10/2/2013)


    --OPTION 1

    ...

    --OPTION 2

    I choose option 1!


    My mantra: No loops! No CURSORs! No RBAR! Hoo-uh![/I]

    My thought question: Have you ever been told that your query runs too fast?

    My advice:
    INDEXing a poor-performing query is like putting sugar on cat food. Yeah, it probably tastes better but are you sure you want to eat it?
    The path of least resistance can be a slippery slope. Take care that fixing your fixes of fixes doesn't snowball and end up costing you more than fixing the root cause would have in the first place.

    Need to UNPIVOT? Why not CROSS APPLY VALUES instead?[/url]
    Since random numbers are too important to be left to chance, let's generate some![/url]
    Learn to understand recursive CTEs by example.[/url]
    [url url=http://www.sqlservercentral.com/articles/St

  • dwain.c (10/2/2013)


    I choose option 1!

    Got to agree, the logic isn't quite right for the outer apply method. The below uses a sproc written by Jeff Moden for generating a hierarchy.

    --===== Do this in a nice, safe place that everyone has

    USE tempdb;

    SET NOCOUNT ON;

    GO

    IF OBJECT_ID('dbo.BuildLargeEmployeeTable','P') IS NOT NULL

    DROP PROCEDURE dbo.BuildLargeEmployeeTable;

    GO

    CREATE PROCEDURE dbo.BuildLargeEmployeeTable

    /****************************************************************************

    Purpose:

    Create a randomized "well formed" Adjacency List hierarchy with indexes.

    Progammer's Notes:

    1. Each EmployeeID (except for the Root Node, of course) is assigned a

    random ManagerID number which is initially always less than the current

    EmployeeID to ensure that no cycles occur in the hierarcy.

    2. The second parameter used to call this stored procedure will optionally

    randomize the EmployeeIDss to make the hierarchy truly random as it would

    likely be in real life. This, of course, takes a small amounnt of extra

    time.

    3. This code runs nasty fast and is great for testing hierarchical

    processing code. Including the index builds, this code will build a

    million node Adjacency List on a 4 processor (i5) laptop with 6GB of RAM

    in just several seconds. The optional randomization adds just several

    more seconds.

    Usage:

    --===== Create the hierarchy where all the ManagerIDs are less than the

    -- EmployeeIDs. This is the fastest option and will build a million node

    -- hierarchy in just about 7 seconds on a modern machine.

    EXEC dbo.BuildLargeEmployeeTable 1000000;

    --===== Making the second parameter a non-zero value will further randomize

    -- the IDs in the hierarchy. This, of course, takes extra time and will

    -- build a million row hierarchy in about 17 seconds on a modern

    -- machine.

    EXEC dbo.BuildLargeEmployeeTable 1000000,1;

    Revision History:

    Initial concept and creation - Circa 2009 - Jeff Moden

    Rev 01 - 15 May 2010 - Jeff Moden

    - Abort if current DB isn't "tempdb" to protect users.

    Rev 02 - 13 Oct 2012 - Jeff Moden

    - Add a randomization stop to make the hierarchy more like real life.

    ****************************************************************************/

    --===== Declare the I/O parameters

    @pRowsToBuild INT,

    @pRandomize TINYINT = 0

    AS

    --===========================================================================

    -- Presets

    --===========================================================================

    --===== Supresss the autodisplay of rowcounts to cleanup the display and to

    -- prevent false error returns if called from a GUI.

    SET NOCOUNT ON;

    --===== Make sure that we're in a safe place to run this...

    IF DB_NAME() <> N'tempdb'

    BEGIN

    RAISERROR('Current DB is NOT tempdb. Run aborted.',11,1);

    RETURN;

    END;

    --===== Conditionaly drop the test table so we can do reruns more easily

    IF OBJECT_ID('tempdb.dbo.Employee','U') IS NOT NULL

    DROP TABLE tempdb.dbo.Employee;

    --===========================================================================

    RAISERROR('Building the hierarchy...',0,1) WITH NOWAIT;

    --===========================================================================

    --===== Build the test table and populate it on the fly.

    -- Everything except ManagerID is populated here. The code uses a

    -- technique called a "Psuedo-Cursor" (kudos to R. Barry Young for the

    -- term) to very quickly and easily build large numbers of rows.

    SELECT TOP (@pRowsToBuild)

    EmployeeID = ISNULL(CAST(

    ROW_NUMBER() OVER (ORDER BY (SELECT 1))

    AS INT),0),

    ManagerID = CAST(NULL AS INT),

    EmployeeName = CAST(NEWID() AS VARCHAR(36))

    INTO tempdb.dbo.Employee

    FROM master.sys.all_columns ac1

    CROSS JOIN master.sys.all_columns ac2

    CROSS JOIN master.sys.all_columns ac3

    ;

    RAISERROR('There are %u rows in the hierarchy.',0,1,@@ROWCOUNT) WITH NOWAIT;

    --===== Update the test table with ManagerID's. The ManagerID is some random

    -- value which is always less than the current EmployeeID to keep the

    -- hierarchy "clean" and free from "loop backs".

    UPDATE tempdb.dbo.Employee

    SET ManagerID = CASE

    WHEN EmployeeID > 1

    THEN ABS(CHECKSUM(NEWID())) % (EmployeeID-1) +1

    ELSE NULL

    END

    ;

    --===========================================================================

    -- Conditionally randomize the hierarchy to be more like real life

    --===========================================================================

    IF @pRandomize <> 0

    BEGIN

    --===== Alert the operator

    RAISERROR('Randomizing the hierarchy...',0,1) WITH NOWAIT;

    --===== Create a randomized cross reference list to randomize the

    -- EmployeeIDs with.

    SELECT RandomEmployeeID = IDENTITY(INT,1,1),

    EmployeeID

    INTO #RandomXRef

    FROM dbo.Employee

    ORDER BY NEWID()

    ;

    --===== Update the ManagerIDs in the Employee table with the new

    -- randomized IDs

    UPDATE emp

    SET emp.ManagerID = RandomEmployeeID

    FROM dbo.Employee emp

    JOIN #RandomXRef xref ON emp.ManagerID = xref.EmployeeID

    ;

    --===== Update the EmployeeIDs in the Employee table with the new

    --randomized IDs

    UPDATE emp

    SET emp.EmployeeID = RandomEmployeeID

    FROM dbo.Employee emp

    JOIN #RandomXRef xref ON emp.EmployeeID = xref.EmployeeID

    ;

    END

    ELSE

    BEGIN

    --===== Alert the operator

    RAISERROR('The hierarchy is not randomized',0,1) WITH NOWAIT;

    END

    ;

    --===========================================================================

    -- Build the indexes necessary for performance.

    --===========================================================================

    --===== Alert the operator

    RAISERROR('Building the keys and indexes...',0,1) WITH NOWAIT;

    --===== Add some indexes that most folks would likely have on such a table

    ALTER TABLE tempdb.dbo.Employee

    ADD CONSTRAINT PK_Employee PRIMARY KEY CLUSTERED (EmployeeID)

    ;

    CREATE UNIQUE INDEX By_ManagerID_EmployeeID

    ON tempdb.dbo.Employee (ManagerID,EmployeeID)

    ;

    ALTER TABLE dbo.Employee

    ADD CONSTRAINT FK_Employee_Employee FOREIGN KEY

    (ManagerID) REFERENCES dbo.Employee (EmployeeID)

    ON UPDATE NO ACTION

    ON DELETE NO ACTION

    ;

    --===========================================================================

    -- Exit

    --===========================================================================

    RAISERROR('===============================================',0,1) WITH NOWAIT;

    RAISERROR('RUN COMPLETE',0,1) WITH NOWAIT;

    RAISERROR('===============================================',0,1) WITH NOWAIT;

    GO

    EXEC dbo.BuildLargeEmployeeTable 1000000,0;

    GO

    SELECT COUNT(*) AS TotalSize

    FROM tempdb.dbo.Employee;

    GO

    PRINT REPLICATE('=',80);

    PRINT 'CHECK THAT EACH QUERY GETS THE SAME RESULT';

    PRINT REPLICATE('=',80);

    DECLARE @HOLDER1 INT, @HOLDER2 INT;

    WITH CTE AS

    (

    SELECT EmployeeID, ManagerID, 0 AS level

    FROM tempdb.dbo.Employee

    WHERE ManagerID IS NULL

    UNION ALL

    SELECT b.EmployeeID, b.ManagerID, a.level+1

    FROM CTE a

    INNER JOIN tempdb.dbo.Employee b ON a.EmployeeID = b.ManagerID

    )

    SELECT @HOLDER1 = MAX(level)

    FROM CTE;

    SELECT @HOLDER2 = MAX(level)

    FROM (SELECT EmployeeID, ManagerID,

    DENSE_RANK() OVER(ORDER BY N)-1 AS level

    FROM tempdb.dbo.Employee a

    OUTER APPLY (SELECT EmployeeID

    FROM tempdb.dbo.Employee

    WHERE EmployeeID = a.ManagerID

    )oa(N)

    )innerQuery;

    SELECT @HOLDER1 AS CTEResult, @HOLDER2 AS OutApplyResult;

    GO

    PRINT REPLICATE('=',80);

    PRINT 'PERFORMANCE COMPARISON';

    PRINT REPLICATE('=',80);

    DECLARE @Loop CHAR(1) = '0', @HOLDER INT, @Duration CHAR(12), @StartTime DATETIME;

    SELECT @StartTime = GETDATE();

    SELECT @HOLDER = COUNT(*)

    FROM tempdb.dbo.Employee;

    SELECT @Duration = CONVERT(CHAR(12),GETDATE()-@StartTime,114);

    RAISERROR('BaseLine Duration: %s',0,1,@Duration) WITH NOWAIT;

    DBCC FREEPROCCACHE WITH NO_INFOMSGS;

    DBCC DROPCLEANBUFFERS WITH NO_INFOMSGS;

    RAISERROR('============',0,1) WITH NOWAIT;

    RAISERROR('============',0,1) WITH NOWAIT;

    WHILE @Loop <= 5

    BEGIN;

    RAISERROR('Loop: %s',0,1,@Loop) WITH NOWAIT;

    RAISERROR('============',0,1) WITH NOWAIT;

    RAISERROR('============',0,1) WITH NOWAIT;

    DBCC FREEPROCCACHE WITH NO_INFOMSGS;

    DBCC DROPCLEANBUFFERS WITH NO_INFOMSGS;

    SELECT @StartTime = GETDATE();

    WITH CTE AS

    (

    SELECT EmployeeID, ManagerID, 0 AS level

    FROM tempdb.dbo.Employee

    WHERE ManagerID IS NULL

    UNION ALL

    SELECT b.EmployeeID, b.ManagerID, a.level+1

    FROM CTE a

    INNER JOIN tempdb.dbo.Employee b ON a.EmployeeID = b.ManagerID

    )

    SELECT @HOLDER = MAX(level)

    FROM CTE;

    SELECT @Duration = CONVERT(CHAR(12),GETDATE()-@StartTime,114);

    RAISERROR('Recursive CTE: %s',0,1,@Duration) WITH NOWAIT;

    RAISERROR('============',0,1) WITH NOWAIT;

    DBCC FREEPROCCACHE WITH NO_INFOMSGS;

    DBCC DROPCLEANBUFFERS WITH NO_INFOMSGS;

    SELECT @StartTime = GETDATE();

    SELECT @HOLDER = MAX(level)

    FROM (SELECT EmployeeID, ManagerID,

    DENSE_RANK() OVER(ORDER BY N)-1 AS level

    FROM tempdb.dbo.Employee a

    OUTER APPLY (SELECT EmployeeID

    FROM tempdb.dbo.Employee

    WHERE EmployeeID = a.ManagerID

    )oa(N)

    )innerQuery;

    SELECT @Duration = CONVERT(CHAR(12),GETDATE()-@StartTime,114);

    RAISERROR('Outer Apply Duration: %s',0,1,@Duration) WITH NOWAIT;

    RAISERROR('============',0,1) WITH NOWAIT;

    SET @Loop = @Loop + 1;

    END;

    GO

    IF OBJECT_ID('tempdb.dbo.Employee','U') IS NOT NULL

    BEGIN;

    DROP TABLE tempdb.dbo.Employee;

    END;

    IF OBJECT_ID('dbo.BuildLargeEmployeeTable','P') IS NOT NULL

    DROP PROCEDURE dbo.BuildLargeEmployeeTable;

    GO

    Building the hierarchy...

    There are 1000000 rows in the hierarchy.

    The hierarchy is not randomized

    Building the keys and indexes...

    ===============================================

    RUN COMPLETE

    ===============================================

    TotalSize

    -----------

    1000000

    ================================================================================

    CHECK THAT EACH QUERY GETS THE SAME RESULT

    ================================================================================

    CTEResult OutApplyResult

    ----------- --------------

    33 499417

    ================================================================================

    PERFORMANCE COMPARISON

    ================================================================================

    BaseLine Duration: 00:00:00:083

    ============

    ============

    Loop: 0

    ============

    ============

    Recursive CTE: 00:00:18:707

    ============

    Outer Apply Duration: 00:00:01:503

    ============

    Loop: 1

    ============

    ============

    Recursive CTE: 00:00:19:017

    ============

    Outer Apply Duration: 00:00:01:487

    ============

    Loop: 2

    ============

    ============

    Recursive CTE: 00:00:18:267

    ============

    Outer Apply Duration: 00:00:01:477

    ============

    Loop: 3

    ============

    ============

    Recursive CTE: 00:00:19:043

    ============

    Outer Apply Duration: 00:00:01:497

    ============

    Loop: 4

    ============

    ============

    Recursive CTE: 00:00:17:413

    ============

    Outer Apply Duration: 00:00:01:440

    ============

    Loop: 5

    ============

    ============

    Recursive CTE: 00:00:18:273

    ============

    Outer Apply Duration: 00:00:01:463

    ============


    Forever trying to learn
    My blog - http://www.cadavre.co.uk/
    For better, quicker answers on T-SQL questions, click on the following...http://www.sqlservercentral.com/articles/Best+Practices/61537/
    For better, quicker answers on SQL Server performance related questions, click on the following...http://www.sqlservercentral.com/articles/SQLServerCentral/66909/

  • thank you for your reply

Viewing 6 posts - 1 through 5 (of 5 total)

You must be logged in to reply to this topic. Login to reply