Reputation: 13
We have Oracle 12 database.
I need to loop through table and update the values based on previous row. Like below I need to get the value from row 1 and update it to rows 2 to 6. Then get the new value from row 7 and continue to the end of the table. The column1 is csv-formatted text so I'll need to extract the number-value from there...
id column1 column2
1 xxx;yyy;zzz;123456;
2 aaa
3 bbb
4 ccc
5 ddd
6 eee
7 xxx;yyy;zzz;789123;
8 aaa
After the update table should look like this:
id column1 column2
1 xxx;yyy;zzz;123456;
2 aaa 123456
3 bbb 123456
4 ccc 123456
5 ddd 123456
6 eee 123456
7 xxx;yyy;zzz;789123;
8 aaa 789123
Tbh I don't have much experience with pl/sql. I tried to use pl/sql while loop but It didn't work.
If someone could kick me in right direction that would be appreciated.
Upvotes: 0
Views: 9527
Reputation: 128
Something like this should do what you want. It loops through the table, passing the last bit of the first record into a variable every time it starts with 'xxx' and updates column 2 if it doesn't.
DECLARE
L_UpdateVal VARCHAR2(10) := '';
BEGIN
FOR REC IN (SELECT Column1, column2, ROWNUM From table) LOOP
IF SUBSTR(REC.column1, 1, 3) = 'xxx' THEN
L_UpdateVal := SUBSTR(column1, 13, 6);
ELSE
UPDATE Table SET Column2 = L_UpdateVal
WHERE ROWNUM := REC.ROWNUM
END IF;
END LOOP;
END;
Upvotes: 1
Reputation: 23578
You don't need PL/SQL for this, you could do it in a single MERGE statement.
First off, work out how to get the results you're after - you can do this by using the 'LAST_VALUE()' analytic function, like so:
WITH your_table AS (SELECT 1 id, 'xxx;yyy;zzz;123456;' column1, NULL column2 FROM dual UNION ALL
SELECT 2 id, 'aaa' column1, NULL column2 FROM dual UNION ALL
SELECT 3 id, 'bbb' column1, NULL column2 FROM dual UNION ALL
SELECT 4 id, 'ccc' column1, NULL column2 FROM dual UNION ALL
SELECT 5 id, 'ddd' column1, NULL column2 FROM dual UNION ALL
SELECT 6 id, 'eee' column1, NULL column2 FROM dual UNION ALL
SELECT 7 id, 'xxx;yyy;zzz;789123;' column1, NULL column2 FROM dual UNION ALL
SELECT 8 id, 'aaa' column1, NULL column2 FROM dual)
select id,
column1,
last_value(CASE WHEN substr(column1, -1) = ';' THEN
regexp_substr(column1, ';*([[:digit:]]*)(;$)', 1, 1, NULL, 1)
END IGNORE NULLS) OVER (ORDER BY ID) column2
from your_table;
ID COLUMN1 COLUMN2
---------- ------------------- -------------------
1 xxx;yyy;zzz;123456; 123456
2 aaa 123456
3 bbb 123456
4 ccc 123456
5 ddd 123456
6 eee 123456
7 xxx;yyy;zzz;789123; 789123
8 aaa 789123
Then you can use that in a MERGE statement to do the update, like so:
MERGE INTO your_table tgt
USING (select id,
column1,
CASE WHEN substr(column1, -1) = ';' THEN 'Y' ELSE 'N' END driving_column1,
last_value(CASE WHEN substr(column1, -1) = ';' THEN
regexp_substr(column1, ';*([[:digit:]]*)(;$)', 1, 1, NULL, 1)
END IGNORE NULLS) OVER (ORDER BY ID) column2 -- assuming id drives the correct order to use here
from your_table) src
ON (tgt.id = src.id) -- assuming id is the primary key of your_table
WHEN MATCHED THEN
UPDATE SET tgt.column2 = src.column2;
If you don't want to update column2 of the column1 rows where the values are the csv values (I've assumed that there will be a semicolon present if they're csv values), then you can either update the ON clause to include and driving_column1 = 'N'
or you can wrap the source subquery in an outer query that filters on the driving_column1 (you can't filter on analytic functions within the same query, unfortunately) or you can add a where clause to the update section of the merge statement.
Upvotes: 0
Reputation: 167822
You do not need PL/SQL for this, it can be done entirely in SQL using a combination of MERGE
, the LAG( ... ) IGNORE NULLS ...
analytic function and REGEXP_SUBSTR
(to extract the sub-string):
MERGE INTO table_name dst
USING (
SELECT id,
CASE WHEN val IS NULL
THEN LAG( val ) IGNORE NULLS OVER ( ORDER BY id )
END AS val
FROM (
SELECT id,
REGEXP_SUBSTR( column1, ';(\d{6});$', 1, 1, NULL, 1 ) AS val
FROM table_name
)
) src
ON ( dst.id = src.id )
WHEN MATCHED THEN
UPDATE SET column2 = src.val;
Updated table:
id column1 column2
-- ------------------- -------
1 xxx;yyy;zzz;123456;
2 aaa 123456
3 bbb 123456
4 ccc 123456
5 ddd 123456
6 eee 123456
7 xxx;yyy;zzz;789123;
8 aaa 789123
Upvotes: 1