Update multiple table column values using single query - sql

How would you update data in multiple tables using a single query?
MySQL Example
The equivalent code in MySQL:
UPDATE party p
LEFT JOIN party_name n ON p.party_id = n.party_id
LEFT JOIN party_details d ON p.party_id = d.party_id
LEFT JOIN incident_participant ip ON ip.party_id = p.party_id
LEFT JOIN incident i ON ip.incident_id = i.incident_id
SET
p.employee_id = NULL,
c.em_address = 'x#x.org',
c.ad_postal = 'x',
n.first_name = 'x',
n.last_name = 'x'
WHERE
i.confidential_dt IS NOT NULL
What would be the same statement using Oracle 11g?
Thank you!
RTFM
It seems a single query is insufficient when using Oracle:
http://download-west.oracle.com/docs/cd/B10501_01/server.920/a96540/statements_108a.htm#2067717

/** XXX CODING HORROR... */
Depending on your needs, you could use an updateable view. You create a view of your base tables and add an "instead of" trigger to this view and you update the view directly.
Some example tables:
create table party (
party_id integer,
employee_id integer
);
create table party_name (
party_id integer,
first_name varchar2(120 char),
last_name varchar2(120 char)
);
insert into party values (1,1000);
insert into party values (2,2000);
insert into party values (3,3000);
insert into party_name values (1,'Kipper','Family');
insert into party_name values (2,'Biff','Family');
insert into party_name values (3,'Chip','Family');
commit;
select * from party_v;
PARTY_ID EMPLOYEE_ID FIRST_NAME LAST_NAME
1 1000 Kipper Family
2 2000 Biff Family
3 3000 Chip Family
... then create an updateable view
create or replace view party_v
as
select
p.party_id,
p.employee_id,
n.first_name,
n.last_name
from
party p left join party_name n on p.party_id = n.party_id;
create or replace trigger trg_party_update
instead of update on party_v
for each row
declare
begin
--
update party
set
party_id = :new.party_id,
employee_id = :new.employee_id
where
party_id = :old.party_id;
--
update party_name
set
party_id = :new.party_id,
first_name = :new.first_name,
last_name = :new.last_name
where
party_id = :old.party_id;
--
end;
/
You can now update the view directly...
update party_v
set
employee_id = 42,
last_name = 'Oxford'
where
party_id = 1;
select * from party_v;
PARTY_ID EMPLOYEE_ID FIRST_NAME LAST_NAME
1 42 Kipper Oxford
2 2000 Biff Family
3 3000 Chip Family

I was having the same problem I couldn't find a easy way to do this in Oracle.
Look here:
Oracle Update Statements for more info.

You could use Oracle MERGE statement to do this. It is a bulk update-or-insert kind of statement based on joining the target table with an inline view.
MERGE INTO bonuses D
USING (
SELECT employee_id, salary, department_id FROM employees
WHERE department_id = 80
) S ON (D.employee_id = S.employee_id)
WHEN MATCHED THEN
UPDATE SET D.bonus = D.bonus + S.salary*.01
WHEN NOT MATCHED THEN
INSERT (D.employee_id, D.bonus)
VALUES (S.employee_id, S.salary*0.1);
if you do not need the insert part, you just omit the last 3 lines above.

In some cases it's possible to use PL/SQL to achieve this. In my case I searched for matching rows in two tables by some criteria, then updated each row in a loop.
Something like this:
begin
for r in (
select t1.id as t1_id, t2.id as t2_id
from t1, t2
where ...
) loop
update t1
set ...
where t1.id = r.t1_id;
update t2
set ...
where t2.id = r.t2_id;
end loop;
end;

Related

UPDATE from another table with multiple WHERE criteria

In Postgres 9.5, I want to connect to another DB using Postgres' dblink, get data and then use them to update another table.
-- connect to another DB, get data from table, put it in a WITH
WITH temp_table AS
(
SELECT r_id, descr, p_id
FROM
dblink('myconnection',
'SELECT
r_id, descr, p_id
FROM table
WHERE table.p_id
IN (10,20);'
)
AS tempTable(r_id integer, descr text, p_id integer)
)
-- now use temp_table to update
UPDATE anothertable
SET
descr =temp_table.descr
FROM anothertable AS x
INNER JOIN temp_table
ON
x.r_id = temp_table.r_id
AND
x.p_id = temp_table.p_id
AND
x.p_id IN (2) ;
dblink works fine and if I do select * from temp_table before the UPDATE, it has data.
The issue is the UPDATE itself. It runs with no errors, but it never actually updates the table.
I tried changing the UPDATE to:
UPDATE anothertable
SET
descr =temp_table.descr
FROM anothertable AS x , temp_table
WHERE x.r_id = temp_table.r_id
AND
x.p_id = temp_table.p_id
AND
x.p_id IN (2) ;
Same as above: runs with no errors, but it never actually updates the table.
I also tried to change the UPDATE to:
UPDATE anothertable
INNER JOIN temp_table
ON x.r_id = temp_table.r_id
AND
x.p_id = temp_table.p_id
AND
x.p_id IN (2)
SET descr =temp_table.descr
But I get:
ERROR: syntax error at or near "INNER" SQL state: 42601
Character: 1894
How can I fix this to actually update?
Don't repeat the target table in the FROM clause of the UPDATE:
WITH temp_table AS ( ... )
UPDATE anothertable x
SET descr = t.descr
FROM temp_table t
WHERE x.r_id = t.r_id
AND x.p_id = t.p_id
AND x.p_id IN (2);
Or simplified:
...
AND x.p_id = 2
AND t.p_id = 2
The manual:
Do not repeat the target table as a from_item unless you intend a self-join (in which case it must appear with an alias in the from_item).
Related:
UPDATE statement with multiple joins in PostgreSQL
SQL update query with substring WHERE clause

