Inserting into too many columns via SELECT query - sql

While this works as expected:
SQL> DROP TABLE TEST1;
Table dropped.
SQL> CREATE TABLE TEST1 (COL1 INTEGER, COL2 INTEGER);
Table created.
SQL> INSERT WHEN 1=1
2 THEN INTO TEST1 (COL1, COL2)
3 SELECT 1, 0 FROM DUAL;
1 row created.
SQL>
I receive a strange "ORA-00918: column ambiguously defined" error attempting this with more than two columns:
SQL> DROP TABLE TEST1;
Table dropped.
SQL> CREATE TABLE TEST1(COL1 INTEGER, COL2 INTEGER, COL3 INTEGER);
Table created.
SQL> INSERT WHEN 1=1
2 THEN INTO TEST1 (COL1, COL2, COL3)
3 SELECT 1, 0, 0 FROM DUAL;
THEN INTO TEST1 (COL1, COL2, COL3)
*
ERROR at line 2:
ORA-00918: column ambiguously defined
SQL>
Why am I receiving ORA-00918 errors here? Is there a limit on how many columns I can SELECT in an INSERT WHEN ... THEN INTO ... SELECT pattern?
Note: I'm using Oracle 11.2.0.1.0, and the actual query I'm attempting to execute in production is more complex and references other tables (and using "VALUES" would not suffice). This is just the simplified case...

Use aliases :
SQL> CREATE TABLE TEST1(COL1 INTEGER, COL2 INTEGER, COL3 INTEGER);
Table created.
SQL> INSERT WHEN 1=1
2 THEN INTO TEST1 (COL1, COL2, COL3)
3 SELECT 1 "1", 0 "2", 0 "3" FROM DUAL;
1 row created.
The reason you get this ambiguous column error is because when you don't provide an alias, Oracle will use a set of rules to name each column. In this case the second and third columns have the same name ("0") and thus can not be referenced unambiguously by the outer query:
SQL> SELECT 1, 0, 0 FROM DUAL;
1 0 0
---------- ---------- ----------
1 0 0
Oracle doesn't look at the data values when performing semantic analysis.

I don't have oracle with me, but it appears that it's the inline query (SELECT FROM DUAL) that's the issue. I recommend aliasing all the fields in that inline query and trying again.
INSERT WHEN 1=1
THEN INTO TEST1 (COL1, COL2, COL3)
SELECT 1 AS c1, 0 AS c2, 0 AS c3 FROM DUAL;

What happens you use use this?
INSERT WHEN 1=1
THEN INTO TEST1 (COL1, COL2, COL3)
SELECT 1 as col1, 0 as col2, 0 as col3 FROM DUAL;

Related

How to do a select insert statement in sql with a changing value in the column for each row

Basically what I am trying to do in sql is find a way to do a select insert statement where all of the values in the other columns will stay the same but one of the columns value will increase by 1 for every row that is created. I am wondering if there is a way to do that in SQL.
You can use:
INSERT INTO table_name (col1, col2, col3)
SELECT col1, col2, col3 + 1
FROM table_name
Which, for the sample data:
CREATE TABLE table_name (col1, col2, col3) AS
SELECT LEVEL, LEVEL, LEVEL FROM DUAL CONNECT BY LEVEL <= 3;
Then, after the INSERT, the table contains:
COL1
COL2
COL3
1
1
1
2
2
2
3
3
3
1
1
2
2
2
3
3
3
4
And the col1 and col2 values of the inserted rows are the same and the col3 values have been incremented by 1.
If you want to increment the values by the number of rows being inserted then you can use:
INSERT INTO table_name (col1, col2, col3)
SELECT col1, col2, col3 + COUNT(*) OVER ()
FROM table_name
However
If you are attempting to add multiple rows and keep col3 with unique values then you should not use that method and should use a sequence.
If you have the table:
CREATE TABLE table_name (col1, col2, col3) AS
SELECT LEVEL, LEVEL, table_name__col3__seq.NEXTVAL FROM DUAL CONNECT BY LEVEL <= 3;
Then you can insert the rows using:
INSERT INTO table_name (col1, col2, col3)
SELECT col1, col2, table_name__col3__seq.NEXTVAL
FROM table_name
and col3 will be populated using the next sequence values.
db<>fiddle here

Fetching a column value for current row in before insert Oracle trigger

