Split rows in multiple lines in oracle 11g and fetching in jsp - sql

The table is as follows:
+------------+--------------+
| product id | Rates |
+------------+--------------+
| 108 | 10, 20, 30 |
+------------+--------------+
| 109 | 10,30 |
+------------+--------------+
I want to create the following:
+------------+--------------+
| Name | Rates |
+------------+--------------+
| 108 | 10 |
+------------+--------------+
| | 20 |
+------------+--------------+
| | 30 |
+------------+--------------+
I want like this in oracle 11g and I am fetching the row in jsp.

Assuming that the table is called product_rate, query bellow may result the answer:
SELECT CASE
WHEN LEVEL = 1 THEN product_id ELSE NULL END AS Name,
regexp_substr(Rates, '[^,]+', 1, LEVEL) Rates
FROM product_rate
CONNECT BY LEVEL <= length(regexp_replace(Rates, '[^,]+')) + 1;
Sqlfiddle demo

Try using this query :
WITH tab(product_id, Rates) AS
(SELECT 108, '10, 20, 30' FROM dual UNION ALL
SELECT 109, '10,30' FROM dual )
-------
--End of data
-------
SELECT CASE WHEN LEVEL = 1 THEN product_id ELSE NULL END AS product_id,
regexp_substr(rates, '[^,]+', 1, LEVEL) rates
FROM TAB
CONNECT BY regexp_substr(rates, '[^,]+', 1, LEVEL) IS NOT NULL
AND PRIOR rates = rates
and prior sys_guid() is not null;
Output:
| PRODUCT_ID | RATES |
|------------|-------|
| 108 | 10 |
| (null) | 20 |
| (null) | 30 |
| 109 | 10 |
| (null) | 30 |

Related

bigquery group by and get all elements except the groupby value

I'm dealing with a some transactional history in bigquery. The table contains two columns:
transaction_number and item_id.
I'm trying to identify two features:
How many (average and std) products are purchased along with a certain item_id in the same transaction?
What are the list of products purchased along with the certain item_id in the same transaction?
For example: if we assume these are the products purchased in the same transaction,
|---------------------|------------------|
| trans_num | item_id |
|---------------------|------------------|
| 1 | 34 |
|---------------------|------------------|
| 1 | 35 |
|---------------------|------------------|
| 2 | 36 |
|---------------------|------------------|
| 2 | 37 |
|---------------------|------------------|
| 2 | 34 |
|---------------------|------------------|
I want the first output to be
|----------------------|------------------|
| item_id | feature_1 |
|----------------------|------------------|
| 34 | 2.5 |
|----------------------|------------------|
| 35 | 2 |
|----------------------|------------------|
| 36 | 2 |
|----------------------|------------------|
| 37 | 2 |
|----------------------|------------------|
| 38 | 2 |
|----------------------|------------------|
And feature_2 should contain
|--------|------------|
|item_id | feature 2 |
|--------|------------|
| 34 |[35, 36, 37]|
|--------|------------|
| 35 | [34] |
|--------|------------|
| 36 | [37, 34] |
|--------|------------|
| 37 | [36, 34] |
|--------|------------|
How should I approach this?
Below is for BigQuery Standard SQL
#standardSQL
with pre_aggregation as (
select a.trans_num, a.item_id, array_agg(b.item_id) other_items
from `project.dataset.table` a
join `project.dataset.table` b
on a.trans_num = b.trans_num
and a.item_id != b.item_id
group by trans_num, item_id
order by item_id, trans_num
)
select item_id,
feature_1,
array (
select distinct item
from t.feature_2 item
order by item
) as feature_2
from (
select item_id,
avg(array_length(other_items)) as feature_1,
array_concat_agg(other_items) as feature_2
from pre_aggregation
group by item_id
) t
if to apply to sample data from your question
`project.dataset.table` as (
select 1 trans_num, 34 item_id union all
select 1, 35 union all
select 2, 36 union all
select 2, 37 union all
select 2, 34
)
output is

Grouping when using analytic functions

