How to compare two comma separated string and insert in DB - oracle

I have following table (Users) with 2 columns and trying to update second column with first column ( first column is autoincrement and primary key field).
UserId CatId
1 10
2 78
3 99
4 89
5 80
I am finding difficult to update second column with P_uid order by (1,3,4). I tried normal Update statement but it doesn't work. I am passing following ids into SP.
P_uid varchar2(20) := '1,3,4';
P_new_cat_id varchar2(20) := '100,12,13';
Expected output
---------------
UserId CatId
1 100
2 78
3 12
4 13
5 80

Use a MERGE statement:
MERGE INTO users dst
USING (
SELECT 1 AS id, 100 AS new_catid FROM DUAL UNION ALL
SELECT 3, 12 FROM DUAL UNION ALL
SELECT 4, 13 FROM DUAL
) src
ON (src.id = dst.userid)
WHEN MATCHED THEN
UPDATE
SET catid = src.new_catid;
Which, for the sample data:
CREATE TABLE users (UserId, CatId) AS
SELECT 1, 10 FROM DUAL UNION ALL
SELECT 2, 78 FROM DUAL UNION ALL
SELECT 3, 99 FROM DUAL UNION ALL
SELECT 4, 89 FROM DUAL UNION ALL
SELECT 5, 80 FROM DUAL;
Then, after the merge, the table contains:
USERID
CATID
1
100
2
78
3
12
4
13
5
80
If you have delimited strings then (apart from finding a better solution for passing multi-row data) you can split the values into rows using a row-generator function and use the previous technique:
DECLARE
P_uid varchar2(20) := '1,3,4';
P_new_cat_id varchar2(20) := '100,12,13';
BEGIN
MERGE INTO users dst
USING (
SELECT REGEXP_SUBSTR(p_uid, '\d+', 1, LEVEL) AS id,
REGEXP_SUBSTR(p_new_cat_id, '\d+', 1, LEVEL) AS new_catid
FROM DUAL
CONNECT BY LEVEL <= LEAST(
REGEXP_COUNT(p_uid, '\d+'),
REGEXP_COUNT(p_new_cat_id, '\d+')
)
) src
ON (src.id = dst.userid)
WHEN MATCHED THEN
UPDATE
SET catid = src.new_catid;
END;
/
Which outputs the same.
db<>fiddle here

Related

How to execute stored procedure cursor records parallelly

I have a stored procedure which does following tasks on 2 tables related to each other as below
CREATE TABLE address (adr_id, ver_id, address) AS
SELECT 1, 1, 'newYork' FROM DUAL UNION ALL
SELECT 1, 2, 'newYork' FROM DUAL UNION ALL
SELECT 1, 3, 'newYork' FROM DUAL UNION ALL
SELECT 4, 1, 'Washington' FROM DUAL UNION ALL
SELECT 4, 2, 'Washington' FROM DUAL;
CREATE TABLE employee (emp_id,adr_id,ver_id,) AS
SELECT 100,1, 1 FROM DUAL UNION ALL
SELECT 200,1, 2 FROM DUAL UNION ALL
SELECT 300,1, 3 FROM DUAL UNION ALL
SELECT 400,4, 1 FROM DUAL UNION ALL
SELECT 500,4, 2 FROM DUAL;
Here Following tasks are done in stored procedure and required to be executed in parallel as to increase throughput on billions of records in both table
Note: Foreign key Constraints are relaxed
Find records with distinct address "newYork" and have them in cursor
Loop on Cursor record as follows
and process each address record select * from address where address ="newYork"
Insert a new record in Address table with same address as first record (with ver_id=0) in #3 and take the newly inserted id
Find parent table records based on foreign key in Employee table and update them with newly id mentioned in #4.
delete all records of #3
Since Step 2 to 6 can be executed in parallel just wanted to know how this can be achieved ?
Expected After
Address
adr_id
ver_id
address
11
0
newYork
12
0
Washington
Employee
id
adr_id
ver_id
100
11
1
200
11
2
300
11
3
400
12
1
500
12
2
As a frame challenge, do not update the adr_id and just change the version; that way you do not need to change the referential constraints.
MERGE INTO address dst
USING (
SELECT ROWID AS rid,
COUNT(*) OVER (PARTITION BY adr_id) AS cnt,
ROW_NUMBER() OVER (PARTITION BY adr_id ORDER BY ver_id) AS rn
FROM address
) src
ON (dst.ROWID = src.rid AND src.cnt > 1)
WHEN MATCHED THEN
UPDATE
SET ver_id = 0
DELETE WHERE src.rn > 1
fiddle

How to insert values to newly added column

I am new to oracle thus this question.
There is a table already existed and I have added a new column to it.
There are 5 rows and I do not want to use update table with where clause to insert the values one by one in the new column. Is there a statement like INSERT ALL to insert the values into the new column in one shot ?
Thanks
You can also use something like below which in-effect I would say is multiple update only, wrapped in single statement.
SQL> select * from test_upd;
ID1 ID2
---------- ----------
1
2
3
4
SQL> update test_upd a set a.id2 =
2 (select
3 case
4 when id1=1 then 100
5 when id1=2 then 200
6 when id1=3 then 300
7 else 5000 end
8 from test_upd b
9 where a.id1=b.id1);
4 rows updated.
SQL> select * from test_upd;
ID1 ID2
---------- ----------
1 100
2 200
3 300
4 5000
Use a MERGE statement:
MERGE INTO your_table dst
USING (
SELECT 1 AS id, 'aaa' AS newvalue FROM DUAL UNION ALL
SELECT 2, 'bbb' FROM DUAL UNION ALL
SELECT 3, 'ccc' FROM DUAL UNION ALL
SELECT 4, 'ddd' FROM DUAL UNION ALL
SELECT 5, 'eee' FROM DUAL
) src
ON (dst.id = src.id)
WHEN MATCHED THEN
UPDATE SET value = src.newvalue;
Which, if you have the table:
CREATE TABLE your_table (id, value) AS
SELECT 1, CAST(NULL AS VARCHAR2(3)) FROM DUAL UNION ALL
SELECT 2, NULL FROM DUAL UNION ALL
SELECT 3, NULL FROM DUAL UNION ALL
SELECT 4, NULL FROM DUAL UNION ALL
SELECT 5, NULL FROM DUAL;
Then, after the MERGE, the table contains:
ID
VALUE
1
aaa
2
bbb
3
ccc
4
ddd
5
eee
db<>fiddle here