SQL can not refer table in from clause within where

I have a simple SQL query used in oracle 11g
select something
from sc
where sc.column satisfies something
but I can not refer SC in the where composite, any one can help explain this, thanks in advance.
---second edit: I tested the sql command in oracle 19c it works but in 11g it does not work.
table contents
create table sc(
sno varchar2(10),
cno varchar2(10),
score number(4,2),
constraint pk_sc primary key (sno,cno)
);
example data
insert into sc values ('s001','c001',78.9);
insert into sc values ('s002','c001',80.9);
insert into sc values ('s003','c001',81.9);
insert into sc values ('s004','c001',60.9);
insert into sc values ('s001','c002',82.9);
insert into sc values ('s002','c002',72.9);
insert into sc values ('s003','c002',81.9);
insert into sc values ('s001','c003','59');
sql command
SELECT SNO
FROM SC A
WHERE 0 = (SELECT COUNT(*)
FROM
(SELECT B.CNO
FROM SC B
WHERE B.SNO = 's001'
MINUS
SELECT C.CNO
FROM SC C
WHERE A.SNO = C.SNO) --this is the error location, oracle reports invalid identifier A.
);
I would try a exists clause instead of where 0 =
SELECT SNO
FROM SC A
WHERE not exists (SELECT B.CNO
FROM SC B
WHERE B.SNO = 's001'
MINUS
SELECT C.CNO
FROM SC C
WHERE A.SNO = C.SNO) --this is the error location, oracle reports undefined A.
I'm assuming you want to return where there is not a match?
If you're trying to get the CNO that are not only in SNO 's001'.
Then maybe this.
SELECT DISTINCT SNO, cno
FROM SC sc
WHERE SNO = 's001'
AND EXISTS (
SELECT 1
FROM SC sc2
WHERE sc2.CNO = sc.CNO
AND sc2.SNO != sc.SNO
)

How can I insert into a nested table from the resultset of a select statement?

I have two tables with nested tables of the same type, the type is:
CREATE OR REPLACE TYPE tipo_valor AS OBJECT (
ano DATE, --year
cantidad INTEGER --ammount of exported wine
) ;
CREATE OR REPLACE TYPE hist_export AS OBJECT (
nombre_pais VARCHAR2(100), --name of importer country
cantidad tipo_valor --type referenced above
);
the nested table:
CREATE OR REPLACE TYPE nt_hist_exp IS
TABLE OF hist_export;
And my two tables are:
CREATE TABLE bodega ( --winery
id_bod INTEGER NOT NULL,
exp_an_bod nt_hist_exp ,
)
CREATE TABLE marca ( --wine
id_marca INTEGER NOT NULL,
exp_an_marca nt_hist_exp
)
I have procedure with a select statement that collects the export ammounts from the wine table on a certain year and orders it by country,
PROCEDURE exp_bod ( p_ano DATE,
p_bod_nom VARCHAR2)IS
sumatoria INTEGER;
p_idbod INTEGER;
BEGIN
SELECT id_bod INTO p_idbod
FROM bodega
WHERE nombre_bod = p_bod_nom;
DBMS_OUTPUT.PUT_LINE(to_char(p_idbod));
SELECT nt.nombre_pais,sum(nt.cantidad.cantidad)
INTO sumatoria
FROM bodega b
JOIN presentacion p on p.bodega_fk = b.id_bod
JOIN marca m on m.id_marca = p.marca_fk
CROSS JOIN TABLE(m.exp_an_marca) nt
WHERE b.id_bod = p_idbod
AND nt.cantidad.ano = p_ano
group by nt.nombre_pais
order by nt.nombre_pais;
);
end exp_bod;
the second select in this procedure successfully returns what I need which is a resultset with two columns,one with the country names and the second one with the export ammounts all summed up and ordered, what I want is to insert the rows from that resultset into the nested table in the winery table including the year which is received as an argument by the function
You could use insert as select, creating an instance of your object type as part of the query:
INSERT INTO TABLE (SELECT exp_an_bod FROM bodega b WHERE b.nombre_bod = p_bod_nom)
SELECT hist_export(nt.nombre_pais, tipo_valor(nt.cantidad.ano, sum(nt.cantidad.cantidad)))
FROM bodega b
JOIN presentacion p on p.bodega_fk = b.id_bod
JOIN marca m on m.id_marca = p.marca_fk
CROSS JOIN TABLE(m.exp_an_marca) nt
WHERE b.nombre_bod = p_bod_nom
AND nt.cantidad.ano = p_ano
GROUP BY nt.nombre_pais, nt.cantidad.ano;
I'm assuming nombre_bod is a column on bodega, though you haven't shown that in the table definition, which means you don't really need a separate look-up for that.
This also assumes that exp_an_bod is not null; it can be empty though. It also doesn't make any allowance for an existing row for the country, but it's not very clear from your data model whether than can exist or what should happen if it does. You can update en existing entry using the same mechanism though, as long as you can identify it.
You can do it in PL/SQL like this:
declare
hist_exp nt_hist_exp;
begin
select exp_an_bod
into hist_exp
from bodega
where id_bod = 123;
hist_exp.extend;
hist_exp(hist_exp.LAST) := hist_export('xyz', 456);
update bodega
set exp_an_bod = hist_exp
where id_bod = 123;
end;
If you like to UPDATE rather then INSERT you can also use
UPDATE (select nombre_pais, cantida, id_bod FROM bodega CROSS JOIN TABLE(exp_an_bod))
SET nombre_pais = 'abc'
WHERE id_bod = 123
and cantida = 456;
You may also try
INSERT INTO (select nombre_pais, cantida, id_bod FROM bodega CROSS JOIN TABLE(exp_an_bod)) ...
but I don't think this is possible - I never tried.

