How to store data from a SELECT statement and use that data to loop with an UPDATE statement - sql

I am using PLSQL and I want to store the query results form SELECT statement in an array and then I want to loop using the elements from that array to UPDATE all the rows. The problem with the code below is that it returns a single-row. Sub-query returns more than one row because he is trying to set more than one variable in a row. Can you help me in this situation?
This is my code:
CREATE OR REPLACE PROCEDURE looping IS
BEGIN
FOR rec IN (SELECT IID FROM DATMCCN0)
LOOP
UPDATE DATMCCN0
SET E_NOME = (SELECT I_NOME FROM DAT_CCNCONFIG0 INNER JOIN DATMCCN0 ON DAT_CCNCONFIG0.I_NOME = DATMCCN0.CAPLIC where DATMCCN0.IID = rec.IID)
where IID = rec.IID;
END LOOP;
END;
EXECUTE looping;

You do not need a loop and can do it all in one MERGE statement (assuming your correlated query returns a single row for each IID):
CREATE OR REPLACE PROCEDURE looping
IS
BEGIN
MERGE INTO DATMCCN0 dst
USING (
SELECT b.IID,
I_NOME
FROM DAT_CCNCONFIG0 a
INNER JOIN DATMCCN0 b
ON a.I_NOME = b.CAPLIC
) src
ON ( src.IID = dst.IID)
WHEN MATCHED THEN
UPDATE SET E_NOME = src.I_NOME;
END;
If it does not then you will need to get only a single row, something like this:
CREATE OR REPLACE PROCEDURE looping
IS
BEGIN
MERGE INTO DATMCCN0 dst
USING (
SELECT b.IID,
MAX( I_NOME ) AS I_NOME
FROM DAT_CCNCONFIG0 a
INNER JOIN DATMCCN0 b
ON a.I_NOME = b.CAPLIC
GROUP BY b.IID
) src
ON ( src.IID = dst.IID)
WHEN MATCHED THEN
UPDATE SET E_NOME = src.I_NOME;
END;

One literal answer for your question "How to store data from a SELECT statement and use that data [to loop] with an UPDATE statement" would be a statement like this:
UPDATE
(SELECT src.E_NOME, dst.I_NOME
FROM DAT_CCNCONFIG0
JOIN DATMCCN0 src ON DAT_CCNCONFIG0.I_NOME = scr.CAPLIC
JOIN DATMCCN0 dst ON src.IID = dst.IID)
SET E_NOME = I_NOME;
However, it does not solve your problem that a single-row subquery returns more than one. Have a look at MT0's answer for that.

Related

Not able to update columns of a table by bind parameters using sql developer

The following select statement returns one row:
SELECT *
FROM CM_APOLLO_DET
WHERE
CM_APOLLO_DET.DETAIL_ID = TRIM(:detailId) AND
CM_APOLLO_DET.HEADER_ID IN (SELECT HEADER_ID FROM CM_APOLLO_HDR
WHERE TRIM(FILE_NAME) = TRIM(:fileName));
Values of bind parameters are as follows:
detailId: 775686609762
filename:sample3.txt
but when I run following update statement, it updates zero rows.
UPDATE CM_APOLLO_DET
SET CM_APOLLO_DET.DIVISION = :div
WHERE
CM_APOLLO_DET.DETAIL_ID = TRIM(:detailId) AND
CM_APOLLO_DET.HEADER_ID IN (SELECT HEADER_ID FROM CM_APOLLO_HDR
WHERE TRIM(FILE_NAME) = TRIM(:fileName));
div=2030
detailId: 775686609762
filename:sample3.txt
This update where condition is same as above select statement.
You can try below using exists
update CM_APOLLO_DET set CM_APOLLO_DET.DIVISION=:div
where CM_APOLLO_DET.DETAIL_ID=trim(:detailId) and
exists (SELECT 1 from
CM_APOLLO_HDR where CM_APOLLO_DET.HEADER_ID=CM_APOLLO_HDR.HEADER_ID
and trim(FILE_NAME)=trim(:fileName)
);

How to update all of the values of a table using merge into statement

How to update all the values from source table to destination table using merge into statement?
What I am trying to do is something like:
merge into src_table
using (select * from dest) dest_table
on (<some_condition>)
when matched then update set src_table.* = dest_table.*
where <condition>
I didn't find anything related to this on Google. I know one can achieve this using execute immediate style statement but I am looking for a better way.
you can do it with pl/sql
for src in ( select * from B ) loop
update A set ROW = src where A.id = src.id;
end loop;
or you can use alter table;
alter table your_table
rename to
your_new_table;

Update all rows with one SQL query

