You can use a ranking function (rank, dense_rank, row_number) to assign a nominal sequential number to each contact number for each customer:
select cus.id as customer_id,
cus.customer_name,
con.contact_no,
row_number() over (partition by cus.id order by con.id) as rn
from customers cus
left join contacts con on con.customer_id = cus.id
and then either use that in a pivot:
select *
from (
select cus.id as customer_id,
cus.customer_name,
con.contact_no,
row_number() over (partition by cus.id order by con.id) as rn
from customers cus
left join contacts con on con.customer_id = cus.id
)
pivot (max(contact_no) for (rn) in (1 as contact_no_1, 2 as contact_no_2,
3 as contact_no_3, 4 as contact_no_4, 5 as contact_no_5))
or with a manual pivot using aggregation:
select customer_id,
customer_name,
max(case when rn = 1 then contact_no end) as contact_no_1,
max(case when rn = 2 then contact_no end) as contact_no_2,
max(case when rn = 3 then contact_no end) as contact_no_3,
max(case when rn = 4 then contact_no end) as contact_no_4,
max(case when rn = 5 then contact_no end) as contact_no_5
from (
select cus.id as customer_id,
cus.customer_name,
con.contact_no,
row_number() over (partition by cus.id order by con.id) as rn
from customers cus
left join contacts con on con.customer_id = cus.id
)
group by customer_id, customer_name
db<>fiddle with some made-up data.
I've shown handling up to 5 contact numbers; you need to add as many in()
values or case
expressions as you need to handle whatever value of N you're comfortable limiting the output to. (If you decide you can't set a limit then you'll have to use a dynamic pivot, which is more complicated.)
I've included customer_id
in case you can have two customer that happen to have the same name. You don't have to include that in the final projection; in the pivot version that means you will need to list all the columns you do want to include.