Procedure to insert data from one column into two columns in another table - sql

I'm trying to get a procedure that will allow me to get data from a column and insert it into two different columns in a different table. the first table currently has both first and last name in a single column. I have another table with first name and last name in different columns and I need to separate and insert them from Column1/Table1 into the two columns in Table2 preferably using a procedure since I have a lot of names to migrate.
Column1(Name) in Table 1 looks like this
NAME
First_Name1 Last_name1
First_Name2 Last_Name2
First_Name3 Last_Name3
And I need the data to be separated like this in Table2 as FName/LName using the data from the first table:
F_Name | L_Name
First_Name1|Last_Name1
First_Name2|Last_Name2
First_Name3|Last_Name3
I figured out how to get the data from the last and the first name separated using SUBSTR and INSTR, but I can't figure out how to put this inside a Procedure, or how to Loop it since I want to use it for several rows.
select substr(staff.name, 0, instr(staff.name, ' ')-1) as Fname
from staff;
select substr(staff.name, instr(staff.name,' ')+1) as Lname
from Staff;
Any ideas/Help? Thanks guys.

Building a Looping PL/SQL Based DML Cursor For Multiple DML Targets
A PL/SQL Stored Procedure is a great way to accomplish your task. An alternate approach to breaking down your single name field into FIRST NAME and LAST NAME components could be to use an Oracle Regular Expression, as in:
SELECT REGEXP_SUBSTR('MYFIRST MYLAST','[^ ]+', 1, 1) from dual
-- Result: MYFIRST
SELECT REGEXP_SUBSTR('MYFIRST MYLAST','[^ ]+', 1, 2) from dual
-- Result: MYLAST
A procedure based approach is a good idea; first wrap this query into a cursor definition. Integrate the cursor within a complete PL/SQL stored procedure DDL script.
CREATE or REPLACE PROCEDURE PROC_MYNAME_IMPORT IS
-- Queries parsed name values from STAFF (the source) table
CURSOR name_cursor IS
SELECT REGEXP_SUBSTR(staff.name,...) as FirstName,
REGEXP_SUBSTR(... ) as LastName
FROM STAFF;
BEGIN
FOR i IN name_cursor LOOP
--DML Command 1:
INSERT INTO Table_One ( first_name, last_name )
VALUES (i.FirstName, i.LastName);
COMMIT;
--DML Command 2:
INSERT INTO Table_Two ...
COMMIT;
END LOOP;
END proc_myname_import;
As you can see from the example block, a long series of DML statements can take place (not just two) for a given cursor record and its values as it is handled by each loop iteration. Each field may be referenced by the name assigned to them within the cursor SQL statement. There is a '.' (dot) notation where the handle assigned to the cursor call is the prefix, as in:
CURSOR c1 IS
SELECT st.col1, st.col2, st.col3
FROM sample_table st
WHERE ...
Then the cursor call for looping through the main record set:
FOR my_personal_loop IN c1 LOOP
...do this
...do that
INSERT INTO some_other_table (column_one, column_two, column_three)
VALUES (my_personal_loop.col1, my_personal_loop.col2, ...);
COMMIT;
END LOOP;
... and so on.

This should work for you.
insert into newtable(FirstName, LastName)
select substr(staff.name, 0, instr(staff.name, ' ') - 1),
substr(staff.name, instr(staff.name, ' ') + 1)
from staff;

Related

Oracle Procedure to insert all records from one staging table into main table

