Insert into Select (Invalid identifier Error) - oracle

I have one Table called Andamio (the destiny table), with these columns:
CODTIPOLOCALIZACION
TXTNOMBRE
CODLOCALIZACION
CODCENTRO
CODPUBLICO
TXTDESCRIPCION
And another one called Centro (the origin one), with these columns:
CODCENTRO
CODPARKING
TXTPARKING
I want to fill all the rows with the same values in the first two columns:
CODTIPOLOCALIZACION = 2 for all
TXTNOMBRE = "Park" for all
The third column comes from an Automatic Sequence: 'MySeq_seq'
CODLOCALIZACION = MySeq_seq.NEXTVAL
And the last three columns have to be the same from the origin table "Centro" but with the condition that there is no other row in the destiny table "Andamio" with the same values of CODCENTRO and CODPUBLICO equals to CODCENTRO and CODPARKING.
INSERT INTO ANDAMIO
(CODTIPOLOCALIZACION, TXTNOMBRE, CODLOCALIZACION, CODCENTRO, CODPUBLICO,
TXT_DESCRIPCION)
SELECT '2', 'Park', MySeq_seq.NEXTVAL, Datos.CODCENTRO, Datos.CODPARKING, Datos.TXTPARKING
FROM (SELECT CODCENTRO, CODPARKING, TXTPARKING FROM CENTRO centrop
WHERE (centrop.CODCENTRO <> ANDAMIO.CODCENTRO
AND centrop.CODPARKING <> ANDAMIO.CODPUBLICO)) Datos
I have tried these INSERT INTO sequence with many variations [using INSERT INTO table VALUES ('2', 'Park', SELECT(...))] and many others...
I always get the Invalid Identifier as Oracle claims there is no column called CODPUBLICO in table Andamio, but it exists indeed.
Any help would be appreciated.
Thanks!

