Oracle hierachial query to select only the root parents - sql

I have a tree data and I am trying to select only the root parents. The data may be subset of a larger set so it is possible that parent may not be empty. I would like to top most level for each tree in the data set.
with test_data as (
select '1' ID,'100' name, null parent from dual
union
select '2' ID,'200' name, null parent from dual
union
select '3' ID,'300' name, null parent from dual
union
select '1.1' ID,'1.100' name, '1' parent from dual
union
select '2.1' ID,'2.100' name, '2' parent from dual
union
select '3.1' ID,'3.100' name, '3' parent from dual
union
select '3.1.1' ID,'3.1.100' name, '3.1' parent from dual
union
select '3.1.2' ID,'3.1.2.100' name, '3.1' parent from dual
union
select '4.1' ID,'4.100' name, '4' parent from dual
union
select '4.1.1' ID,'4.1.100' name, '4.1' parent from dual
union
select '4.1.2' ID,'4.1.2.100' name, '4.1' parent from dual )
select * from test_data
start with parent is null
connect by parent=prior id
I would like to see results as
ID NAME PAR
----- --------- ---
1 100
2 200
3 300
4.1 4.100 4
Rowid 4 is not selected as part of subset is a parent, but since 4.1 is a top most in this data set, i would like to return that row. So basically, i would like to see all top most level records for each hierarchy.
Thank You.

One method is using not exists:
select id, name, parent
from test_data td
where not exists (select 1
from test_data td2
where td.parent = td2.id
);

Related

oracle query to show tourist_name' and the places which is not visited by tourist

I have one table with two columns in it. First one is 'tourist_name' and second one is 'visited_places'. So I need a query which will give me following output :
'tourist_name' and the places which is not visited by tourist.
You can generate the rows using cross-join and then simply use minus clause to get your desired result -
WITH TAB AS (SELECT 1 TOURIST_NAME, 'B' VISITED_PLACE FROM DUAL UNION ALL
SELECT 1, 'C' FROM DUAL UNION ALL
SELECT 1, 'D' FROM DUAL UNION ALL
SELECT 2, 'A' FROM DUAL UNION ALL
SELECT 2, 'E' FROM DUAL)
SELECT TOURIST_NAME, VISITED_PLACE FROM (SELECT DISTINCT VISITED_PLACE FROM TAB),
(SELECT DISTINCT TOURIST_NAME FROM TAB)
MINUS
SELECT TOURIST_NAME, VISITED_PLACE FROM TAB;
Link.

how to get the missing values in SQL query when using in clause

Suppose I have the following query :
select value from table where value in ('abc','cde','efg');
If only 'abc' is populated in the table,
I want to be able to see which value is missing in the result set,
so the results looks like :
cde
efg
You can use UNION ALL to get a resultset with all the values that you want:
SELECT 'abc' AS value FROM dual UNION ALL
SELECT 'cde' FROM dual UNION ALL
SELECT 'efg' FROM dual
(you may omit FROM dual depending on your database).
And with NOT EXISTS get all the values from the above resultset that do not appear in the table:
SELECT u.*
FROM (
SELECT 'abc' AS value FROM dual UNION ALL
SELECT 'cde' FROM dual UNION ALL
SELECT 'efg' FROM dual
) u
WHERE NOT EXISTS (SELECT 1 FROM tablename t WHERE t.value = u.value)

How can I add two columns sequentially (and not concatenate)?