I wrote Stored Procedure (SP) where inside SP, 2 SP separated for 2 insertion from table. Both table contains more than 25 columns in each temp & main table. Below is query-
create or replace procedure sp_main as
procedure tbl1_ld as
cursor c1 is select * from tmp1;
type t_rec1 is table of c1%rowtype;
v_rec1 t_rec1;
begin
open c1;
loop
fetch c1 bulk collect into v_rec1 limit 1000;
exit when v_rec1.count=0;
insert into tbl1 values v_rec1;
end loop;
end tbl1_ld;
procedure tbl2_ld as
cursor c2 is select * from tmp2;
type t_rec2 is table of c2%rowtype;
v_rec2 t_rec2;
begin
open c2;
loop
fetch c2 bulk collect into v_rec2 limit 1000;
exit when v_rec2.count=0;
insert into tbl2 values v_rec2;
end loop;
end tbl2_ld;
begin
null;
end sp_main;
/
I used EXECUTE IMMEDIATE 'insert into tbl1 select * from tmp1'; for insertion inside both SP tbl1_ld & tbl2_ld instead of using cursor, SP compiled but no record has been inserted.
Well, you didn't actually run any of these procedures. The last few lines of your code should be
<snip>
end tbl2_ld;
begin
tbl1_ld; --> this
tbl2_ld --> this
end sp_main;
/
On the other hand, I prefer avoiding insert into ... select * from because it just loves to fail when you modify tables' description and don't fix code that uses those tables.
Yes, I know - it is just boring to name all 25 columns, but - in my opinion - it's worth it. Therefore, I'd just
begin
insert into tbl1 (id, name, address, phone, ... all 25 columns)
select id, name, address, phone, ... all 25 columns
from tmp1;
insert into tbl2 (id, name, address, phone, ... all 25 columns)
select id, name, address, phone, ... all 25 columns
from tmp2;
end;
In other words, no cursors, types, loops, ... nothing. Could have been pure SQL (i.e. no PL/SQL). If you want to restrict number of rows inserted, use e.g. ... where rownum <= 1000 (if that's why you used the limit clause).
As of dynamic SQL you mentioned (execute immediate): why would you use it? There's nothing dynamic in code you wrote.

SQL Oracle get the all columns of a cursor to use in a insert into

I want to create a procedure in which I use a cursor to select certain lines and then insert them in another table. I wonder if there is a notation to write it faster.
For instance here is the complete procedure
create or replace procedure myProc as
Cursor lines is
select * from table1 where c = '2';
begin
for line in lines loop
insert into table2 values(line.a, line.b, line.c, line.d ....);
end loop;
end;
/
I want to know if I can replace the 'insert into' line by something like
insert into table2 values(line.something);
or
insert into tables2 values(something(line));
(I think a view could be more effective but it's not the question here.)
Absolutely:
create or replace procedure myProc as
begin
insert into table2( . . .)
select a, b, c, d, . .
from table1
where c = '2';
end;
/
You should list the columns in table2 as well. That is what the table2( . . . ) means.
Despite the following code should work in case structure of both tables were identical...
INSERT INTO target_table
SELECT * FROM source_table;
... you should avoid this way of programming because any column addition to source or target table will end in SQL became invalid.
Looks like you're looking for a short-cut in order to avoid qualifying column names in an insert statement.
Well, although I do not recommend this, it can be done bug it's a little tedious. Here is an example:
if you have a table employee (employee_id, employee_name, designation). You'll need to create two types in the database-
a. An object type which is similar to employee-
create type employee_obj as object (employee_id number(28,0),
employee_name varchar2(100),
designation varchar2(30));
b. Create the set type of this record-
create type employee_obj_set is table of employee_obj;
c. In your PL/SQL procedure you can use something like:
DECLARE
empset employee_obj_set;
-- More variables here
BEGIN
-- other operations
-- populate your empset
-- other operations
INSERT INTO employee
SELECT * FROM table(empset);
-- other operations
END;
/
For faster inserts use bulk collect/FORALL insert instead of singular inserts.Find below sample...
DECLARE
CURSOR lines is select * from table1 where c = '2';
type lines_ty is table of table2%rowtype;
l_lines_t2 lines_ty;
BEGIN
OPEN lines;
LOOP
FETCH lines BULK COLLECT INTO l_lines_t2 LIMIT 500;
FORALL i IN 1..l_lines_t2.COUNT
INSERT INTO table2 VALUES l_lines_t2(i);
EXIT WHEN lines%NOTFOUND;
END LOOP;
CLOSE lines;
END;

how to get table name from column value in oracle sql?

I have a main table that has two columns with table names and id's. And I have those tables with table names in my DB.
For example, I find particular table name, selecting id. And then I want to populate table with that name with data. And I want to do that in one query. How I can do that?
The goal: to populate with data all tables at once, that has the names that similar with values in table name column from main table.
That is how I'm getting the list of tables. I should probably loop through it.
select tbl from asp_tbl where asp in (
select id from (
SELECT * FROM DIMENSION WHERE EXTERNALKEY LIKE 'W16%')
);
And then I will try to merge the data from other tables inside the table that needs to be populated:
MERGE INTO tbl d
USING
(SELECT ? nums, ? names from data_table) s
ON(d.product = s.product and d.ga = s.ga and d.metric_id = s.metric_id)
WHEN MATCHED THEN UPDATE SET d.names = s.names
WHEN NOT MATCHED THEN INSERT (nums, names)values(s.nums,s.names);
Did I provide enough info?
As I understand you need some stored procedure witch may fulfil a table with some test data. If so you may write something like:
create procedure fulfil_test_data (p_table_name varchar2) is
begin
for x IN (select tbl from asp_tbl where asp in (
SELECT table_id FROM DIMENSION WHERE EXTERNALKEY LIKE p_table_name )) loop
execute immediate 'insert into '|| x.tbl ||' (nums, names)
select level , chr(ascci(''A'') + mod(level,26)) from dual connect by level < 1001';
end loop;
end;
/
And call it
begin
fulfil_test_data('W16%');
end;
/

Oracle SQL insert query - into parent and child tables

for an assignment I had something similar to the following (simplified for brevity):
STUDENT(StudentID, Fname. Lname) StudentID PK
UNIT(UnitID, UnitName) UnitID PK
STUDENT_UNIT((StudentID, UnitID) StudentID PK/FK UnitID PK/FK
Needed to insert info about a student and the units that he/she had completed.
As it is only beginner level SQL the following was accepted
INSERT INTO STUDENT
VALUES(seqStudID.NextVal, 'Bob', 'Brown');
INSERT INTO STUDENT_UNIT(seqStudID.CurrVal, 111);
INSERT INTO STUDENT_UNIT(seqStudID.CurrVal, 222);
INSERT INTO STUDENT_UNIT(seqStudID.CurrVal, 333);
But I was wondering what would be the real way to enter this data, would it be a procedure with a loop? If so what sort of loop (so that it could handle any amount of units).
Thanks in advance
One of the best approach to do this is by using stored procedure. The below procedure will do everything for you.
CREATE OR REPLACE
PROCEDURE set_stud_unit(
i_fname IN VARCHAR2,
i_lname IN VARCHAR2,
i_unitid IN VARCHAR2)
IS
l_studentid student.studentid%TYPE;
BEGIN
INSERT INTO student(studentid, fname, lname)
VALUES(seqstudid.NEXTVAL, i_fname, i_lname)
RETURNING studentid INTO l_studentid;
INSERT INTO student_unit (studentid, unitid)
(SELECT l_studentid, (COLUMN_VALUE).getNumberVal() vid FROM xmltable(i_unitid));
COMMIT;
END;
/
You can pass the unitid as comma separated as below,
EXECUTE set_stud_unit('Bob', 'Brown', '111,222,333');
you can use select in your insert:
INSERT INTO STUDENT_UNIT select t1.StudentID ,t2.UnitID from STUDENT t1 ,UNIT t2;
and you can use where to limit this selection ;-)
Probably what you need is a procedure that accept:
First Name
Last Name
an array of Units
The procedure will:
Insert into STUDENT (I suggest you to use a trigger to populate StudentID, maybe you are already doing it),
Loop over the array and insert into STUDENT_UNIT each of the element of the array and the StudentID for First Name and Last Name, but without using the sequence. My pseudo code:
FOR i IN 1..input_array.count LOOP
INSERT INTO STUDENT_UNIT
SELECT StudentID, input_array(i)
FROM STUDENT
WHERE Fname = FirstNameParam
AND Lname = LastNameParam;
END LOOP;
I suggest you to query the student table to get the actual ID to avoid problems in case of concurrency. An optimization would be to query the STUDENT table only once and save the StudentID in a variable.
You can find more info about passing an array to an Oracle procedure here:
Passing an array of data as an input parameter to an Oracle procedure

How to modify data held by the cursor only, without the database updating?

I have a cursor as below:
CURSOR employee_cur IS
SELECT * FROM employee where department_id='accounts';
I need to modify the data for some specific rows inside the cursor only. Later on whoever will use this cursor, they should find the modified data only, from this cursor.
I don't want to update any DB table. Can anybody help me on this?
You can't.
A cursor is a read-only structure. It is a pointer to a SQL statement. When that SQL statement is executed, the data will be fetched from the database. You can manipulate the data in the SELECT statement itself as much as you would like. But you can't modify the data that the cursor will return once the cursor is opened.
You can, of course, manipulate the data in the SELECT statement. For example, your query can do something like
SELECT employee_id,
first_name,
last_name,
(CASE WHEN last_name = 'King'
THEN salary*2
ELSE salary
END) salary
FROM employee
to double any employee named "King"'s salary in the result set. You can use a UNION ALL to return rows that don't exist in the database, i.e.
SELECT employee_id,
first_name,
last_name,
salary
FROM employee
UNION ALL
SELECT -1,
'Justin',
'Cave',
17
FROM dual
The simplest way to get you're trying to achieve is to return either varray or nested table (pipelined or by out param). When you're populate it you could to do all changes you need. If you really need to return a cursor and do not need something like UPDATE CURRENT OF you could put data in varray/nested table, past as the IN param into pipilined function and create cursor as select * from your pipilened function(prepared vararray/nested table).
You could do all treatment inside your function, put results at any temporary table and return the cursor from the temporary table.
Declare a collection-
type my_emp_table_type is table of employee%rowtype;
myEmpTable my_emp_table_type;
And then use BULK COLLECT to collect the cursor data, by
SELECT * BULK COLLECT INTO myEmpTable
FROM employee
WHERE department_id='accounts';
Modify collection elements like-
myEmpTable(10).id := 10;
myEmpTable(10).first_name := 'John';
myEmpTable(10).last_name := 'Doe';
And use myEmpTable wherever you want.
Note, you can loop through the collection as
for i in myEmpTable.first .. myEmpTable.last loop
if myEmpTable.(i)last_name = 'Doe'
then
myEmpTable.(i)sal := 50000;
end if;
...
....
end loop;