i need make an INSERT INTO tablex (SELECT columns)VALUES(v1...vn) - oracle

I need to make an "insert dynamically" random data in table WORKER, now I have the fieldname, the datatype and the size of every column of this table WORKER in other table TEMPORAL (this info was captured before and saved on this table).
The question is: when I make the insert, I need to know the fields from this table to make the insert. How do I do it? How do I take the values from TEMPORAL to make the insert? Until now I got this but doesn't work:
insert into worker(select chr(500)||upper(name)||',' from temporal)
I thought about this too:
select chr(500)||upper(name)||',' from temporal
or make a dynamic statement using this values and a cursor.
thx to everibody.
TEMPORAL contains the name, the datatype and the length of the table TRABAJADOR, captured for do the insertion of random data. but i most to do this dynamically for that reason i don't know what or how much columns this table had(i have to do this for every or any table on any shema). this is what temporal contains:
NAME Typ Length
ID_MAN NUMBER 22
SALARIO NUMBER 22
GENERO VARCHAR2 8
FDN DATE 7
NOMBRE VARCHAR2 100
DIRECCION VARCHAR2 60
DEPT VARCHAR2 60
PAIS VARCHAR2 60
CATEGORIA VARCHAR2 60
i got right now in one variable(COL) the names of the columns for pass into the insert statement
insert into worker(col). but i dont know how to do this or if even is posible. THX

You can achieve that with dynamic SQL. This should work:
declare
myQuery varchar2(4000);
begin
-- Open insert and aggregate all values
select 'insert into worker('
|| listagg(t.name, ', ') within group (order by t.name)
into myQuery
from TEMPORAL t;
-- Close insert
myQuery := myQuery || ') values(<here you have to specify your values>)';
execute immediate myQuery;
end;
/

Related

Insert into not working on plsql in oracle

declare
vquery long;
cursor c1 is
select * from temp_name;
begin
for i in c1
loop
vquery :='INSERT INTO ot.temp_new(id)
select '''||i.id||''' from ot.customers';
dbms_output.put_line(i.id);
end loop;
end;
/
Output of select * from temp_name is :
ID
--------------------------------------------------------------------------------
customer_id
1 row selected.
I have customers table which has customer_id column.I want to insert all the customer_id into temp_new table but it is not being inserted. The PLSQL block executes successfully but the temp_new table is empty.
The output of dbms_output.put_line(i.id); is
customer_id
What is wrong there?
The main problem is that you generate a dynamic statement that you never execute; at some point you need to do:
execute immediate vquery;
But there are other problems. If you output the generated vquery string you'll see it contains:
INSERT INTO ot.temp_new(id)
select 'customer_id' from ot.customers
which means that for every row in customers you'll get one row in temp_new with ID set to the same fixed literal 'customer_id'. It's unlikely that's what you want; if customer_id is a column name from customers then it shouldn't be in single quotes.
As #mathguy suggested, long is not a sensible data type to use; you could use a CLOB but only really need a varchar2 here. So something more like this, where I've also switched to use an implicit cursor:
declare
l_stmt varchar2(4000);
begin
for i in (select id from temp_name)
loop
l_stmt := 'INSERT INTO temp_new(id) select '||i.id||' from customers';
dbms_output.put_line(i.id);
dbms_output.put_line(l_stmt);
execute immediate l_stmt;
end loop;
end;
/
db<>fiddle
The loop doesn't really make sense though; if your temp_name table had multiple rows with different column names, you'd try to insert the corresponding values from those columns in the customers table into multiple rows in temp_new, all in the same id column, as shown in this db<>fiddle.
I guess this is the starting point for something more complicated, but still seems a little odd.

Is there an easy to to iterate over all :NEW values from an Oracle database trigger execution?

I am attempting to write a generic trigger that will provide all of the :NEW values for the row inserted. Ultimately I want to turn them into XML and insert the XML string into a binary field on another table.
There are a variable number of columns in each table - many times over 100 fields and over 100 tables in all, so individual mapping to XML per table is extremely time consuming.
Is there a way to reference the :NEW pseudorecord as a collection of column values - or perhaps a way to pass the whole :NEW record to a Stored Procedure that could pass it to a Java function (hosted on the database) that might make the individual values iterable?
I've found an example here:
https://docs.oracle.com/database/121/LNPLS/triggers.htm
Create history table and trigger:
CREATE TABLE tbl_history ( d DATE, old_obj t, new_obj t)
/
CREATE OR REPLACE TRIGGER Tbl_Trg
AFTER UPDATE ON tbl
FOR EACH ROW
BEGIN
INSERT INTO tbl_history (d, old_obj, new_obj)
VALUES (SYSDATE, :OLD.OBJECT_VALUE, :NEW.OBJECT_VALUE);
END Tbl_Trg;
/
This seems to imply there is some sort of way it is storing all of the values as a variable, but this appears to put them directly back into a database table. I want to get the 'text' values of the column values listed.
You can create a stored procedure to create your trigger
for table tbl like
create table tbl (id number, value varchar2(10));
and an history table like
create table tbl_history (d date,id number, value varchar2(10));
you can create your trigger like this
create or replace procedure CREATE_TRIGGER IS
trig_str VARCHAR2(32767);
col_str VARCHAR2(32767) := '(d';
values_str VARCHAR2(32767) := '(sysdate';
begin
trig_str := 'CREATE OR REPLACE TRIGGER Tbl_Trg AFTER UPDATE ON tbl FOR EACH ROW'||chr(10)||
'BEGIN'||chr(10)||chr(9)||'INSERT INTO tbl_history ';
for col in (
SELECT column_name FROM all_tab_columns where table_name = 'TBL'
) loop
col_str := col_str||','||col.column_name;
values_str := values_str||','||':OLD.'||col.column_name;
end loop;
col_str := substr(col_str,1,length(col_str)-1)||')';
values_str := substr(values_str,1,length(values_str)-1)||')';
trig_str := trig_str||col_str||' VALUES '||values_str||';'||chr(10)||'END;';
execute immediate trig_str;
END;
/
With an history table with old and new values it's a bit more complicated but same idea