How to join table and get values

I have 2 tables:
LOGS_TABLE:
FID (NUMBER)
SERIAL_NUMBER (VARCHAR2)
LOG_RESULT (VARCHAR2)
POINT_TABLE:
FID (NUMBER)
SERIAL_NUMBER (VARCHAR2)
FID_LOG (NUMBER)
The attribute to join tables is SERIAL_NUMBER, but what I need is to insert into POINT_TABLE.FID_LOG values from LOGS_TABLE.FID on basement SERIAL_NUMBER relations.
How can I do this?
For Oracle you may use following statement:
UPDATE (SELECT p.FID_LOG fid_log
, l.FID fid
FROM POINT_TABLE p
, LOGS_TABLE l
WHERE p.SERIAL_NUMBER = l.SERIAL_NUMBER)
SET fid_log = fid
Also it is possible to use MERGE statement like this:
MERGE INTO POINT_TABLE p
USING (SELECT *
FROM LOGS_TABLE) l
ON (p.SERIAL_NUMBER = l.SERIAL_NUMBER)
WHEN MATCHED THEN
UPDATE
SET p.FID_LOG = l.FID;

How to fetch all id from table after multiple insertion on a table

I am passing datatable as input parameter to a stored procedure. I have created custom type for it.
Here is my stored procedure:
INSERT INTO Employee
([Name],[Lname],[Code])
SELECT [Name],[Lname],#Code
FROM #tblEmp A
WHERE NOT EXISTS (SELECT 1
FROM Employee B
inner join Contactdetail c
on cid = B.cid
WHERE B.[Name] = A.[Name]
AND B.[Lname] = A.[Lname]
AND C.[mobno] = A.[mobno])
Here I fetching record from datatable and inserting into Employee table. Datatable contain Name,Lname and mobileno. I want to check combination of Name,Lname and mobileno.If combination of it present in Employee table,pls don't insert record([Name], [Lname], #Code ) in Employee.Else Insert.After inserting record from Employee,I want id of all inserted record.Scope_identity give last identity of table.I want all id,which are newly inserted into table because in same sp,I want to do further processing.
You can use Output clause like:
DECLARE #InsertedIDs table(EmployeeID int);
INSERT INTO Employee
([Name],[Lname],[Code])
--
OUTPUT INSERTED.ID
INTO #InsertedIDs
--
SELECT [Name],[Lname],#Code
FROM #tblEmp A
WHERE NOT EXISTS (SELECT 1
FROM Employee B
inner join Contactdetail c
on cid = B.cid
WHERE B.[Name] = A.[Name]
AND B.[Lname] = A.[Lname]
AND C.[mobno] = A.[mobno])
[DEMO]
You can this with a MERGE statement. (Could be there is a mistake in this query, but its just so you get the idea behind it...)
MERGE INTO Employee AS Target
USING (SELECT B.[Name], B.[Lname], C.[mobno]
FROM Employee B
inner join Contactdetail c
on cid = B.cid ) AS source (Name, Lname, mobno)
ON Target.[Name] = Source.[Name]
AND Target.[Lname] = Source.[Lname]
AND Target.[mobno] = Source.[mobno]
WHEN NOT MATCHED BY TARGET THEN
INSERT (Name, Lname, code) VALUES (Name, Lname, #Code);