Let's suppose we have a table that looks like this:
Level|Depth|Descrip|
0 | 0 | Base |
1 | 50 | Level_1 |
2 | 53 | Level_2 |
3 | 60 | Level_3 |
8 | 80 | Level_8 |
10 | 81 | Level_10|
15 | 101 | Level_15|
16 | 102 | Level_16|
17 | 102 | Level_16_bis|
18 | 103 | Level_17|
I need, in first place, to get the rows that represent significative(more than 15 mts) depth jump respecting the previous ones. I get those rows doing something like this:
Select level,depth, descrip from(
Select level
, depth
,lag(depth) over (order by level asc) as prev_depth
, descrip
from ground_levels
)
Where abs(depth-prev_depth) > 15 and depth > 0
Which give me a table like this:
Level|Depth|Descrip|
1 | 50 | Level_1|
8 | 80 | Level_8|
15 | 101 | Level_15|
Now, I need to collect the levels that falls in between the jumps. So, I need something like this:
Level|Depth| Descrip | Equivalent_levels |
1 | 50 | Level_1 | 2,3 |
8 | 80 | Level_8 | 10 |
15 | 101 | Level_15| 16,17,18 |
I have being doing some searching about use "listagg", rank() and other analytic functions but I'm stuck with the script :(
In addition, it would be great if I can start a grouping when this condition is meet: abs(depth-prev_depth) > 15, so I can get something like that:
Level|Depth|Descrip | Group_ID
1 | 50 | Level_1 | 1 |
2 | 53 | Level_2 | 1 |
3 | 60 | Level_3 | 1 |
8 | 80 | Level_8 | 2 |
10 | 81 | Level_10| 2 |
15 | 101 | Level_15| 3 |
16 | 102 | Level_16| 3 |
17 | 102 | Level_16_bis| 3 |
18 | 103 | Level_17| 3 |
Any ideas ??
P.S: Sorry my bad english...
You can use a cumulative sum to define the groups. And then aggregation:
Select min(level) as level,
min(depth) keep (dense_rank first order by level) as depth,
min(descrip) keep (dense_rank first order by level) as descrip,
list_agg(level, ',') within group (order by level) as levels
from (select gl.*,
sum(case when abs(prev_depth - depth) > 15 and depth > 0 then 1 else 0 end) over (order by level) as grp
from (select gl.*, lag(depth) over (order by level asc) as prev_depth
from ground_levels
) gl
) gl
group grp;
This actually keeps the starting level in the list. It can be removed, but that requires a bit more work.

How do I dynamically make calculations via a CASE statement based on the results of the previous row's calculations in Oracle?

