SELECT DISTINCT c.customer_id
INTO dbo.table1 will create table1, which is permanent table, so if another procedure will run or just second session the same stored procedure it will fail because table1 already created by first procedure. use #table1 # tables is runtime temporary tables attached to current session and exist only while session exist, so if you have to sessions of management studio open each can has own #table1
2. All your try catch cases. You do not need them in this case, next run will create new tables.
3. cursor you using 2 times it will slow down your stored procedure significantly you should avid using cursors. I'mnot sure what you are trying to achive, but yourcursor in step 4 may be replaced by simple update like
UPDATE t3
set alt_key_code =ak.alt_key_code
from dbo.table3 t3
join table2 ak
on t3.alt_key = t3.alt_key
4. all your PRINT 'Number of rows Updated in dbo.table3 with alt_key_code: ' + CAST(@v_count as nvarchar(max));
will not produce real output when you run procedure it is just debug messages...

This procedure will be run only once manually by me. So there is no chance of another instance running at the same time. We are moving from Oracle to sql server currently and I am trying to make life easier by writing a procedure instead of executing these steps one by one. I cannot use temporary Tables as I might have to refer this data after.

I can remove all the try-catch blocks but the issue still remains.

Using cursors in Oracle was always a better solution as I have the choice to commit (say every 10000 rows) and clear the redo log. I can replace that query with a direct update if that is more efficient here.

But again, the 2nd update does not update even within the cursor. But if I run it separately (using the same try-catch block) it works!

And is there any other alternative to Print statement? Currently it serves my purpose. But I would like to use something like dbms_output.put_line function.

SELECT DISTINCT c.customer_id
INTO dbo.table1 will create table1, which is permanent table, so if another procedure will run or just second session the same stored procedure it will fail because table1 already created by first procedure. use #table1 # tables is runtime temporary tables attached to current session and exist only while session exist, so if you have to sessions of management studio open each can has own #table1
2. All your try catch cases. You do not need them in this case, next run will create new tables.
3. cursor you using 2 times it will slow down your stored procedure significantly you should avid using cursors. I'mnot sure what you are trying to achive, but yourcursor in step 4 may be replaced by simple update like
UPDATE t3
set alt_key_code =ak.alt_key_code
from dbo.table3 t3
join table2 ak
on t3.alt_key = t3.alt_key
4. all your PRINT 'Number of rows Updated in dbo.table3 with alt_key_code: ' + CAST(@v_count as nvarchar(max));
will not produce real output when you run procedure it is just debug messages...

To commit (say every 10000 rows) for smaller log better use while loop.
your try catch does not name sense becouse if first fail script goes to second, but table1 not exist and so on, you can have one try catch for entire script it is ok.