Output records that match the condition, as well as their parent records up to the root record. Oracle SQL - sql

There is such an Oracle table:
+----+-----+--------+
| ID | PID | NAME |
+----+-----+--------+
| 1 | | testX |
| 2 | | test2 |
| 3 | 2 | test3 |
| 4 | 3 | testX |
| 5 | 3 | test5 |
| 6 | 3 | test6 |
| 7 | 4 | test7 |
| 8 | 5 | test8 |
| 9 | 3 | test9 |
| 10 | 4 | test10 |
| 11 | 5 | testX |
| 12 | 5 | test12 |
+----+-----+--------+
, where pid is the id of the parent record.
Need to output all records that match the condition, as well as their parent records up to the root record.
Such parent records should not be duplicated with those parent records that are found during the search phase.
For example, under this condition where name = 'testX', should get this result:
+----+-----+-------+
| ID | PID | NAME |
+----+-----+-------+
| 1 | | testX |
| 2 | | test2 |
| 3 | 2 | test3 |
| 4 | 3 | testX |
| 5 | 3 | test5 |
| 11 | 5 | testX |
+----+-----+-------+
How to do it?
P.S. Oracle 11.2.0.4.0.

I'm sure there is a more elegant way to do this, but this is what I came up with.
This is the with clause to generate the sample data:
with testdata as
(select 1 ID, null PID, 'testX' NAME from dual union all
select 2 , null, 'test2' from dual union all
select 3 , 2, 'test3' from dual union all
select 4 , 3, 'testX' from dual union all
select 5 , 3, 'test5' from dual union all
select 6 , 3, 'test6' from dual union all
select 7 , 4, 'test7' from dual union all
select 8 , 5, 'test8' from dual union all
select 9 , 3, 'test9' from dual union all
select 10, 4, 'test10' from dual union all
select 11, 5, 'testX' from dual union all
select 12, 5, 'test12' from dual)
Here is the query:
select distinct id, pid, name
from(
select sys_connect_by_path(name,'/') path,
id, pid, name
from testdata
connect by prior PID = ID)
where instr(path,'/testX') > 0
order by id
I used SYS_CONNECT_BY_PATH in order to get the name field from all parents. Then I just checked that testX was one of the elements in the string using instr.
My results are:
ID PID NAME
1 testX
2 test2
3 2 test3
4 3 testX
5 3 test5
11 5 testX

This simple query should help -
SELECT distinct id, pid, name FROM tab1
connect by prior pid = id
start with name = 'testX'
order by id
;
http://sqlfiddle.com/#!4/8da121/6/0
Output -
ID PID NAME
1 (null) testX
2 (null) test2
3 2 test3
4 3 testX
5 3 test5
11 5 testX

Related

Oracle Aggregate(SUM function) after self-join

I have a table which contains ID, PARENT_ID AND COUNT.
EX)
+-----+-----------------------+--------------------------+
| ID | PARENT_ID | COUNT |...
+-----+-----------------------+--------------------------+
| 1 | NULL | 40 |...
| 2 | 1 | 10 |...
| 3 | 1 | 20 |...
| 4 | NULL | 35 |...
+-----+-----------------------+--------------------------+
And, i want result the sum of parent and sibling's count.
ID 1's count = ID 1's count + ID 2's count + ID 3's count
RESULT)
+-----+-----------------------+--------------------------+
| ID | PARENT_ID | COUNT |...
+-----+-----------------------+--------------------------+
| 1 | NULL | 70 |...
| 2 | 1 | 10 |...
| 3 | 1 | 20 |...
| 4 | NULL | 35 |...
+-----+-----------------------+--------------------------+
I used connect by to get the desired result, but I want to change the method as the above method uses too much oracle cpu.
Is there any way I can do this using sum function?
You can use the self join as follows:
SQL> with dataa (ID, PARENT_ID, CNT) as
2 (SELECT 1 , NULL, 40 FROM DUAL UNION ALL
3 SELECT 2 , 1 , 10 FROM DUAL UNION ALL
4 SELECT 3 , 1 , 20 FROM DUAL UNION ALL
5 SELECT 4 , NULL, 35 FROM DUAL)
6 -- your query starts from here
7 SELECT D1.ID, D1.PARENT_ID, D1.CNT + COALESCE(SUM(D2.CNT),0)
8 FROM DATAA D1 LEFT JOIN DATAA D2
9 ON D1.ID = D2.PARENT_ID
10 GROUP BY D1.ID, D1.PARENT_ID, D1.CNT
11 ORDER BY D1.ID;
ID PARENT_ID D1.CNT+COALESCE(SUM(D2.CNT),0)
---------- ---------- ------------------------------
1 70
2 1 10
3 1 20
4 35
SQL>

Oracle : SQL Request with a Group By and a Percentage on two differents tables