I'm trying to make calculations via CASE statements which rely on the results of calculations made on the previous row. The data I'm working with is hierarchical data. My end goal is to structure the resulting data to be in line with a Modified Preorder Tree Traversal algorithm.
Here's what my raw data looks like:
+-------+--------+
| id | parent |
+-------+--------+
| 1 | (null) |
+-------+--------+
| 600 | 1 |
+-------+--------+
| 690 | 600 |
+-------+--------+
| 6990 | 690 |
+-------+--------+
| 6900 | 690 |
+-------+--------+
| 69300 | 6900 |
+-------+--------+
| 69400 | 6900 |
+-------+--------+
Here's what I want the end result to look like. I'm happy to expand on why this is what I'm looking for, related to MPTT, etc.
+-------+-----------+-----+------+--+--+--+--+
| id | parent_id | lft | rght | | | | |
+-------+-----------+-----+------+--+--+--+--+
| 1 | | 1 | 14 | | | | |
+-------+-----------+-----+------+--+--+--+--+
| 600 | 1 | 2 | 13 | | | | |
+-------+-----------+-----+------+--+--+--+--+
| 690 | 600 | 3 | 12 | | | | |
+-------+-----------+-----+------+--+--+--+--+
| 6900 | 690 | 4 | 9 | | | | |
+-------+-----------+-----+------+--+--+--+--+
| 6990 | 690 | 10 | 11 | | | | |
+-------+-----------+-----+------+--+--+--+--+
| 69300 | 6900 | 5 | 6 | | | | |
+-------+-----------+-----+------+--+--+--+--+
| 69400 | 6900 | 7 | 8 | | | | |
+-------+-----------+-----+------+--+--+--+--+
Here's what my SQL code looks like so far. It calculates many of the fields that I think the algorithm that I describe below requires. This is "organization" data within an enterprise setting, which is why the orgn abbreviation is common in my code.
Here's the algorithm that I think will successfully transform it into the MPTT format:
-If level is root (lvl=1), lft = 1, rght = subnodes*2 + 2
-If level is the next level down (lvl = prev_lvl+1), and prev_parent != parent (meaning this is the first sibling)
-lft = parent_lft+1
-If lvl = prev_lvl, so we are on the same level (don’t know if this is a true sibling of the same parent yet)
-if parent = prev_parent, lft=prev_rght+1 (true sibling, just use previous sibling’s right + 1)
-if parent != prev_parent, lft=parent_lft+1 (same level, not true sibling, so use parent’s left + 1)
-rght=(subnodes*2) + lft + 1
SQL Code I have so far:
WITH tab1 (
id,
parent_id
) AS (
SELECT
1,
NULL
FROM
dual
UNION ALL
SELECT
600,
1
FROM
dual
UNION ALL
SELECT
690,
600
FROM
dual
UNION ALL
SELECT
6990,
690
FROM
dual
UNION ALL
SELECT
6900,
690
FROM
dual
UNION ALL
SELECT
69300,
6900
FROM
dual
UNION ALL
SELECT
69400,
6900
FROM
dual
),t1 (
id,
parent_id,
lvl
) AS (
SELECT
id,
parent_id,
1 AS lvl
FROM
tab1
WHERE
parent_id IS NULL
UNION ALL
SELECT
t2.id,
t2.parent_id,
lvl + 1
FROM
tab1 t2,
t1
WHERE
t2.parent_id = t1.id
)
SEARCH BREADTH FIRST BY id SET order1,orgn_subnodes AS (
SELECT
id AS id,
COUNT(*) - 1 AS subnodes
FROM
(
SELECT
CONNECT_BY_ROOT ( t1.id ) AS id
FROM
t1
CONNECT BY
PRIOR t1.id = t1.parent_id
)
GROUP BY
id
),orgn_partial_data AS (
SELECT
orgn_subnodes.id AS id,
orgn_subnodes.subnodes,
parent_id,
lvl,
LAG(lvl,1) OVER(
ORDER BY
order1
) AS prev_lvl,
LAG(parent_id,1) OVER(
ORDER BY
order1
) AS prev_parent,
CASE
WHEN parent_id IS NULL THEN 1
END
lft,
CASE
WHEN parent_id IS NULL THEN ( subnodes * 2 ) + 2
END
rght,
order1
FROM
orgn_subnodes
JOIN t1 ON orgn_subnodes.id = t1.id
) SELECT
*
FROM
orgn_partial_data;
The result is:
+-------+----------+-----------+-----+----------+-------------+-----+------+--------+
| id | subnodes | parent_id | lvl | prev_lvl | prev_parent | lft | rght | order1 |
+-------+----------+-----------+-----+----------+-------------+-----+------+--------+
| 1 | 6 | | 1 | | | 1 | 14 | 1 |
+-------+----------+-----------+-----+----------+-------------+-----+------+--------+
| 600 | 5 | 1 | 2 | 1 | | | | 2 |
+-------+----------+-----------+-----+----------+-------------+-----+------+--------+
| 690 | 4 | 600 | 3 | 2 | 1 | | | 3 |
+-------+----------+-----------+-----+----------+-------------+-----+------+--------+
| 6900 | 2 | 690 | 4 | 3 | 600 | | | 4 |
+-------+----------+-----------+-----+----------+-------------+-----+------+--------+
| 6990 | 0 | 690 | 4 | 4 | 690 | | | 5 |
+-------+----------+-----------+-----+----------+-------------+-----+------+--------+
| 69300 | 0 | 6900 | 5 | 4 | 690 | | | 6 |
+-------+----------+-----------+-----+----------+-------------+-----+------+--------+
| 69400 | 0 | 6900 | 5 | 5 | 6900 | | | 7 |
+-------+----------+-----------+-----+----------+-------------+-----+------+--------+
I don't care about the ordering of "sibling nodes" within the tree. Also, if you don't find the SQL I've started on useful, you can post an answer that doesn't use any of it. I only posted to show what pieces of info I think I need to perform the steps of the algorithm.
I'll accept any Oracle code (database procedure, SELECT statement, etc) as an answer.
Please ask for more details if you need them!
I think there is a typo in starting post, it should be (7, 8) and not (4, 8) for 69400.
The canonical way to get the result is by using recursive procedure/function.
Below approach uses procedure and temporary table but you can achieve the same with function returning collection.
Temporary table
create global temporary table tmp$ (id int, l int, r int) on commit delete rows;
Package
create or replace package pkg as
procedure p(p_id in int);
end pkg;
/
sho err
Package body
create or replace package body pkg as
seq int;
procedure p_(p_id in int) as
begin
seq := seq + 1;
insert into tmp$(id, l, r) values (p_id, seq, null);
for i in (select id from tab1 where parent_id = p_id order by id) loop
p_(i.id);
end loop;
seq := seq + 1;
update tmp$ set r = seq where id = p_id;
end;
procedure p(p_id in int) as
begin
seq := 0;
p_(p_id);
end;
end pkg;
/
sho err
Test in SQL*PLus
SQL> exec pkg.p(1);
PL/SQL procedure successfully completed.
SQL> select * from tmp$;
ID L R
---------- ---------- ----------
1 1 14
600 2 13
690 3 12
6900 4 9
69300 5 6
69400 7 8
6990 10 11
7 rows selected.
Update
Standalone procedure without global variables
create or replace procedure p(p_id in int, seq in out int) as
begin
seq := seq + 1;
insert into tmp$(id, l, r) values (p_id, seq, null);
for i in (select id from tab1 where parent_id = p_id order by id) loop
p(i.id, seq);
end loop;
seq := seq + 1;
update tmp$ set r = seq where id = p_id;
end;
/
Test in SQL*PLus
SQL> var n number
SQL> exec :n := 0;
PL/SQL procedure successfully completed.
SQL> exec p(1, :n);
PL/SQL procedure successfully completed.
SQL> select * from tmp$;
ID L R
---------- ---------- ----------
1 1 14
600 2 13
690 3 12
6900 4 9
69300 5 6
69400 7 8
6990 10 11
7 rows selected.

