Speed up complex join with disjunctive condition
The below creates 300000 dummy orders with 3 random "phone" numbers and then finds the matches, should get you started.

use tempdb
GO

drop table if EXISTS Orders 
GO


create table Orders (
    OrderID int primary key,
    UserName varchar(50),
    PhoneNumber1 varchar(50),
    PhoneNumber2 varchar(50),
    PhoneNumber3 varchar(50)
)

-- generate 300000 with randon "phone" numbers

;WITH TallyTable AS (
SELECT TOP 300000 ROW_NUMBER() OVER(ORDER BY (SELECT NULL)) AS [N]
  FROM dbo.syscolumns tb1,dbo.syscolumns tb2 
)
insert into Orders
select n, 'user' + cast(n as varchar(10)), cast(CRYPT_GEN_RANDOM(3) as int), cast(CRYPT_GEN_RANDOM(3) as int), cast(CRYPT_GEN_RANDOM(3) as int)
FROM TallyTable;

/*

-- original query, takes a long time

select o1.OrderID, 
       o1.PhoneNumber1, 
       o1.PhoneNumber2, 
       o1.PhoneNumber3, 
       count(distinct o2.UserName)
from Orders o1
inner join Orders o2
    on o1.PhoneNumber1 = o2.PhoneNumber1
    or o1.PhoneNumber2 = o2.PhoneNumber2
    or o1.PhoneNumber3 = o2.PhoneNumber3
group by o1.OrderID, o1.PhoneNumber1, o1.PhoneNumber2, o1.PhoneNumber3
*/

-- which users share the same phonenumber1
select o1.UserName as username, 'phonenumber1' as phonenumber, o2.UserName as sharedwith
from Orders o1
inner join Orders o2
    on o1.PhoneNumber1 = o2.PhoneNumber1
    and o1.UserName &lt;&gt; o2.username -- make sure it's a different user
union ALL
-- which orders share the same phonenumber2
select o1.UserName, 'phonenumber2',  o2.UserName
from Orders o1
inner join Orders o2
    on o1.PhoneNumber2 = o2.PhoneNumber2
    and o1.UserName &lt;&gt; o2.username -- make sure it's a different user
union ALL
-- which orders share the same phonenumber3
select o1.UserName, 'phonenumber3',  o2.UserName
from Orders o1
inner join Orders o2
    on o1.PhoneNumber3 = o2.PhoneNumber3
    and o1.UserName &lt;&gt; o2.username -- make sure it's a different user
order by username asc


-- use the above as a cte to do some counting
; with matches as (
    select o1.UserName as username, 'phonenumber1' as phonenumber, o2.UserName as sharedwith
    from Orders o1
    inner join Orders o2
        on o1.PhoneNumber1 = o2.PhoneNumber1
        and o1.UserName &lt;&gt; o2.username -- make sure it's a different user
    union ALL
    -- which orders share the same phonenumber2
    select o1.UserName, 'phonenumber2',  o2.UserName
    from Orders o1
    inner join Orders o2
        on o1.PhoneNumber2 = o2.PhoneNumber2
        and o1.UserName &lt;&gt; o2.username -- make sure it's a different user
    union ALL
    -- which orders share the same phonenumber3
    select o1.UserName, 'phonenumber3',  o2.UserName
    from Orders o1
    inner join Orders o2
        on o1.PhoneNumber3 = o2.PhoneNumber3
        and o1.UserName &lt;&gt; o2.username -- make sure it's a different user
)
SELECT matches.username, COUNT(*) AS matches
from matches
group by username
order by 2 desc