I'm currently blocked on an complex request... (with a join) :
I have this table "DATA":
order | product
----------------
1 | A
1 | B
2 | A
2 | D
3 | A
3 | C
4 | A
4 | B
5 | Y
5 | Z
6 | W
6 | A
And this table "DICO":
order | couple | first | second
-------------------------------
1 | A-B | A | B
2 | A-D | A | D
3 | A-C | A | C
4 | A-B | A | B
5 | Y-Z | Y | Z
6 | W-A | W | A
I would like to obtain, on one line :
order | count | total1stElem | %1stElem | total2ndElem | %1ndElem
------------------------------------------------------------------
A-B | 2 | 5 | 40% | 2 | 100%
A-D | 1 | 5 | 20% | 1 | 100%
A-C | 1 | 5 | 20% | 1 | 100%
Y-Z | 1 | 1 | 100% | 1 | 100%
W-A | 1 | 1 | 100% | 5 | 20%
I'm totally blocked on the jointure part of my request. Somebody can help me ?
Without any joins - just using UNPIVOT and PIVOT:
Oracle Setup:
CREATE TABLE DICO ( "order", couple, first, second ) AS
SELECT 1, 'A-B', 'A', 'B' FROM DUAL UNION ALL
SELECT 2, 'A-D', 'A', 'D' FROM DUAL UNION ALL
SELECT 3, 'A-C', 'A', 'C' FROM DUAL UNION ALL
SELECT 4, 'A-B', 'A', 'B' FROM DUAL UNION ALL
SELECT 5, 'Y-Z', 'Y', 'Z' FROM DUAL UNION ALL
SELECT 6, 'W-A', 'W', 'A' FROM DUAL;
Query:
SELECT "order",
"count",
"1stElem_TOTAL" AS Total1stElem,
100*"count"/"1stElem_TOTAL" AS "%1stElem",
"2ndElem_TOTAL" AS Total2ndElem,
100*"count"/"2ndElem_TOTAL" AS "%2ndElem"
FROM (
SELECT couple AS "order",
key,
COUNT(*) OVER ( PARTITION BY COUPLE )/2 AS "count",
COUNT(*) OVER ( PARTITION BY VALUE ) AS num_value
FROM DICO
UNPIVOT ( Value FOR Key IN ( first AS 1, second AS 2 ) )
)
PIVOT ( MAX( NUM_VALUE ) AS Total FOR key IN ( 1 AS "1stElem", 2 AS "2ndElem" ) );
Results:
order count TOTAL1STELEM %1stElem TOTAL2NDELEM %2ndElem
----- ----- ------------ -------- ------------ --------
A-D 1 5 20 1 100
A-B 2 5 40 2 100
A-C 1 5 20 1 100
Y-Z 1 1 100 1 100
W-A 1 1 100 5 20

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;

oracle sql field from cross reference table