I am creating a trigger in oracle which gets triggered before any insert statement. I want to insert a value in column2 using that trigger, but the problem is, I have to first get the the value of another column which is column 1 , of the new row being inserted and based on that value ,I will write business logic to insert the value in column2.
I am unable to access the value of column1 for the new row which will be inserted. How can I access the values of the new row being inserted.
Scenario is like this:
Value for column2 has to be inserted using trigger.
But, to popluate value in column2 , first value for column1 from the row which is going to be inserted, is required.
and Based on the value of column1, value for column2 will be calculated and inserted.
Please help me with syntax and proper resolution.
Use Case is:
Suppose column1 value is TS-1
then column2 value has to be TS-1-1, for the next time column2 value will be
TS-1-2 and so on. SO there could be multiple values for TS-1 in the table, and for that every time vales for column2 will increase as TS-1-,TS-1-2, TS-1-3 and so on, last digit gets incremented for column2.
So I will have the fetch the maximum value of last number getting incremented and increase it by 1 for each insert.
Please help
Once you create such a trigger as below :
SQL> create or replace trigger trg_tab_bi
before insert on tab
for each row
declare
nr int;
begin
select count(1)+1
into nr
from tab
where col1 = :new.col1;
:new.col2 := :new.col1||'-'||nr;
end;
you may manage your desired duty like in a below way:
SQL> insert into tab(col1) values('TS-1');
1 row inserted
SQL> select t.*
2 from tab t
3 order by col1, col2;
COL1 COL2
------ ------
TS-1 TS-1-1
SQL> insert into tab(col1) values('TS-1');
1 row inserted
SQL> select t.*
2 from tab t
3 order by col1, col2;
COL1 COL2
------ ------
TS-1 TS-1-1
TS-1 TS-1-2
SQL> insert into tab(col1) values('TS-1');
1 row inserted
SQL> select t.*
2 from tab t
3 order by col1, col2;
COL1 COL2
------ ------
TS-1 TS-1-1
TS-1 TS-1-2
TS-1 TS-1-3
SQL> insert into tab(col1) values('TS-7');
1 row inserted
SQL> select t.*
2 from tab t
3 order by col1, col2;
COL1 COL2
------ ------
TS-1 TS-1-1
TS-1 TS-1-2
TS-1 TS-1-3
TS-7 TS-7-1
SQL> insert into tab(col1) values('TS-7');
1 row inserted
SQL> select t.*
2 from tab t
3 order by col1, col2;
COL1 COL2
------ ------
TS-1 TS-1-1
TS-1 TS-1-2
TS-1 TS-1-3
TS-7 TS-7-1
TS-7 TS-7-2
You can use these two terms in a trigger :old to reference the old value and :new to reference the new value.
Here is an example from the Oracle documentation linked to above
CREATE OR REPLACE TRIGGER Print_salary_changes
BEFORE DELETE OR INSERT OR UPDATE ON Emp_tab
FOR EACH ROW WHEN (new.Empno > 0)
DECLARE
sal_diff number;
BEGIN
sal_diff := :new.sal - :old.sal;
dbms_output.put('Old salary: ' || :old.sal);
dbms_output.put(' New salary: ' || :new.sal);
dbms_output.put_line(' Difference ' || sal_diff);
END;
In this example the trigger fires BEFORE DELETE OR INSERT OR UPDATE :old.sal will contain the salary prior to the trigger firing and :new.sal will contain the new value.

Column alias on table declaration

Is there a way, in Oracle 11gR2 that I could create a single column with two names?
Reason I need this is back compatibility, on situations in which this would work:
create table test1 (col1 varchar2(10), col2 [some ref to col1]);
insert into test1 values ('test_value');
and then
SQL> select col1 from test1;
test_value
SQL> select col2 from test1;
test_value
There seem to be a way of thing so on SQL Server, I am looking for the Oracle equivalent to it.
ideas?
You can create a VIRTUAL COLUMN:
CREATE TABLE test1 (
col1 VARCHAR2(10),
col2 VARCHAR2(10) GENERATED ALWAYS AS (col1 || '')
);
INSERT INTO test1 (col1) VALUES ('test_value');
COMMIT;
SELECT * FROM test1;
COL1 COL2
---------- ----------
test_value test_value
However, virtual columns cannot be manipulated by DML. Read more here: Oracle Base - Virtual Columns

Using Merge statement inside a cursor