I am trying to pull two tables from an Oracle SQL database, and want to join them sequentially, so they appear as if they are one list.
List one has items [1,2,3,4]
List two has items [a,b,c,d]
I want to output [1,2,3,4,a,b,c,d]
Any thoughts?
One option uses a UNION with a computed column:
SELECT val
FROM
(
SELECT val, 1 AS position FROM table1
UNION ALL
SELECT val, 2 AS position FROM table2
) t
ORDER BY
position, val;
Demo
Note that I assume that all data here is text. If not, e.g. the first table be numeric, then we would have to do a cast along the way. But, this is not the main focus of your question anyway.
SELECT id_1, value_column1 from table_1
UNION
SELECT id_2, value_column2 from table_2;
if the types of columns are different - make sure you cast/convert them to char() - the resulting type should be same.
https://docs.oracle.com/cd/B19306_01/server.102/b14200/queries004.htm
use union, i think 1,2,3 as numeric value that why converted it on varchar as for union you have to same data type
with t1 as (
select 1 as id from dual union all
select 2 from dual union all
select 3 from dual union all
select 4 from dual
), t2 as (
select 'a' as item from dual union all
select 'b' from dual union all
select 'c' from dual union all
select 'd' from dual
)
select cast(id as varchar(20)) as id from t1
union
select * from t2
demo
output
1
2
3
4
a
b
c
d

print name of parents who have children with same name

PARENT table PK is PID and PID is FK in CHILDREN table. How do I print names of Parents from PARENT table that have children who share the same name as another child in CHILDREN table? I think a recursive join should be used to find the same name but I can't get it to work. I am able to join the PARENT and CHILDREN tables using below query:
select PARENT.NAME as ParentName
from PARENT inner join CHILDREN
on PARENT.PID=CHILDREN.PID
group by NAME;
I have tried this query to complete the recursive join but it isn't working:
select CHILDREN.NAME
from CHILDREN e, CHILDREN m
where e.CHILDREN.PID=m.CHILDREN.PID
order by CHILDREN.PID;
Group by child name and evaluate, if at least two different parents exist.
-- TEST DATA
with parent(pid, name) as
(select 1, 'Parent1' from dual
union all
select 2, 'Parent2' from dual
union all
select 3, 'Parent3' from dual
union all
select 4, 'Parent4' from dual),
children(name, pid) as
(select 'Tom', 1 from dual
union all
select 'Tim', 1 from dual
union all
select 'Steven', 2 from dual
union all
select 'Tim', 2 from dual
union all
select 'Marta', 2 from dual
union all
select 'Jess', 3 from dual
union all
select 'Jim', 4 from dual
union all
select 'Jess', 4 from dual)
--> SELECT
select c.name, listagg(p.name, ',') within group(order by p.name)
from parent p
join children c
on c.pid = p.pid
group by c.name -- group by child name
having min (p.pid) <> max (p.pid) -- at least two different parents
--> RESULT
Jess Parent3,Parent4
Tim Parent1,Parent2
AND Parent.Name = Children.Name
So wouldn't this print names of the parents where it matches with Children's names?

How to get an accurate JOIN using Fuzzy matching in Oracle

