For your exact scenario, I usually just implement some kind of PIVOT logic in my code. Link to MSDN documentation here - https://learn.microsoft.com/en-us/sql/t-sql/queries/from-using-pivot-and-unpivot?view=sql-server-ver16
To set up your tables as you have shown them, I've ran this code -
CREATE TABLE #phone_numbers
(
id INT
, phone_number VARCHAR(12)
);
CREATE TABLE #contacts
(
id INT IDENTITY(1, 1) PRIMARY KEY CLUSTERED
, phone_number_1 VARCHAR(12)
, phone_number_2 VARCHAR(12)
, name VARCHAR(10)
);
INSERT INTO #contacts
VALUES
('', '', 'Joe')
, ('', '', 'Bob')
, ('', '', 'Steve');
INSERT INTO #phone_numbers
VALUES
(1, '102-345-6789')
, (1, '102-445-6789')
, (2, '102-545-6789')
, (2, '102-645-6789')
, (3, '102-845-6789');
Simply running a SELECT on those tables give me this -
id phone_number
----------- ------------
1 102-345-6789
1 102-445-6789
2 102-545-6789
2 102-645-6789
3 102-845-6789
id phone_number_1 phone_number_2 name
----------- -------------- -------------- ----------
1 Joe
2 Bob
3 Steve
Since your phone_number table doesn't really contain an order of phone numbers, I've just had it assume that there will be a maximum of two phone numbers in the phone_number table, and if there are more, those will just be ignored.
Running my select with my pivot below -
SELECT c.id
, c.phone_number_1
, c.phone_number_2
, c.name
, pn_display.[1]
, pn_display.[2]
FROM #contacts AS c
CROSS APPLY
(
SELECT pn_pvt.[1]
, pn_pvt.[2]
FROM
(
SELECT pn.phone_number
, ROW_NUMBER() OVER (PARTITION BY pn.id ORDER BY pn.id) AS rn
FROM #phone_numbers AS pn
WHERE c.id = pn.id
) AS pn_order
PIVOT
(
MAX(phone_number)
FOR rn IN ([1], [2])
) pn_pvt
) AS pn_display;
Yields this table -
id phone_number_1 phone_number_2 name 1 2
----------- -------------- -------------- ---------- ------------ ------------
1 Joe 102-345-6789 102-445-6789
2 Bob 102-545-6789 102-645-6789
3 Steve 102-845-6789
According to your specific request -
And then my attempt at the update statement inside, where I want it to update the first column if null, and if not, the second column (I only care about the first two).
The update statement would look like this -
UPDATE c
SET c.phone_number_1 = IIF(ISNULL(c.phone_number_1, '') = '', [1], c.phone_number_1)
, c.phone_number_2 = IIF(ISNULL(c.phone_number_1, '') != '', [2], c.phone_number_2)
FROM #contacts AS c
CROSS APPLY
(
SELECT [1]
, [2]
FROM
(
SELECT pn.phone_number
, ROW_NUMBER() OVER (PARTITION BY pn.id ORDER BY pn.id) AS rn
FROM #phone_numbers AS pn
WHERE c.id = pn.id
) AS pn_order
PIVOT
(
MAX(phone_number)
FOR rn IN ([1], [2])
) pn_pvt
) AS pn_display
WHERE (
ISNULL(c.phone_number_1, '') = ''
AND ISNULL([1], '') != ''
)
OR
(
ISNULL(c.phone_number_2, '') = ''
AND ISNULL([2], '') != ''
);
This statement would select only records where 1 or both of the 2 phone_number columns in the contacts table were null or empty yet 1 or 2 records in the phone_numbers table were available for that contact. If the phone_number_1 field were empty, it would populate only that field. If it wasn't empty, but the phone_number_2 field was, it would fill in the second number if there were a second entry for that specific id in the phone_numbers table. That ensures that even if there are no records to update, rows are not updated with existing data. Depending on the size of the table that could be a nasty performance hit.
Design choices aside, that's how you would get the following over 2 executions (The first for phone number 1, the second for phone number 2). If you executed the update three times, the first would update 3 rows, the second would update 2, and the third would update 0.
SELECT c.id
, c.phone_number_1
, c.phone_number_2
, c.name
FROM #contacts AS c;
id phone_number_1 phone_number_2 name
----------- -------------- -------------- ----------
1 102-345-6789 102-445-6789 Joe
2 102-545-6789 102-645-6789 Bob
3 102-845-6789 Steve