Search code examples
sqlsql-serverleft-joinrecursive-queryhierarchical-query

How to write this recursive CTE for SQL Server?


For simplicity my schema:

  1. Folders table (FolderId, ParentFolderId)

  2. DeviceFolderProperties table (FolderId, LogDataRetentionDaysEvent)

Not every folder has a retention day. However this is an inherited value. How can you write something in SQL to return every folder and its retention day and if that is null its inherited value.

There are multiple levels to inheritance, so it will need to walk the tree.

This is what I have tried:

;
WITH [cte]
AS 
(
    SELECT f.FolderId, f.ParentFolderId, dfp.LogDataRetentionDaysEvent
    FROM [Folders] f
    LEFT JOIN DeviceFolderProperties dfp
    ON f.FolderId = dfp.FolderId
), 
[cte_collapse] 
AS --recurse where r days is null
(

    SELECT c.FolderId, c.ParentFolderId, c.LogDataRetentionDaysEvent
    FROM [cte] c
    WHERE c.LogDataRetentionDaysEvent IS NULL

    UNION ALL

    SELECT c.FolderId, c.ParentFolderId, ISNULL(c.LogDataRetentionDaysEvent, cc.LogDataRetentionDaysEvent)
    FROM [cte] c
    JOIN [cte_collapse] cc ON cc.FolderId = c.ParentFolderId

)
SELECT
    *
FROM 
    [cte_collapse]

Solution

  • You could write this as:

    with 
        data as (
            select f.FolderID, f.ParentFolderId, dfp.LogDataRetentionDaysEvent
            from Folders f
            left join DeviceFolderProperties dfp on dfp.FolderID = f.FolderID
        ),
        cte as (
            select d.*, FolderID OriginalFolderId
            from data d
            union all
            select d.*, c.OriginalFolderId
            from cte c
            inner join data d on d.FolderID = c.ParentFolderId
            where c.LogDataRetentionDaysEvent is null
        )
    select OriginalFolderId, max(LogDataRetentionDaysEvent) LogDataRetentionDaysEvent
    from cte 
    group by OriginalFolderId
    

    We start by generating a derived table that contains information from both tables. Then, for each record, the recursive query climbs up the hierarchy, searching for a non-null the retention at each level. The trick is to stop as soon as a match is met.