I'm trying to join a set of county names from one table with county names in another table. The issue here is that, the county names in both tables are not normalized. They are not same in count; also, they may not be appearing in similar pattern always. For instance, the county 'SAINT JOHNS' in "Table A" may be represented as 'ST JOHNS' in "Table B". We cannot predict a common pattern for them.
That means , we cannot use "equal to" (=) condition while joining. So, I'm trying to join them using the JARO_WINKLER_SIMILARITY function in oracle.
My Left Outer Join condition would be like:
Table_A.State = Table_B.State
AND UTL_MATCH.JARO_WINKLER_SIMILARITY(Table_A.County_Name,Table_B.County_Name)>=80
I've given the measure 80 after some testing of the results and it seemed to be optimal.
Here, the issue is that I'm getting set of "false Positives" when joining. For instance, if there are some counties with similarity in names under the same state ("BARRY'and "BAY" for example), they will be matched if the measure is >=80.
This creates inaccurate set of joined data.
Can anyone please suggest some work around?
Thanks,
DAV
Can you plz help me to build a query that will lookup Table_A for each record in Table B/C/D, and match against the county name in A with highest ranked similarity that is >=80
Oracle Setup:
CREATE TABLE official_words ( word ) AS
SELECT 'SAINT JOHNS' FROM DUAL UNION ALL
SELECT 'MONTGOMERY' FROM DUAL UNION ALL
SELECT 'MONROE' FROM DUAL UNION ALL
SELECT 'SAINT JAMES' FROM DUAL UNION ALL
SELECT 'BOTANY BAY' FROM DUAL;
CREATE TABLE words_to_match ( word ) AS
SELECT 'SAINT JOHN' FROM DUAL UNION ALL
SELECT 'ST JAMES' FROM DUAL UNION ALL
SELECT 'MONTGOMERY BAY' FROM DUAL UNION ALL
SELECT 'MONROE ST' FROM DUAL;
Query:
SELECT *
FROM (
SELECT wtm.word,
ow.word AS official_word,
UTL_MATCH.JARO_WINKLER_SIMILARITY( wtm.word, ow.word ) AS similarity,
ROW_NUMBER() OVER ( PARTITION BY wtm.word ORDER BY UTL_MATCH.JARO_WINKLER_SIMILARITY( wtm.word, ow.word ) DESC ) AS rn
FROM words_to_match wtm
INNER JOIN
official_words ow
ON ( UTL_MATCH.JARO_WINKLER_SIMILARITY( wtm.word, ow.word )>=80 )
)
WHERE rn = 1;
Output:
WORD OFFICIAL_WO SIMILARITY RN
-------------- ----------- ---------- ----------
MONROE ST MONROE 93 1
MONTGOMERY BAY MONTGOMERY 94 1
SAINT JOHN SAINT JOHNS 98 1
ST JAMES SAINT JAMES 80 1
Using some made up test data inline (you would use your own TABLE_A and TABLE_B in place of the first two with clauses, and begin at with matches as ...):
with table_a (state, county_name) as
( select 'A', 'ST JOHNS' from dual union all
select 'A', 'BARRY' from dual union all
select 'B', 'CHEESECAKE' from dual union all
select 'B', 'WAFFLES' from dual union all
select 'C', 'UMBRELLAS' from dual )
, table_b (state, county_name) as
( select 'A', 'SAINT JOHNS' from dual union all
select 'A', 'SAINT JOANS' from dual union all
select 'A', 'BARRY' from dual union all
select 'A', 'BARRIERS' from dual union all
select 'A', 'BANANA' from dual union all
select 'A', 'BANOFFEE' from dual union all
select 'B', 'CHEESE' from dual union all
select 'B', 'CHIPS' from dual union all
select 'B', 'CHICKENS' from dual union all
select 'B', 'WAFFLING' from dual union all
select 'B', 'KITTENS' from dual union all
select 'C', 'PUPPIES' from dual union all
select 'C', 'UMBRIA' from dual union all
select 'C', 'UMBRELLAS' from dual )
, matches as
( select a.state, a.county_name, b.county_name as matched_name
, utl_match.jaro_winkler_similarity(a.county_name,b.county_name) as score
from table_a a
join table_b b on b.state = a.state )
, ranked_matches as
( select m.*
, rank() over (partition by m.state, m.county_name order by m.score desc) as ranking
from matches m
where score > 50 )
select rm.state, rm.county_name, rm. matched_name, rm.score
from ranked_matches rm
where ranking = 1
order by 1,2;
Results:
STATE COUNTY_NAME MATCHED_NAME SCORE
----- ----------- ------------ ----------
A BARRY BARRY 100
A ST JOHNS SAINT JOHNS 80
B CHEESECAKE CHEESE 92
B WAFFLES WAFFLING 86
C UMBRELLAS UMBRELLAS 100
The idea is matches computes all scores, ranked_matches assigns them a sequence within (state, county_name), and the final query picks all the top scorers (i.e. filters on ranking = 1).
You may still get some duplicates as there is nothing to stop two different fuzzy matches scoring the same.