I'm using this PostgreSQL table to store configuration variables:
CREATE TABLE SYS_PARAM(
SETTING_KEY TEXT NOT NULL,
VALUE_TYPE TEXT,
VALUE TEXT
)
;
How I can update all configuration settings values using one SQL statement?
you can use where true at the end and it update all rows in your table.
for example:
UPDATE table_name set table_column = value where true;
it will be update all rows in one SQL query.
If you plan on performing these updates more than once or twice over time, it would be good to have a function handle this for you. You could use the table itself as a type for a variadic parameter within a function, like so:
-- The function
CREATE OR REPLACE FUNCTION update_sys_param(VARIADIC params sys_param[])
RETURNS VOID
AS $$
BEGIN
UPDATE sys_param
SET value_type = upd.value_type, value = upd.value
FROM
sys_param src
INNER JOIN
UNNEST(params) upd
ON (src.setting_key = upd.setting_key);
END; $$ LANGUAGE PLPGSQL;
-- To call it
SELECT update_sys_param(('SMTP_PORT','int','123'),('SMTP_OTHER','text','435343'));
However, if this is a one-time update you can try either of these two:
UPDATE using JOIN
UPDATE sys_param
SET
value_type = new.value_type,
value = new.value
FROM
sys_param src
INNER JOIN
new_params new --< this table/view/cte must already exist or you must create it.
ON (src.setting_key = new.setting_key);
UPDATE using CASE
UPDATE sys_param
SET value = CASE setting_key
WHEN 'SMTP_PORT' THEN '2100'
(..and so on..)
END;
-- You would need to repeat the case statement if you intend on updating the value_type, too.
I guess you can achieve this by doing correlated update.
Please refer to the posts below:
https://www.ibm.com/support/knowledgecenter/ssw_i5_54/sqlp/rbafyexsub4.htm
You can join against a list of values and update with that:
update sys_param
set value = v.new_value
from (
values
('SMTP_PORT', '123'),
('SMTP_SERVER', 'new_server'),
('SMTP_USERNAME', 'Arthur')
) as v(skey, new_value)
where v.skey = sys_param.setting_key;
This assumes that setting_key is the primary key of that table.

Update Trigger Affecting Too Many Rows

I wrote the following trigger:
begin
update NFL.TeamStatistics
set Passing_Yards = (select sum(Quarterbacks.Yards)
from NFL.Quarterbacks
where Quarterbacks.Team = inserted.Team)
from NFL.Quarterbacks
inner join inserted on Quarterbacks.Team = inserted.Team;
Whenever someone updates the passing yards in the table about quarterbacks, it should automatically set NFL.TeamStatistics.Passing_Yards to the sum of each team's passing yards.
I used the following update statement to test it:
update NFL.Quarterbacks
set Quarterbacks.Yards = 4000
where Team = 'PIT';
However, in the table NFL.TeamStatistics it set the passingyards for all teams to 4000 instead of just for PIT. What is the matter?
You need to add a where clause and column in NFL.TeamStatistics that you want to compare to
e.g:
begin
update NFL.TeamStatistics
set Passing_Yards = (select sum(Quarterbacks.Yards)
from NFL.Quarterbacks
where Quarterbacks.Team = inserted.Team)
from NFL.Quarterbacks
inner join inserted on Quarterbacks.Team = inserted.Team
where Team = Quarterbacks.Team;
Assuming you were comparing to Quaterbacks.Team
You should use where clause in your update statement in order to filter data you are going to update.

How to delete all rows without deleting the last returned row in SQL Server?

I want to delete all the rows from a SELECT without deleting the last returned row by using a trigger when a delete query is executed.
This trigger doesn't work so any help is greatly appreciated.
CREATE TRIGGER TR_StergereOfertaSpeciala
ON OferteSpeciale
INSTEAD OF DELETE
AS
DECLARE #nr INTEGER;
IF (EXISTS(SELECT * FROM DELETED))
BEGIN
SET #nr = (SELECT COUNT(*) FROM DELETED);
DELETE FROM (
SELECT TOP(#nr - 1)* FROM OferteSpeciale
INNER JOIN DELETED ON OferteSpeciale.codP = Deleted.codP
AND OferteSpeciale.codM = Deleted.codM
AND OferteSpeciale.dela = Deleted.dela)
END
Here is an example of getting your concept to work properly:
CREATE TRIGGER TR_StergereOfertaSpeciala
ON OferteSpeciale
INSTEAD OF DELETE
AS BEGIN
DECLARE #nr INT
SET #nr = (SELECT COUNT(*) FROM DELETED)
IF (#nr > 1) BEGIN
DELETE o
FROM OferteSpeciale AS o
INNER JOIN (SELECT TOP (#nr - 1) * FROM DELETED /* ORDER BY ??? */) AS d
ON o.codP = d.codP
AND o.codM = d.codM
AND o.dela = d.dela
END
END
Note the syntax for a delete with a join. Also note that we're arbitrarily choosing the 1 row to keep. I would suggest, as #RBarryYoung has mentioned, specifically ordering the set by something to know which row we are keeping.
Another way of doing this which could avoid the somewhat dynamic TOP clause (clever, BTW) would be to specifically exclude the record you want to keep using NOT EXISTS/IN
Also, you probably want to avoid trigger recursion and nested triggers in this case.