We have a requirement to populate a master table which consists of columns from a set of 20 different tables.
I have written a stored procedure to join some of the tables that return me max number of columns and have them in a cursor.
Now. I am using for loop to iterate through the cursor records so I can insert them into the master table.
How I can use a merge statement inside the cursor for loop so I can check if I need to update existing row or insert a new row depending if the records already exists or not.
Any ideas if we can use merge statement inside a cursor for loop? Any examples?
You can do a MERGE by selecting the cursor's data from DUAL. For example
Create a source and destination table with some data
SQL> create table src ( col1 number, col2 varchar2(10) );
Table created.
SQL> create table dest( col1 number, col2 varchar2(10) );
Table created.
SQL> insert into src values( 1, 'A' );
1 row created.
SQL> insert into src values( 2, 'B' );
1 row created.
SQL> insert into dest values( 1, 'C' );
1 row created.
SQL> commit;
Commit complete.
Run the merge
SQL> ed
Wrote file afiedt.buf
1 begin
2 for x in (select * from src)
3 loop
4 merge into dest
5 using( select x.col1 col1, x.col2 col2
6 from dual ) src
7 on( src.col1 = dest.col1 )
8 when matched then
9 update set col2 = src.col2
10 when not matched then
11 insert( col1, col2 )
12 values( src.col1, src.col2 );
13 end loop;
14* end;
SQL> /
PL/SQL procedure successfully completed.
And verify that the merge did what we wanted. Row 1 was updated and row 2 was inserted.
SQL> select * from dest;
COL1 COL2
---------- ----------
1 A
2 B
However, it generally wouldn't make too much sense to structure the code this way. You'd generally be better off putting the query that you'd use to open the cursor into the MERGE statement directly so that rather than selecting one row of data from DUAL, you're selecting all the data you want to merge from all the tables you're trying to merge the data from. Of course, it may make sense to create a view for this query that the MERGE statement can query in order to keep the MERGE statement readable.

Update with after insert trigger on same table

I have a to write a insert trigger on a tableA. which will perform update with same table but different column. I am getting error while doing this. My trigger is
create or replace trigger trigger_A
after insert on table_A
begin
update table_A set col1=1 where col1 is null;
end;
I have an application will perform col2 alone will be inserted and col1 will be kept null. so my trigger will give value for col1 once the row is inserted. But i am getting error saying "Trigger is failed and invalid" when a row is inserted.
How to do this. TIA.
If you want to assign a simple default value, the easiest way is to declare it on the table, using the DEFAULT clause.
SQL> create table t42
2 ( col1 number default 1 not null
3 , col2 date)
4 /
Table created.
SQL> insert into t42 (col2) values (sysdate)
2 /
1 row created.
SQL> select * from t42
2 /
COL1 COL2
---------- ---------
1 03-AUG-11
SQL>
This works with literals or pseudocolumns such as SYSDATE or USER. If you want to derive a more complicated value with a user-defined function or a sequence, you will need to use
a trigger.
Here is a new version of the table...
SQL> create table t42
2 ( col1 number default 1 not null
3 , col2 date default sysdate
4 , col3 varchar2(30) default user
5 , col4 number )
6 /
Table created.
SQL>
... with a trigger:
SQL> create or replace trigger t42_trg
2 before insert or update
3 on t42
4 for each row
5 begin
6 if :new.col4 is null
7 then
8 :new.col4 := my_seq.nextval;
9 end if;
10 end;
11 /
Trigger created.
SQL> insert into t42 (col1, col2, col3)
2 values (99, sysdate, 'MR KNOX')
3 /
1 row created.
SQL> select * from t42
2 /
COL1 COL2 COL3 COL4
---------- --------- ------------------------------ ----------
99 03-AUG-11 MR KNOX 161
SQL>
Note that although every column on the table is defaultable, I have to populate at least one column to make the SQL valid:
SQL> insert into t42 values ()
2 /
insert into t42 values ()
*
ERROR at line 1:
ORA-00936: missing expression
SQL>
But I can pass in NULL to COL4 to get a completely defaulted record:
SQL> insert into t42 (col4) values (null)
2 /
1 row created.
SQL> select * from t42
2 /
COL1 COL2 COL3 COL4
---------- --------- ------------------------------ ----------
99 03-AUG-11 MR KNOX 161
1 03-AUG-11 APC 162
SQL>
Caveat lector: my trigger uses the new 11g syntax. In previous versions we have to assign the sequence value using a SELECT statement:
select my_seq.nextval
into :new.col4
from dual;
You cannot update a table where the trigger is invoked:
Within a stored function or trigger, it is not permitted to modify a
table that is already being used (for reading or writing) by the
statement that invoked the function or trigger.
Doing so will generate Error 1442:
Error Code: 1442
Can't update table 'MyTable' in stored function/trigger because it is already used by statement which invoked this stored function/trigger.
In short, we are not allowed to update the table in use - but your case is simple, only want to update the field if it's NULL, for this choose BEFORE INSERT ON trigger, this way you can update all the fields of the new/current entry/row (as it has not been entered yet):
DELIMITER //
DROP TRIGGER IF EXISTS trigger_A//
CREATE TRIGGER trigger_A BEFORE INSERT ON table_A
FOR EACH ROW BEGIN
IF NEW.col1 IS NULL THEN
set NEW.col1 = <some-value>;
ENF IF;
END;
//
DELIMITER ;