Oracle: find the largest number within one string

I have some strings within a column of a table like asdfAB98:(hjkl,)AB188(uiop)uuuAB78:jknd. I am wondering how may I extract the largest number within such a string in each row. For example, here the largest number is 188 (out of 188, 98 and 78).
Since the numbers I'm interested in are always right after AB, I was thinking about using regexp_substr. Unfortunately, I'm not sure how to have it output multiple rows so that I can use max clause. PLSQL language would be great as well. Please show me a simple example if you have an idea. Thank you in advance!
You could tokenize the string into all its number components, and then find the maximum:
select max(to_number(
regexp_substr('sdfAB98:(hjkl,)AB188(uiop)uuuAB78:jknd', '(\d+)', 1, level))
) as max_value
from dual
connect by regexp_substr('sdfAB98:(hjkl,)AB188(uiop)uuuAB78:jknd', '(\d+)', 1, level)
is not null;
MAX_VALUE
----------
188
or
select max(to_number(
regexp_substr('sdfAB98:(hjkl,)AB188(uiop)uuuAB78:jknd', '(\d+)', 1, level, null, 1))
) as max_value
from dual
connect by level <= regexp_count('sdfAB98:(hjkl,)AB188(uiop)uuuAB78:jknd', '\d+');
MAX_VALUE
----------
188
If you need to get values from multiple rows you need the connect-by to match the IDs, and also need to include a reference to a non-deterministic function to prevent looping; with two values in a CTE:
with your_table (id, str) as (
select 1, 'sdfAB98:(hjkl,)AB188(uiop)uuuAB78:jknd' from dual
union all select 2, '123abc456abc78d9' from dual
)
select id, max(to_number(regexp_substr(str, '(\d+)', 1, level, null, 1))) as max_value
from your_table
connect by prior id = id
and prior dbms_random.value is not null
and level <= regexp_count(str, '\d+')
group by id;
ID MAX_VALUE
---------- ----------
1 188
2 456
Alternatively (to Alex' answer), if there are multiple rows:
SQL> with your_table (id, str) as (
2 select 1, 'sdfAB98:(hjkl,)AB188(uiop)uuuAB78:jknd' from dual
3 union all select 2, '123abc456abc78d9' from dual
4 )
5 select id, max(to_number(regexp_substr(str, '\d+', 1, column_value))) max_num
6 from your_table,
7 table(cast(multiset(select level from dual
8 connect by level <= regexp_count(str, '\d+')
9 ) as sys.odcinumberlist))
10 group by id;
ID MAX_NUM
---------- ----------
1 188
2 456
SQL>

Oracle: Getting highest value from two fields (date then value)

I have something like this (date simplified to integer just for the example):
Order Date Value
12 5 555
12 5 800
12 2 900
13 3 122
13 4 155
14 1 121
... ... ...
And I'd like to get the order with the highest date and then the highest value:
Order Date Value
12 5 800
13 4 155
14 1 121
... ... ...
I know this is similar to several other questions but cant figure out how to apply those answers to my case, sorry.
Thanks!
Use analytic function ROW_NUMBER
SELECT "Order", "Date", "Value"
FROM (
select t.*,
row_number()
over (partition by "Order" order by "Date" desc, "Value" desc ) As rn
FROM table1 t
) x
WHERE rn = 1
You can use the analytic FIRST/LAST function (see Oracle documentation). This solution does not need the subquery/outer query arrangement. I changed the column names since at least ORDER and DATE are Oracle keywords.
with
inputs ( ord, dt, val ) as (
select 12, 5, 555 from dual union all
select 12, 5, 800 from dual union all
select 12, 2, 900 from dual union all
select 13, 3, 122 from dual union all
select 13, 4, 155 from dual union all
select 14, 1, 121 from dual
)
-- End of test data (not part of the solution).
-- SQL query begins BELOW THIS LINE
select ord, max(dt) as dt, max(val) keep (dense_rank last order by dt) as val
from inputs
group by ord
;
ORD DT VAL
--- -- ---
12 5 800
13 4 155
14 1 121
Lets assume your table name is orders
In a subquery you find orders with highest dates and for those you find highest values... It will look like this:
select order,date,max(value) value from orders
where (order,date) in
(select order, max(date) date
from orders
group by order
)
group by order,date
order by order,date desc

Getting Results in Horizontal way in oracle

I wrote query
select s_id from emp
where s_inv=12
i got results in this manner
1
2
3
4
5
but i want it in this format
1 2 3 4 5
If you need your result in a single column, you can use LISTAGG:
with emp(s_id, s_inv) as
(
select 1, 12 from dual union all
select 2, 12 from dual union all
select 3, 12 from dual union all
select 4, 12 from dual union all
select 5, 12 from dual
)
select listagg(s_id, ' ') within group (order by s_id)
from emp
where s_inv = 12
If you need to build many columns on the same row, you should first define how many columns will your result have

Resources