Convert the below logic in oracle tree - sql

Recursively the parent is inserted first and then the children. How can I migrate the logic in Oracle using parent-child.The logic for the SQL should be first the parent is evaluated and then inserted or added and then the child is checked and then it is prepared.
Thank you for the response , the table structure as follow Name
REF_ID NOT NULL NUMBER
REF_TYPE_ID NOT NULL NUMBER
PARENT_REF_ID NUMBER
REF_VALUE NOT NULL VARCHAR2(255)
Now the expected result conditions: we need to start finding the query with ref_id, so if ref_id is supposed 14, then we need to check the corresponding should have parent_ref_id is null or not, if parent_ref_id is present suppose 4, then we need to again get the value for 4 as ref_id and check is parent_id is null or not If null then we need to check the value ref_type_id whether it is 1, 2,3,4,..; etc and based on the condition we need to append the result 4(parent_ref_id) with some text let it be 'article 4'. Now resuming back to the loop, the user had enter ref_id 14, then we need to check the value for ref_type_id for the corresponding and then we need to append with the result suppose 'par 14' and this result had to come after parent result: article 4 par 14 (article 4 - parent, par 14 children)

I believe that using a CONNECT BY statement with SYS_CONNECT_BY_PATH should be able to help you achieve your goal.
WITH
refs (ref_id,
ref_type_id,
parent_ref_id,
ref_value)
AS
(SELECT 501, 1, NULL, 207 FROM DUAL
UNION ALL
SELECT 502, 2, 501, 4 FROM DUAL
UNION ALL
SELECT 503, 3, 502, 1 FROM DUAL)
SELECT CONNECT_BY_ROOT r.ref_id as starting_ref_id,
TRIM (
',' FROM
SYS_CONNECT_BY_PATH (
CASE r.ref_type_id
WHEN 1 THEN 'article '
WHEN 2 THEN 'par '
WHEN 3 THEN '('
WHEN 4 THEN 'point '
WHEN 5 THEN 'sous '
WHEN 6 THEN NULL
WHEN 8 THEN NULL
ELSE '/'
END
|| r.ref_id,
',')) AS ref_label
FROM refs r
WHERE CONNECT_BY_ISLEAF = 1
CONNECT BY PRIOR r.parent_ref_id = r.ref_id;
STARTING_REF_ID REF_LABEL
__________________ ___________________________
501 article 501
502 par 502,article 501
503 (503,par 502,article 501

Related

Extracting most recent date from VARCHAR2

Data:
USER_ID VIOLATION_DATES
--------------------------------
1 18-Jul-21 > 24-Jul-21
2 05-Aug-21
3 09-Jun-21
1 18-Jul-21
I have a table that has columns for Users and their dates of violations. I want to extract the most recent violation for each user.
This is the query I've written:
select
USR_ID,
max(to_date(VIOLATION_DATES, 'DD-MON-YY')) as Most_Recent_VIOLATIONS
from
table
group by
USR_ID
However I get this error:
ORA-01830: date format picture ends before converting entire input string
I believe it has something to do with the way the most recent violation is appended (18-Jul-21 > 24-Jul-21 ). Can anyone provide any clarity on how I can extract the most recent date for each user? for example:
USER_ID VIOLATION_DATES
--------------------------
1 24-Jul-21
2 05-Aug-21
3 09-Jun-21
I understand that this storage method isn't ideal but this is out of my control.
You can split the multi-value string into separate rows, then "group by" as normal (the with clause is just to provide test data - substitute your actual table):
with demo (user_id, violation_dates) as
( select 1, '18-Jul-21 > 24-Jul-21' from dual union all
select 2, '05-Aug-21' from dual union all
select 3, '09-Jun-21' from dual union all
select 1, '18-Jul-21' from dual )
select user_id
, max(to_date(regexp_substr(d.violation_dates, '[^> ]+', 1, r.rn) default null on conversion error,'DD-MON-YY')) as violation_date
from demo d
cross apply
( select rownum as rn
from dual connect by rownum <= regexp_count(d.violation_dates,'>') +1 ) r
group by user_id
order by 1;
"default null on conversion error" requires Oracle 12.2.

Looping through string splitted in Oracle

I have a query which returns a data from a column[datatype varchar2] in string format.
select AuditEvents.BeforeValue from AuditEvents where condition......
Now , AuditEvents.BeforeValue has a mix type of data like null or "1,2,3,4,5" or "2,3,1,4" or "3,14" or "11:10" or "Security" --without quotes. These "1,2,3,4,5" are eventId in another table 'EventDesc'.
AuditEvents.BeforeValue column data depends on another column, AuditEvents.EventyType which is varchar. In EventyType column, if data is "ExcludedEvents" then BeforeValue should fetch data from another table EventDesc which has eventId description. Like 1=Event1 2=Event2 etc.So in case of "1,2,3" it should fetch data as "Event1,Event2,Event3"
My query is is it possible to fetch data based on above scenario directly in oracle query rather than doing in c# side(as I'm having alot of trouble when Ui asks for searching or sorting)
Is it possible? Yes, with some trouble because not all values exist in your events table, nor are all of them valid.
See this example, read comments within code.
SQL> with
2 -- sample data
3 events (id, name) as
4 (select 1, 'Event 1' from dual union all
5 select 2, 'Event 2' from dual union all
6 select 3, 'Event 3' from dual
7 ),
8 auditevents (beforevalue) as
9 (select '2,3,1' from dual union all
10 select 'security' from dual union all
11 select '11:10' from dual
12 ),
13 -- query you might need begins here
14 -- split AUDITEVENTS to rows
15 ae_split as
16 (select beforevalue,
17 regexp_substr(beforevalue, '[^,]+', 1, column_value) id
18 from auditevents cross join
19 table(cast(multiset(select level from dual
20 connect by level <= regexp_count(beforevalue, ',') + 1
21 ) as sys.odcinumberlist))
22 )
23 -- join split AUDITEVENTS with the EVENTS table
24 select s.beforevalue, listagg(e.name, ', ') within group (order by e.id) events
25 from ae_split s join events e on to_char(e.id) = s.id
26 where exists (select null from events a where to_char(a.id) = s.id)
27 group by s.beforevalue;
BEFOREVALUE EVENTS
----------- ------------------------------
2,3,1 Event 1, Event 2, Event 3
SQL>

How to convert single record row to column using in ORACLE Select statement with condition

I need to show the column names in the front side. That columns based on the condition satisfied in the select statement which is in the column have the value that column I need as one record. for that I am trying to writing the DB query. Could you please help on this query generating
Unpivot with additional condition; lines #1 - 4 represent sample data (you have that, so your query begins at line #5):
SQL> with test (id, emp_name, is_maths_passed, is_science_passed, is_social_passed) as
2 (select 123, 'Robert', 'YES', 'NO' , 'YES' from dual union all
3 select 345, 'Devid' , 'NO' , 'YES', 'YES' from dual
4 )
5 select code, passed
6 from (select * from (select * from test where id = &par_id)
7 unpivot (passed
8 for code
9 in (is_maths_passed as 'is_maths_passed',
10 is_science_passed as 'is_science_passed',
11 is_social_passed as 'is_social_passed'
12 )
13 )
14 )
15 where passed = 'YES';
Enter value for par_id: 123
CODE PAS
----------------- ---
is_maths_passed YES
is_social_passed YES
SQL> /
Enter value for par_id: 345
CODE PAS
----------------- ---
is_science_passed YES
is_social_passed YES
SQL>

Oracle - Split the parameter by comma and check if the parameter exist in Column

I am new to Oracle and not sure if there are any inbuilt functions to do this task.
I have a column that contains Product_ID's separated by comma.
Product_ID
123,234,546,789,487
I am passing a list of Product_ID's separated by a comma as varchar2.
so, I am passing "234,789" as varchar2.
I want to find if 234 and 789 exist in that column and if exists get that row.
How can I do this?
If you want to check that all the values in your input list are included in the column then you can use:
SELECT *
FROM table_name t
WHERE EXISTS (
WITH input ( value ) AS (
SELECT '123,789' FROM DUAL -- Your input value
)
SELECT 1
FROM input
WHERE ','||t.product_id||',' LIKE '%,' || REGEXP_SUBSTR( value, '[^,]+', 1, LEVEL ) || ',%'
CONNECT BY LEVEL <= REGEXP_COUNT( value, '[^,]+' )
HAVING COUNT(*) = REGEXP_COUNT( value, '[^,]+' )
)
Which, for the sample data:
CREATE TABLE table_name ( Product_ID ) AS
SELECT '123,234,546,789,487' FROM DUAL
Outputs:
| PRODUCT_ID |
| :------------------ |
| 123,234,546,789,487 |
If you want to check that at least one value in your input list is in the column then you can use the same query without the line containing the HAVING clause.
db<>fiddle here
Here's one way - making the comma-separated number lists into JSON arrays so that we can split them using json_table, then re-aggregating as nested tables so that we can compare with the submultiset operator:
create type table_of_pid as table of number;
/
with
sample_data (product_id) as (
select '123,234,546,789,487' from dual union all
select '333,444,555,666,888' from dual
)
, user_input (product_list) as (
select '234,789' from dual
)
select *
from sample_data
where ( select cast(collect(pid) as table_of_pid) as input_pid
from user_input cross apply
json_table('[' || product_list || ']', '$[*]'
columns pid number path '$')
)
submultiset
( select cast(collect(pid) as table_of_pid) as input_pid
from json_table('[' || product_id || ']', '$[*]'
columns pid number path '$')
)
;
PRODUCT_ID
-------------------
123,234,546,789,487
Your inputs violate First Normal Form, the most basic sanity requirement in a relational database. If the data was in normal form, you wouldn't need any of the JSON trickery. Still, the aggregation into collection and the submultiset comparison would be the correct approach even if the data was already in normal form.
It is a bad idea to store comma-separated values into a single column.
One option to do what you asked for is in the following example; read comments within code. Note that - for large tables - performance WILL suffer.
SQL> set ver off
SQL>
SQL> with
2 test (product_id) as
3 -- your sample table
4 (select '123,234,546,789,487' from dual union all
5 select '111,222,333' from dual
6 ),
7 test_split as
8 -- you have to split it into rows
9 (select product_id,
10 regexp_substr(product_id, '[^,]+', 1, column_value) val
11 from test
12 cross join table(cast(multiset(select level from dual
13 connect by level <= regexp_count(product_id, ',') + 1
14 ) as sys.odcinumberlist))
15 ),
16 parameter_split as
17 -- split input parameter into rows as well
18 (select regexp_substr('&&par_id', '[^,]+', 1, level) val
19 from dual
20 connect by level <= regexp_count('&&par_id', ',') + 1
21 )
22 -- join split values, return the result
23 select distinct t.product_id
24 from test_split t join parameter_split p on p.val = t.val;
Enter value for par_id: 123,546
PRODUCT_ID
-------------------
123,234,546,789,487
SQL> undefine par_id
SQL> /
Enter value for par_id: 333
PRODUCT_ID
-------------------
111,222,333
SQL>

PRIOR in SELECT list

I can't understand what it adds to the result of the query. From the book that I'm learning:
If you prefix a column name with PRIOR in the
select list (SELECT PRIOR EMPLOYEE_ID, ...), you specify the “prior” row’s value.
SELECT PRIOR EMPLOYEE_ID, MANAGER_ID, LPAD(' ', LEVEL * 2) || EMPLOYEES.JOB_ID
FROM EMPLOYEES
START WITH EMPLOYEE_ID = 100
CONNECT BY PRIOR EMPLOYEE_ID = MANAGER_ID;
The only difference I see, is that it adds a NULL value in the first row and increments IDs of employees by 1.
PRIOR just takes a record from a previous record in the traversed hierarchy.
I think the best way to undestand how it works is to play with a simple hierarchy:
create table qwerty(
id int,
name varchar2(100),
parent_id int
);
insert all
into qwerty values( 1, 'Grandfather', null )
into qwerty values( 2, 'Father', 1 )
into qwerty values( 3, 'Son', 2 )
into qwerty values( 4, 'Grandson', 3 )
select 1234 from dual;
The below query traverses the above hierarchy:
select level, t.*
from qwerty t
start with name = 'Grandfather'
connect by prior id = parent_id
LEVEL ID NAME PARENT_ID
---------- ---------- -------------------- ----------
1 1 Grandfather
2 2 Father 1
3 3 Son 2
4 4 Grandson 3
If we add "PRIOR name" to the above query, then the name of "parent" is displayed. This vaue is taken from prevoius record in the hierarchy (from LEVEL-1)
select level, prior name as parent_name, t.*
from qwerty t
start with name = 'Grandfather'
connect by prior id = parent_id;
LEVEL PARENT_NAME ID NAME PARENT_ID
---------- -------------------- ---------- -------------------- ----------
1 1 Grandfather
2 Grandfather 2 Father 1
3 Father 3 Son 2
4 Son 4 Grandson 3
PRIOR operator returns previous value in a hierarchy build using CONNECT BY clause.
WITH hierarchy(id, parent_id, value) AS (
SELECT 1, NULL, 'root' FROM dual UNION ALL
SELECT 2, 1, 'child 1' FROM dual UNION ALL
SELECT 3, 1, 'child 2' FROM dual UNION ALL
SELECT 4, 3, 'grand child 1' FROM dual
)
SELECT
hierarchy.*, LEVEL depth, PRIOR value
FROM
hierarchy
START WITH
parent_id IS NULL
CONNECT BY
PRIOR id = parent_id
This simple query connects the rows from root to leafs. The PRIORVALUE column returns value of VALUE column of row's parent row (predecessor within the hierarchy), so 'grand child 1' parent is 'child 2' or 'child 1' parent is 'root'. 'root', the first row within the hierarchy (LEVEL = 1) doesn't have any parent therefore PRIOR returns NULL.
If you connect the hierarchy in opposite direction, from a leaf to the root, the PRIOR operator will return child row that was used to connect the row you're looking at.
The LEVEL column shows the depth of specific row within the hierarchy.