Hierarchical table in Oracle

I have a problem with creating Hierarchical table, I searched a lot but this problem maybe different from the popular problem.
I have table that need to reconstruct
The input table :
| Agency_CODE | Code_length | Agency_Name
| 1 | 1 | Boogy
| 11 | 2 | Elhady
| 12 | 2 | EzzBatriq
| 13 | 2 | Haythomy
| 111 | 3 | Migz
| 121 | 3 | Mido
| 131 | 3 | Thabet
The hierarchy should be as : The agency which has only one digit is the root of the hierarchy 'Level 1', and level 2 the items which has two digits and level 3 which has 3 digits. Here we have 3 levels
So we need a query to get this output:
|Parent_ID |Parent_Name|Child_1_Id|Child_1_name|Child_2_Id|Child_2_name|
| 1 | Boogy | 11 | Elhady | 111 | Migz |
| 1 | Boogy | 12 | EzzBatriq | 121 | Mido |
| 1 | Boogy | 13 | Haythomy | 131 | Thabet |
Thanks in advance
SQL Fiddle
Oracle 11g R2 Schema Setup:
CREATE TABLE Agencies (
Agency_CODE NUMBER(8,0) CONSTRAINT Agencies__AC__PK PRIMARY KEY,
Code_length NUMBER(4,0) GENERATED ALWAYS AS ( LENGTH( Agency_Code ) ) VIRTUAL,
Agency_Name VARCHAR2(50),
parent_code NUMBER(7,0) GENERATED ALWAYS AS ( SUBSTR( Agency_Code, 1, LENGTH( Agency_Code ) - 1 ) ) VIRTUAL
CONSTRAINT Agencies__PC__FK REFERENCES Agencies( Agency_Code )
);
INSERT INTO Agencies ( Agency_CODE, Agency_Name )
SELECT 1, 'Boogy' FROM DUAL UNION ALL
SELECT 11, 'Elhady' FROM DUAL UNION ALL
SELECT 12, 'EzzBatriq' FROM DUAL UNION ALL
SELECT 13, 'Haythomy' FROM DUAL UNION ALL
SELECT 111, 'Migz' FROM DUAL UNION ALL
SELECT 121, 'Mido' FROM DUAL UNION ALL
SELECT 131, 'Thabet' FROM DUAL;
Query 1:
SELECT CONNECT_BY_ROOT( Agency_Code ) AS parent_id,
CONNECT_BY_ROOT( Agency_name ) AS parent,
PRIOR( Agency_Code ) AS child_id,
PRIOR( Agency_name ) AS child,
Agency_Code AS child2_id,
Agency_Name AS child2
FROM Agencies
WHERE LEVEL = 3
START WITH Code_length = 1
CONNECT BY PRIOR Agency_code = parent_code
Results:
| PARENT_ID | PARENT | CHILD_ID | CHILD | CHILD2_ID | CHILD2 |
|-----------|--------|----------|-----------|-----------|--------|
| 1 | Boogy | 11 | Elhady | 111 | Migz |
| 1 | Boogy | 12 | EzzBatriq | 121 | Mido |
| 1 | Boogy | 13 | Haythomy | 131 | Thabet |
Update: If you know the maximum depth of your nested hierarchy then you can use PIVOT:
SQL Fiddle
Oracle 11g R2 Schema Setup:
INSERT INTO Agencies ( Agency_CODE, Agency_Name )
SELECT 1311, 'Thabet.1' FROM DUAL;
Query 2:
SELECT *
FROM (
SELECT CONNECT_BY_ROOT( Agency_Code ) AS leaf_id,
Agency_Code,
code_length,
Agency_Name
FROM Agencies a
START WITH NOT EXISTS (
SELECT 1
FROM Agencies x
WHERE x.parent_code = a.agency_code
)
CONNECT BY PRIOR parent_code = Agency_code
) a
PIVOT(
MAX( agency_code ) AS id,
MAX( agency_name ) AS name
FOR code_length IN (
1 AS parent,
2 AS child,
3 AS child1,
4 AS child2
)
)
Results:
| LEAF_ID | PARENT_ID | PARENT_NAME | CHILD_ID | CHILD_NAME | CHILD1_ID | CHILD1_NAME | CHILD2_ID | CHILD2_NAME |
|---------|-----------|-------------|----------|------------|-----------|-------------|-----------|-------------|
| 121 | 1 | Boogy | 12 | EzzBatriq | 121 | Mido | (null) | (null) |
| 1311 | 1 | Boogy | 13 | Haythomy | 131 | Thabet | 1311 | Thabet.1 |
| 111 | 1 | Boogy | 11 | Elhady | 111 | Migz | (null) | (null) |

