'SQL referrals view

I have a table with users:

id              referred_by_user_id
-----------------------------------
1               null
2               1
3               1
4               2  

I need to write request to get number of people referred by user in two levels.

  • First - direct referral (example: user 1 referred users 2 3. count for level 1 = 2)
  • Second - user 1 referred to users 2 and 3, user 2 referred to user 4. So count for level 2 should be 1

Result of query should be:

id              referred_user_tier_one_total      referred_user_tier_two_total
------------------------------------------------------------------------------
1               2                                 1 
2               1                                 null
3               null                              null 
4               null                              null

I figured out how to count referred_user_tier_one_total:

select 
    "id", referred_user_tier_one_total
from 
    "user"
inner join 
    (select 
         count(*) as referred_user_tier_one_total, referred_by_user_id
     from 
         "user"
     where 
         "user".referred_by_user_id is not null
     group by 
         "user".referred_by_user_id) ur on "user".id = ur.referred_by_user_id

But I don't understand how to calculate referred_user_tier_two_total. Please, help

UPD:

Thanks @Stoff for SQL Server solution.

Here is the script rewritten for Postgres

WITH RECURSIVE agg AS 
(
    SELECT
        a.ID, a.referred_by_user_id, 
        COUNT(b.referred_by_user_id) AS "count"
    FROM
        "user" a
    LEFT JOIN
        "user" b ON a.ID = b.referred_by_user_id
    GROUP BY 
        a.ID, a.referred_by_user_id
)
SELECT 
    a.ID, 
    a.Count AS referred_user_tier_one_total, 
    CASE 
        WHEN SUM(b.count) IS NULL 
            THEN 0 
            ELSE SUM(b.count) 
    END AS referred_user_tier_two_total 
FROM
    agg a
LEFT JOIN
    agg b ON a.ID = b.referred_by_user_id
GROUP BY
    a.ID, a.Count
ORDER BY 
    a.ID


Solution 1:[1]

Here is a solution which works in Postgres.
We could carry on writing more levels in the same way.

create table referals(
id int,
referred_by_user_id int);
insert into referals values
(1 ,         null),
(2 ,            1),
(3 ,            1),
(4 ,            2 );
select
t0.id,
count(t1.id) tier1,
count(t2.id) tier2
from
referals t0
left join referals t1
on t0.id = t1.referred_by_user_id
left join referals t2
on t1.id = t2.referred_by_user_id
group by t0.id
id | tier1 | tier2
-: | ----: | ----:
 1 |     2 |     1
 2 |     1 |     0
 3 |     0 |     0
 4 |     0 |     0

db<>fiddle here

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1