PL/SQL Extract Column Names and use in select statment

not sure if this is possible at all but im trying to do this with as little manual work as possible.
I have a table with 150 columns based on different combinations of factors.
I wish to extract the column names where a certain certain string is inside the column name.
I have done the following which does this. This is a basic example of what I have
--Create the table
Create Table temp
(id number,
Fac1_Fac2_Fac_3_Fac4_Fac5 number,
Fac1_Fac6_Fac_3_Fac4_Fac5 number,
Fac1_Fac6_Fac_7_Fac4_Fac5 number,
Fac1_Fac9_Fac_3_Fac4_Fac5 number,
Fac1_Fac10_Fac_3_Fac4_Fac5 number,
Fac1_Fac2_Fac_3_Fac11_Fac5 number,
Fac1_Fac2_Fac_3_Fac4_Fac12 number,
Fac13_Fac2_Fac_3_Fac4_Fac5 number);
Insert into temp Values (1,35634,3243,343,564,56,4635,3,334);
Insert into temp Values (2,3434234,3243,343,564,56,435,3,34234);
Insert into temp Values (3,5555,3243,33,564,56,435,3,3434);
Insert into temp Values (4,34234,343,343,564,56,4335,3,34);
commit;
--Extract Column Names
Select * from (
Select COLUMN_NAME
from user_tab_cols
where lower(table_name) ='temp'
)
where column_name like '%FAC13%'
--This is what I want to automate.
Select id, FAC13_FAC2_FAC_3_FAC4_FAC5
From temp
--I want the column name to come fron the select statment above as there may be lots of names.
Basically, I want to select all the rows from my table that have Fac13 in the column name all in one query if possible.
Thanks
I do not think you can do that in one query. First, your extract column names query can be simplified to one query as a cursor, and then use a dynamic select statement as follows:
CREATE OR REPLACE proc_dyn_select IS
CURSOR c1 IS
SELECT column_name
FROM user_tab_cols
WHERE LOWER(table_name) ='temp' and column_name LIKE '%FAC13%';
cols c1%ROWTYPE;
sqlstmt VARCHAR2(2000);
BEGIN
OPEN c1;
LOOP
FETCH c1 into cols;
EXIT WHEN c1%NOTFOUND;
sqlstmt := sqlstmt ||cols.column_name||',';
END LOOP;
CLOSE c1;
sqlstmt := 'select '||substr(sqlstmt, 1, length(sqlstmt)-1)||' FROM temp';
EXECUTE IMMEDIATE sqlstmt;
EXCEPTION
WHEN OTHERS THEN
DBMS_OUTPUT.PUT_LINE('error '||sqlerrm);
END;
/
Explanation
First, the cursor will store the columns that meet your conditions (to be from the table temp and the column names have the sub string FAC13. Then in execution section (after BEGIN), you will build your query dynamically using columns names stored in the cursor c1. With each round of the loop, a column name is added as a string and concatenated with a comma. So a string of columns will be built like this 'col1, col2, col3, ... coln,'. The string is stored in sqlstmt variable.
After the loop end, you amend the string to build sql statement, by adding the keywords SELECT, FROM and table name. However, we remove the last character of the sqlstmt variable, as it is an extra comma.
EXECUTE IMMEDIATE statement, will run the query stored in sqlstmt.
By using a procedure, you can always pass parameters, such that this procedure can perform any dynamic sql statement you want.

How do I convert row into CLOB in the applied trigger after update?

The idea is, I want to clone the record as a CLOB when it is updated.
Why do it in such a way?
There are two different applications A1 and A2, A1 is depended on by A2.
Based on A1 values, calculations are made for values for A2.
The A2 process runs just once per day to calculate the values, but for A1 every field in the TABLE_NAME in question can be altered several times a day and doesn't have a history.
The aim is to create a history which is a CLOB field in a table "NEW_TABLE" of automatic form.
Sorry for my English, but if something is not understandable I can rewrite the question
My Code Here:
CREATE or REPLACE TRIGGER TRIGGER_NAME
AFTER UPDATE
ON TABLE_NAME
FOR EACH ROW
DECLARE
row_record NEW_TABLE%rowtype;
c_xml CLOB;
FUNCTION GetXML(a_tablela varchar2, a_key_1 varchar2, a_key_2 varchar2)
RETURN CLOB
is
x_xml CLOB;
BEGIN
select dbms_xmlgen.getxml('select * from '||a_tablela||' where key_1 = '''||a_key_1||''' and key_2 = '''||a_key_2||'''') into x_xml from dual;
return x_xml;
END;
BEGIN
--** TABLE_NAME Automatically fetches all columns and transforms them to CLOB
c_xml := GetXML('TABLE_NAME', :new.key_1, :new.key_2);
if c_xml is not null then
row_record.TABLE_NAME :=c_xml;
end if;
INSERT INTO NEW_TABLE VALUES row_record;
EXCEPTION
when others then
raise_application_error(-20000,'ERROR: '||to_char(sqlcode));
END;
Now I get error:
ORA-04091: table TABLE_NAME is mutating, trigger/function may not see it.
when I get this record across SELECT statement.
How do I convert row into CLOB in the applied TRIGGER AFTER UPDATE ?
Thanks.
The reason you can't use a select statement is because you're in the trigger, and the table is changing, or 'mutating', as the error says. The only way you can get the data from the row that's being updated here is using new and old:
old.column1
new.column1
Old being the value of the column before the update, new being the value after the update.
Example:
CREATE or REPLACE TRIGGER TRIGGER_NAME
AFTER UPDATE
ON TABLE_NAME
FOR EACH ROW
BEGIN
l_string := 'This is the old value for column 1: ' || old.column1 || '. This is the new value: ' || new.column1;
dbms_output.put_line(l_string);
END;
You won't be able to use dbms_xmlgen because it uses a select statement, which throws the mutating error exception.
I'm not sure I perfectly understand what you're trying to do, but you should be able to build the CLOB yourself just by concatenating yourself with the column names. Like this:
CREATE or REPLACE TRIGGER TRIGGER_NAME
AFTER UPDATE
ON TABLE_NAME
FOR EACH ROW
BEGIN
l_clob := 'Column1 ' || old.column1 || ', Column2 ' || old.column2; --For as many columns as are in the table
--Now you have a clob with all the old values, insert it where you want it
END;
And then go from there. If you really want the XML format you can do that yourself as well, just concatenate the strings together.

How to programmatically set table name in PL/SQL?

I created the following simple PL/SQL stored procedure example to ask a specific question. This procedure inserts an employee name and id number into a table called employees_???. The ??? is explained below.
PROCEDURE hire_employee (emp_id IN INTEGER, name IN VARCHAR2, country IN VARCHAR2)
AS
BEGIN
INSERT INTO employees_??? VALUES (emp_id, name, 1000);
END hire_employee;
What I need is to set the table name based on the IN variable country. For example,
If country = 'usa', I want the INSERT line to read:
INSERT INTO employees_usa VALUES (emp_id, name, 1000);
If country = 'germany', I want the INSERT line to read:
INSERT INTO employees_germany VALUES (emp_id, name, 1000);
If country = 'france', I want the INSERT line to read:
INSERT INTO employees_france VALUES (emp_id, name, 1000);
etc...
Is there a way to do this in PL/SQL by substituting something in place of employee_??? so only one line of code for INSERT is used? Or is using a case or if/then/else statement the best way?
To answer your question, you have to use execute immediate and create your statement dynamically.
create or replace procedure hire_employee (
emp_id IN INTEGER
, name IN VARCHAR2
, country IN VARCHAR2 ) is
-- maximum length of an object name in Oracle is 30
l_table_name varchar2(30) := 'employees_' || country;
begin
execute immediate 'insert into ' || l_table_name
|| ' values (:1, :2, 1000)'
using emp_id, name;
end hire_employee;
However, this is a massively over-complicated way of storing the data. If you want to select all data you have to union large numbers of tables.
It would be far better to normalise the database properly and add country to an employees table.
Something like the following:
create table employees (
emp_id number(16)
, country varchar2(3) -- ISO codes
, name varchar2(4000) -- maximum who knows what name people might have
, < other_columns >
, constraint pk_employees primary key ( emp_id )
);
Your procedure then becomes a very simple insert statement:
create or replace procedure hire_employee (
emp_id in integer
, name in varchar2
, country in varchar2 ) is
insert into employees
values ( emp_id, country, name, 1000 );
end hire_employee;
You can use dynamic SQL and the EXECUTE IMMEDIATE construct. In this, you construct the query as a string and then execute it. A good example is at http://docs.oracle.com/cd/B10500_01/appdev.920/a96590/adg09dyn.htm

Resources