i am novice at SQL and have problems hope you can help me :
ORACLE 10g
table ACCOUNT
+----------+----------+
| ACCOUNTID| LBKEY |
+----------+----------+
| ... | ... |
| 254 | value254 |
| ... | ... |
| 401 | value401 |
| ... | ... |
| 405 | value405 |
+----------+----------+
cross reference table
+----------+----------+----------+--------+
| IDTABLE2 | ACCOUNTID| OIDID | VALUE |
+----------+----------+----------+--------+
| ... | ... | ... | ... |
| 475 | 401 | 4 | 40000 |
| 476 | 405 | 4 | 35000 |
| ...| ... | ... | ... |
| 3000 | 254 | 5 | PARIS |
| 3001 | 401 | 5 | LONDON |
| 3002 | 405 | 5 | SYDNEY |
| ...| ... | ... | ... |
+----------+----------+----------+--------+
table OID
+----------+-------------+-------------+
| OIDID | OID | DESCRIPTION |
+----------+-------------+-------------+
| 1 | x | x |
| 2 | x | x |
| 3 | x | x |
| 4 | 1.3.6.1.4.1 | Post Code |
| 5 | 1.3.6.1.4.2 | City |
| 6 | x | x |
| 7 | x | x |
| 8 | x | x |
| 9 | x | x |
| 10 | x | x |
+----------+-------------+-------------+
Expected result
Constraint : all the ACCOUNT (LBKEY) who has one postal code(OID 4) or city code(OID 5) in the cross reference table
+----------+-------------+-------------+
| LBKEY | POST CODE | CITY |
+----------+-------------+-------------+
| value254 | null | PARIS |
| value401 | 40000 | LONDON |
| value405 | 35000 | SYDNEY |
+----------+-------------+-------------+
Three different ways of doing it:
SQL Fiddle
Oracle 11g R2 Schema Setup:
CREATE TABLE ACCOUNT ( ACCOUNTID, LBKEY ) AS
SELECT 254, 'value254' FROM DUAL
UNION ALL SELECT 401, 'value401' FROM DUAL
UNION ALL SELECT 405, 'value405' FROM DUAL
UNION ALL SELECT 406, 'value406' FROM DUAL;
CREATE TABLE CrossReference ( IDTABLE2, ACCOUNTID, OIDID, VALUE ) AS
SELECT 475, 401, 4, '40000' FROM DUAL
UNION ALL SELECT 476, 405, 4, '35000' FROM DUAL
UNION ALL SELECT 3000, 254, 5, 'PARIS' FROM DUAL
UNION ALL SELECT 3001, 401, 5, 'LONDON' FROM DUAL
UNION ALL SELECT 3002, 405, 5, 'SYDNEY' FROM DUAL
UNION ALL SELECT 4000, 406, 6, 'x' FROM DUAL;
CREATE TABLE OID (OIDID, OID, DESCRIPTION ) AS
SELECT 1, 'x', 'x' FROM DUAL
UNION ALL SELECT 2, 'x', 'x' FROM DUAL
UNION ALL SELECT 3, 'x', 'x' FROM DUAL
UNION ALL SELECT 4, '1.3.6.1.4.1', 'Post Code' FROM DUAL
UNION ALL SELECT 5, '1.3.6.1.4.2', 'City' FROM DUAL
UNION ALL SELECT 6, 'x', 'x' FROM DUAL
UNION ALL SELECT 7, 'x', 'x' FROM DUAL
UNION ALL SELECT 8, 'x', 'x' FROM DUAL
UNION ALL SELECT 9, 'x', 'x' FROM DUAL
UNION ALL SELECT 10, 'x', 'x' FROM DUAL;
Query 1:
SELECT LBKEY,
MAX( CASE OIDID WHEN 4 THEN VALUE END ) AS "Post Code",
MAX( CASE OIDID WHEN 5 THEN VALUE END ) AS "City"
FROM ACCOUNT a
INNER JOIN
CrossReference c
ON ( a.ACCOUNTID = c.ACCOUNTID )
WHERE c.OIDID IN ( 4, 5 )
GROUP BY LBKEY
Results:
| LBKEY | POST CODE | CITY |
|----------|-----------|--------|
| value254 | (null) | PARIS |
| value405 | 35000 | SYDNEY |
| value401 | 40000 | LONDON |
Query 2:
WITH data AS (
SELECT LBKEY,
( SELECT VALUE
FROM CrossReference c
WHERE c.ACCOUNTID = a.ACCOUNTID
AND c.OIDID = 4 ) AS "Post Code",
( SELECT VALUE
FROM CrossReference c
WHERE c.ACCOUNTID = a.ACCOUNTID
AND c.OIDID = 5 ) AS "City"
FROM ACCOUNT a
)
SELECT *
FROM data
WHERE "Post Code" IS NOT NULL
OR "City" IS NOT NULL
Results:
| LBKEY | POST CODE | CITY |
|----------|-----------|--------|
| value254 | (null) | PARIS |
| value401 | 40000 | LONDON |
| value405 | 35000 | SYDNEY |
Query 3:
SELECT LBKEY,
c1.VALUE AS "Post Code",
c2.VALUE AS City
FROM ACCOUNT a
LEFT OUTER JOIN
( SELECT ACCOUNTID, VALUE FROM CrossReference WHERE OIDID = 4 ) c1
ON ( c1.ACCOUNTID = a.ACCOUNTID )
LEFT OUTER JOIN
( SELECT ACCOUNTID, VALUE FROM CrossReference WHERE OIDID = 5 ) c2
ON ( c2.ACCOUNTID = a.ACCOUNTID )
WHERE c1.VALUE IS NOT NULL
OR c2.VALUE IS NOT NULL
Results:
| LBKEY | POST CODE | CITY |
|----------|-----------|--------|
| value254 | (null) | PARIS |
| value401 | 40000 | LONDON |
| value405 | 35000 | SYDNEY |
I think this could work for you:
select
lbkey,
cross_post.value as postcode,
cross_city.value as city
from
ACCOUNT a,
cross cross_city,
cross cross_post,
where
a.accountid=cross_city.accountid(+) and
a.accountid=cross_post.accountid(+) and
nvl(cross_city.oidid,5)=5 and
nvl(cross_post.oidid,4)=4 and
(cross_city.oidid is not null or cross_post.oidid is not null)

select the most recent in all groups of with the same value in one column

The question isn't very clear, but I'll illustrate what I mean, suppose my table is like such:
item_name | date added | val1 | val2
------------------------------------
1 | date+1 | 10 | 20
1 | date | 12 | 21
2 | date+1 | 5 | 6
3 | date+3 | 3 | 1
3 | date+2 | 5 | 2
3 | date | 3 | 1
And I want to select row 1, 3, 4 as they are the most recent entries for each item
Try this:
select *
from tableX t1
where t1.date_added = (select max(t2.date_added)
from tableX t2
where t2.item_name = t1.item_name )