You're getting that error because you're referencing the andamio columns in the select part of the insert statement without also having referenced the andamio table in the from clause.
I suspect a not exists clause would give you what you're after, eg:
insert into andamio (codtipolocalizacion,
txtnombre,
codlocalizacion,
codcentro,
codpublico,
txt_descripcion)
select '2',
'Park',
myseq_seq.nextval,
datos.codcentro,
datos.codparking,
datos.txtparking
from (select codcentro, codparking, txtparking
from centro cen
where not exists (select null
from andamio an
where cen.codcentro = an.codcentro
and cen.codparking = an.codpublico) datos;

Related

Not enough values when inserting a row in oracle

I'm trying to insert a row by selecting some datas from another table.
insert into spb (id_barang,nama_barang,qty,lokasi_tujuan,lokasi_asal,waktu)
values
((select stg.id_barang,stg.nama_barang,calculate_req_stok(display.max_stok,display.stok,display.pcs_in_ctn,display.id_barang),display.lokasi,stg.lokasi
from display
inner join stg
on display.nama_barang = stg.nama_barang
where stg.stok >= calculate_req_stok(display.max_stok,display.stok,display.pcs_in_ctn,display.id_barang) * display.pcs_in_ctn),current_time_ms);
It shows this error : ORA-00947: not enough values.
I don't know what causes this even though I inserted enough values
Wrong syntax; remove values and move the last value to be inserted (current_time_ms) into select's column list:
INSERT INTO spb(
id_barang,
nama_barang,
qty,
lokasi_tujuan,
lokasi_asal,
waktu
)
(SELECT stg.id_barang,
stg.nama_barang,
calculate_req_stok(
display.max_stok,display.stok,
display.pcs_in_ctn,
display.id_barang
),
display.lokasi,
stg.lokasi,
current_time_ms
FROM display
INNER JOIN stg ON display.nama_barang = stg.nama_barang
WHERE stg.stok >=
calculate_req_stok
(display.max_stok,display.stok,
display.pcs_in_ctn,
display.id_barang
) * display.pcs_in_ctn
);

Update statement with joins in Oracle

I need to update one column in table A with the result of a multiplication of one field from table A with one field from table B.
It would be pretty simple to do this in T-SQL, but I can't write the correct syntax in Oracle.
What I've tried:
UPDATE TABLE_A
SET TABLE_A.COLUMN_TO_UPDATE =
(select TABLE_A.COLUMN_WITH_SOME_VALUE * TABLE_B.COLUMN_WITH_PERCENTAGE
from TABLE_A
INNER JOIN TABLE_B
ON TABLE_A.PRODUCT_ID = TABLE_B.PRODUCT_ID
AND TABLE_A.SALES_CHANNEL_ID = TABLE_B.SALES_CHANNEL_ID)
WHERE TABLE_A.MONTH_ID IN (201601, 201602, 201603);
But I keep getting errors. Could anybody help me, please?
I generally prefer to use the below format for such cases since this will ensure there's no update performed if there's no data in the table(query extracted temp table) whereas in the above solution provided by Brian Leach will update the new value as null if there's no record present in the 2nd table but exists in the first table.
UPDATE
(
select TABLE_A.COLUMN_TO_UPDATE
, TABLE_A.PRODUCT_ID
, TABLE_A.COLUMN_WITH_SOME_VALUE * TABLE_B.COLUMN_WITH_PERCENTAGE as value
from TABLE_A
INNER JOIN TABLE_B
ON TABLE_A.PRODUCT_ID = TABLE_B.PRODUCT_ID
AND TABLE_A.SALES_CHANNEL_ID = TABLE_B.SALES_CHANNEL_ID
AND TABLE_A.MONTH_ID IN (201601, 201602, 201603)
) DATA
SET DATA.COLUMN_TO_UPDATE = DATA.value;
This solution can cause key preserved value issues which shouldn't be an issue here since i expect a single row in both the tables for one product(ID).
More on Key Preserved table concept in inner join can be found here
https://asktom.oracle.com/pls/asktom/f?p=100:11:::::P11_QUESTION_ID:548422757486
#Jayesh Mulwani raiesed a valid point, this will set the value to null if there is no matching record. This may or may not be the desired result. If it isn't, and no change is desirect, you can change the select statement to:
coalesce((SELECT table_b.column_with_percentage
FROM table_b
WHERE table_a.product_id = table_b.product_id AND table_a.sales_channel_id = table_b.sales_channel_id),1)
If this is the desired outcome, Jayesh's solution will be more efficient as it will only update matching records.
UPDATE table_a
SET table_a.column_to_update = table_a.column_with_some_value
* (SELECT table_b.column_with_percentage
FROM table_b
WHERE table_a.product_id = table_b.product_id
AND table_a.sales_channel_id = table_b.sales_channel_id)
WHERE table_a.month_id IN (201601, 201602, 201603);

SELECT within UPDATE gives an error

I am getting SQL Error: ORA-01779: cannot modify a column which maps to a non key-preserved table error on this statement:
UPDATE
(
SELECT CELLS.NUM, UND.CLIENT_PARAMS
FROM CELLS
LEFT OUTER JOIN UND
ON CELLS.UND_ID = UND.ID
WHERE CELLS.SASE = 1
) t
SET t.CLIENT_PARAMS = 'test';
I would like to update CLIENT_PARAMS field for all rows, which select returns.
The most straightforward (though possibly not the most efficient) way to update rows in one table which correspond directly to rows in another table via an identity column would be to use WHERE table1.column IN (SELECT id FROM table2 WHERE ...).
In this case:
UPDATE UND
SET client_params = 'test'
WHERE id IN
(SELECT und_id
FROM CELLS
WHERE SASE=1)
Try this
UPDATE und u
SET client_params = 'test'
WHERE EXISTS
(SELECT 1
FROM cells c
WHERE C.SASE = 1
AND c.und_id = u.id)

Copy records from one table to another with pl-sql

I want to copy records from one table to another.
The only records from table 1 that will be copied to table 2 are the ones that still dont exist in table 2.
If duplicate records exists in Table 1 then only be copied to table 2 the record with the larger size name.
I could already implement a query that almost does what I want.
The problem I have is when there are names with the same maximum size of characters.
In these cases, my query returns more than one record and I just want to insert one new record in table 2.
Does anyone know how I can fix this?
Here is my code:
For x in (Select distinct xdd.id_t, xdd.name_t
From table1 xdd
Where xdd.id_t not in (Select distinct det.id_t2
From table2 det)
And LENGTH(xdd.name_t) in (Select Max(LENGTH(xdd2.name_t))
From table1 xdd2
Where xdd2.id_t = xdd.id_t)
) Loop
Insert into id_t2 (id_t2, name_t2)
Values (x.id_t, x.name_t);
End loop;
Can you give me an example to solve this?
Sure. If I understood requirements correctly, then the merge statement will look similar to this one:
We use row_number() analytic function to choose a duplicate record with longer name_t
merge into table_two t2
using(
select id_t
, name_t
from (select id_t
, name_t
, row_number() over(partition by id_t
order by length(name_t) desc) as rn
from table_one) q
where q.rn = 1
) t1
on (t2.id_t = t1.id_t)
when not matched then
insert(id_t, name_t)
values(t1.id_t, t1.name_t)
SQLFiddle demo
This is a merge statement that should "upsert" data from table 1 into table 2. Matching keys should update only when the name field in table1 is greater than that of table 2. And inserts should occur when keys from table one are not matched to table 2.
MERGE INTO table2 D
USING (SELECT table1.id_t, table1.name_t FROM table1) S
ON (D.id_t2 = S.id_t)
WHEN MATCHED THEN UPDATE SET D.name_t2 = S.name_t
WHERE (LENGTH(S.name_t) > LENGTH(D.name_t2))
WHEN NOT MATCHED THEN INSERT (D.id_t, D.name_t)
VALUES (S.id_t2, S.name_t2);

Rownum in the join condition

Recently I fixed the some bug: there was rownum in the join condition.
Something like this: left join t1 on t1.id=t2.id and rownum<2. So it was supposed to return only one row regardless of the “left join”.
When I looked further into this, I realized that I don’t understand how Oracle evaluates rownum in the "left join" condition.
Let’s create two sampe tables: master and detail.
create table MASTER
(
ID NUMBER not null,
NAME VARCHAR2(100)
)
;
alter table MASTER
add constraint PK_MASTER primary key (ID);
prompt Creating DETAIL...
create table DETAIL
(
ID NUMBER not null,
REF_MASTER_ID NUMBER,
NAME VARCHAR2(100)
)
;
alter table DETAIL
add constraint PK_DETAIL primary key (ID);
alter table DETAIL
add constraint FK_DETAIL_MASTER foreign key (REF_MASTER_ID)
references MASTER (ID);
prompt Disabling foreign key constraints for DETAIL...
alter table DETAIL disable constraint FK_DETAIL_MASTER;
prompt Loading MASTER...
insert into MASTER (ID, NAME)
values (1, 'First');
insert into MASTER (ID, NAME)
values (2, 'Second');
commit;
prompt 2 records loaded
prompt Loading DETAIL...
insert into DETAIL (ID, REF_MASTER_ID, NAME)
values (1, 1, 'REF_FIRST1');
insert into DETAIL (ID, REF_MASTER_ID, NAME)
values (2, 1, 'REF_FIRST2');
insert into DETAIL (ID, REF_MASTER_ID, NAME)
values (3, 1, 'REF_FIRST3');
commit;
prompt 3 records loaded
prompt Enabling foreign key constraints for DETAIL...
alter table DETAIL enable constraint FK_DETAIL_MASTER;
set feedback on
set define on
prompt Done.
Then we have this query :
select * from master t
left join detail d on d.ref_master_id=t.id
The result set is predictable: we have all the rows from the master table and 3 rows from the detail table that matched this condition d.ref_master_id=t.id.
Result Set
Then I added “rownum=1” to the join condition and the result was the same
select * from master t
left join detail d on d.ref_master_id=t.id and rownum=1
The most interesting thing is that I set “rownum<-666” and got the same result again!
select * from master t
left join detail d on d.ref_master_id=t.id and rownum<-666.
Due to the result set we can say that this condition was evaluated as “True” for 3 rows in the detail table. But if I use “inner join” everything goes as supposed to be.
select * from master t
join detail d on d.ref_master_id=t.id and rownum<-666.
This query doesn’t return any row,because I can't imagine rownum to be less then -666 :-)
Moreover, if I use oracle syntax for outer join, using “(+)” everything goes well too.
select * from master m ,detail t
where m.id=t.ref_master_id(+) and rownum<-666.
This query doesn’t return any row too.
Can anyone tell me, what I misunderstand with outer join and rownum?
ROWNUM is a pseudo-attribute of result sets, not of base tables. ROWNUM is defined after rows are selected, but before they're sorted by an ORDER BY clause.
edit: I was mistaken in my previous writeup of ROWNUM, so here's new information:
You can use ROWNUM in a limited way in the WHERE clause, for testing if it's less than a positive integer only. See ROWNUM Pseudocolumn for more details.
SELECT ... WHERE ROWNUM < 10
It's not clear what value ROWNUM has in the context of a JOIN clause, so the results may be undefined. There seems to be some special-case handling of expressions with ROWNUM, for instance WHERE ROWNUM > 10 always returns false. I don't know how ROWNUM<-666 works in your JOIN clause, but it's not meaningful so I would not recommend using it.
In any case, this doesn't help you to fetch the first detail row for each given master row.
To solve this you can use analytic functions and PARTITION, and combine it with Common Table Expressions so you can access the row-number column in a further WHERE condition.
WITH numbered_cte AS (
SELECT *, ROW_NUMBER() OVER (PARTITION BY t.id ORDER BY d.something) AS rn
FROM master t LEFT OUTER JOIN detail d ON d.ref_master_id = t.id
)
SELECT *
FROM numbered_cte
WHERE rn = 1;
if you want to get the first three values from the join condition change the select statement like this.
select *
from (select *
from master t left join detail d on d.ref_master_id=t.id)
where rownum<3;
You will get the required output. Take care on unambigiously defined column names when using *
Let me give an absolute answer which u can run directly with out making any changes to the code.
select *
from (select t.id,t.name,d.id,d.ref_master_id,d.name
from master t left join detail d on d.ref_master_id=t.id)
where rownum<3;
A ROWNUM filter doesn't make any sense in a join, but it isn't being rejected as invalid.
The explain plan will either include the ROWNUM filter or exclude it. If it includes it, it will apply the filter to the detail table after applying the other join condition(s). So if you put in ROWNUM=100 (which will never be satisfied) all the detail rows are excluded and then the outer join kicks in.
If you put in ROWNUM=1 it seems to drop the filter.
And if you query
with
a as (select rownum a_val from dual connect by level < 10),
b as (select rownum*2 b_val from dual connect by level < 10)
select * from a left join b on a_val < b_val and rownum in (1,3);
you get something totally weird.
It probably should be rejected as an error, so expect nonsensical things to happen

Resources