sql query oracle to fit a tree structure format

I have created these sample tables to create a json tree structure so that i can use jqtree to create a tree layout.
I want my json to be in the format
[
{"id":1, "parentid": 0, "name": "Carnivores"},
{"id":2, "parentid": 0, "name": "Herbivores"},
{"id":3, "parentid": 1, "name": "Dogs"},
{"id":4, "parentid": 3, "name": "Labradors"},
{"id":5, "parentid": 3, "name": "Pugs"},
{"id":6, "parentid": 3, "name": "Terriers"}
]
The tables are as follows.
| catg_id | catg_name |
| —————- |————————- |
| 1 | Carnivores |
| 2 | Herbivores |
| animal_catg_id | animal_catg_name | catg_id |
| —————- |————————- |————————- |
| 1 | Dogs | 1 |
| 2 | Cats | 1 |
| 3 | Cows | 2 |
| 4 | Buffalo | 2 |
| animal_id | animal_name | animal_catg_id |
| —————- |————————- | ————————- |
| 1 | labs | 1 |
| 2 | pugs | 1 |
| 3 | terriers | 1 |
| 4 | german | 1 |
| 5 | lion | 2 |
| 6 | tiger | 2 |
I am assuming it would be hierarchical query, i have never written one before, i need some help with that.
I don't know where to start and how to start it.
EDIT
One of the comments in the answers is that the schema design is not clear.
What changes should I do to make to get the data in the json format, so that it maintains the hierarchy
EDIT2
My current query returns this table
Carnivores | Dogs | labs
Carnivores | Dogs | pugs
Carnivores | Dogs | terriers
.......
The JSON you are proposing appears to have no correlation between the IDs you are assigning and the IDs in the tables this will make it difficult to connect anything from the client-side back to the database.
You would be better re-organising your tables so that you can put everything into a single hierarchical structure. Something like a Linnaean Taxonomy:
SQL Fiddle
Oracle 11g R2 Schema Setup:
CREATE TABLE Taxonomies ( ID, PARENT_ID, Category, Taxonomy, Common_Name ) AS
SELECT 1, CAST(NULL AS NUMBER), 'Kingdom', 'Animalia', 'Animal' FROM DUAL
UNION ALL SELECT 2, 1, 'Phylum', 'Chordata', 'Chordate' FROM DUAL
UNION ALL SELECT 3, 2, 'Class', 'Mammalia', 'Mammal' FROM DUAL
UNION ALL SELECT 4, 3, 'Order', 'Carnivora', 'Carnivore' FROM DUAL
UNION ALL SELECT 5, 4, 'Family', 'Felidae', 'Feline' FROM DUAL
UNION ALL SELECT 6, 5, 'Genus', 'Panthera', 'Tiger' FROM DUAL
UNION ALL SELECT 7, 5, 'Genus', 'Felis', 'Cat' FROM DUAL
UNION ALL SELECT 8, 5, 'Genus', 'Lynx', 'Lynx' FROM DUAL
UNION ALL SELECT 9, 4, 'Family', 'Canidae', 'Canid' FROM DUAL
UNION ALL SELECT 10, 9, 'Genus', 'Canis', 'Canine' FROM DUAL
UNION ALL SELECT 11, 10, 'Species', 'Canis Lupus', 'Gray Wolf' FROM DUAL
UNION ALL SELECT 12, 11, 'Sub-Species', 'Canis Lupus Familiaris', 'Domestic Dog' FROM DUAL
UNION ALL SELECT 13, 12, 'Breed', NULL, 'Pug' FROM DUAL
UNION ALL SELECT 14, 12, 'Breed', NULL, 'German Shepherd' FROM DUAL
UNION ALL SELECT 15, 12, 'Breed', NULL, 'Labradors' FROM DUAL
UNION ALL SELECT 16, 7, 'Species', 'Felis Catus', 'Domestic Cat' FROM DUAL
UNION ALL SELECT 17, 8, 'Species', 'Lynx Lynx', 'Eurasian Lynx' FROM DUAL
UNION ALL SELECT 18, 8, 'Species', 'Lynx Rufus', 'Bobcat' FROM DUAL;
Then you can extract the data relatively simply:
Query 1 - Get everything taxonomically related to "Cat":
SELECT *
FROM (
SELECT *
FROM Taxonomies
START WITH Common_Name = 'Cat'
CONNECT BY PRIOR PARENT_ID = ID
ORDER BY LEVEL DESC
)
UNION
SELECT *
FROM (
SELECT *
FROM Taxonomies
START WITH Common_Name = 'Cat'
CONNECT BY PRIOR ID = PARENT_ID
ORDER SIBLINGS BY Common_Name
)
Results:
| ID | PARENT_ID | CATEGORY | TAXONOMY | COMMON_NAME |
|----|-----------|----------|-------------|--------------|
| 1 | (null) | Kingdom | Animalia | Animal |
| 2 | 1 | Phylum | Chordata | Chordate |
| 3 | 2 | Class | Mammalia | Mammal |
| 4 | 3 | Order | Carnivora | Carnivore |
| 5 | 4 | Family | Felidae | Feline |
| 7 | 5 | Genus | Felis | Cat |
| 16 | 7 | Species | Felis Catus | Domestic Cat |
Query 2 - Get everything taxonomically related to "Canine":
SELECT *
FROM (
SELECT *
FROM Taxonomies
START WITH Common_Name = 'Canine'
CONNECT BY PRIOR PARENT_ID = ID
ORDER BY LEVEL DESC
)
UNION
SELECT *
FROM (
SELECT *
FROM Taxonomies
START WITH Common_Name = 'Canine'
CONNECT BY PRIOR ID = PARENT_ID
ORDER SIBLINGS BY Common_Name
)
Results:
| ID | PARENT_ID | CATEGORY | TAXONOMY | COMMON_NAME |
|----|-----------|-------------|------------------------|-----------------|
| 1 | (null) | Kingdom | Animalia | Animal |
| 2 | 1 | Phylum | Chordata | Chordate |
| 3 | 2 | Class | Mammalia | Mammal |
| 4 | 3 | Order | Carnivora | Carnivore |
| 9 | 4 | Family | Canidae | Canid |
| 10 | 9 | Genus | Canis | Canine |
| 11 | 10 | Species | Canis Lupus | Gray Wolf |
| 12 | 11 | Sub-Species | Canis Lupus Familiaris | Domestic Dog |
| 13 | 12 | Breed | (null) | Pug |
| 14 | 12 | Breed | (null) | German Shepherd |
| 15 | 12 | Breed | (null) | Labradors |
Here's an example of a hierarchical query from the Oracle documentation:
SELECT last_name, employee_id, manager_id, LEVEL
FROM employees
START WITH employee_id = 100
CONNECT BY PRIOR employee_id = manager_id
ORDER SIBLINGS BY last_name;
http://docs.oracle.com/cd/B19306_01/server.102/b14200/queries003.htm
Something like this in your case but your schema design isn't clear
SELECT animal_name, level
FROM animals
START WITH parentid is null
CONNECT BY PRIOR id = parentid;