Select tree by leaf in Oracle Database - oracle

There is hierarchical queries in Oracle, using CONNECT BY PRIOR. Everybody knows, how to select children by parent, but I need to select parent by child.
Here is my table:
ID PID NAME TYPE
1 null EARTH PLANET
2 1 USA COUNTRY
3 2 CALIFORNIA STATE
4 3 Los_Angeles CITY
5 3 San_Francisco CITY
6 3 San_Diego CITY
In my app I have ID of San_Diego, and I need to know, in what country San_Diego is? I need to get USA (TYPE=COUNTRY) with my query? How to select it with oracle hierarchical structures?

Hierarchy means, that one row of a table is a "parent", and another one - a "child". PRIOR is used to show who is who. Clause CONNECT BY PRIOR EMPNO = MGR means, that if two rows have the same value, but one row in a column EMPNO, and the second - in a column MGR, then second is a "parent" and first is a "child". So, query
SELECT EMPNO,ENAME,MGR,LEVEL
FROM TMP_PCH
CONNECT BY PRIOR EMPNO = MGR
START WITH MGR = 'John'
returns all subordinates of John (and John itself), and query
SELECT EMPNO,ENAME,MGR,LEVEL
FROM TMP_PCH
CONNECT BY PRIOR MGR = EMPNO
START WITH MGR = 'John'
returns all bosses of John (and John itself).

You need to create correct condition for connect by and start with leaf 'San Diego':
select name
from (
select * from test
connect by id = prior pid
start with name='San_Diego')
where type='COUNTRY'
SQL Fiddle demo

SELECT * FROM places_table
WHERE type = 'COUNTRY'
START WITH id = 6 -- San Diego
CONNECT BY PRIOR pid = id;

There's no difference, you just have to use PRIOR in the parent side. For example to get the tree for the city of San Diego:
WITH your_table AS (
SELECT 1 id, NULL pid, 'EARTH' name, 'PLANET' type FROM dual
UNION
SELECT 2 id, 1 pid, 'USA' name, 'COUNTRY' type FROM dual
UNION
SELECT 3 id, 2 pid, 'CALIFORNIA' name, 'STATE' type FROM dual
UNION
SELECT 4 id, 3 pid, 'Los_Angeles' name, 'CITY' type FROM dual
UNION
SELECT 5 id, 3 pid, 'San_Francisco' name, 'CITY' type FROM dual
UNION
SELECT 6 id, 3 pid, 'San_Diego' name, 'CITY' type FROM dual
)
SELECT id, name, level
FROM your_table
CONNECT BY id = PRIOR pid
START WITH id = 6
If you just wanted to get the country, you could filter by level:
WITH your_table AS (
SELECT 1 id, NULL pid, 'EARTH' name, 'PLANET' type FROM dual
UNION
SELECT 2 id, 1 pid, 'USA' name, 'COUNTRY' type FROM dual
UNION
SELECT 3 id, 2 pid, 'CALIFORNIA' name, 'STATE' type FROM dual
UNION
SELECT 4 id, 3 pid, 'Los_Angeles' name, 'CITY' type FROM dual
UNION
SELECT 5 id, 3 pid, 'San_Francisco' name, 'CITY' type FROM dual
UNION
SELECT 6 id, 3 pid, 'San_Diego' name, 'CITY' type FROM dual
)
SELECT id, name, level
FROM your_table
WHERE LEVEL = 3
CONNECT BY id = PRIOR pid
START WITH id = 6

Related

Oracle Select statement without using listagg

DB : Oracle 11G
Below is a query subset result in which I want to fetch only ID =1, also i want to display if this value is there in the result
NAME = 'S123', if its there return the output as an indicator in the query result
ID, NAME,....(other columns)
1, 'I123',...
3, 'S123',...
4, 'W123',...
6, 'C123',...
....,
....,
eg., output
ID, NAME , value_present_ind('S123')
1, 'I123', 'Y'
without using sub-queries on the select statement
without using listagg
WITH demo_data AS ( SELECT 1 AS ID, 'I123' AS NAME FROM DUAL UNION ALL
SELECT 3 AS ID, 'S123' AS NAME FROM DUAL UNION ALL
SELECT 4 AS ID, 'W123' AS NAME FROM DUAL UNION ALL
SELECT 5 AS ID, 'C123' AS NAME FROM DUAL )
SELECT MAX(CASE WHEN id = 1 THEN id ELSE 0 END) AS id,
MAX(CASE WHEN id = 1 THEN name ELSE NULL END) AS name,
MAX(CASE WHEN name = 'I123' THEN 'Y' ELSE 'N' END) AS value_present_ind
FROM demo_data;

Only join two columns when it is not null in one table oracle

I have a table a
ID | Name | City
1 |Jack | Null
2 |Tom | Null
And table b
ID | Name | City
1 |Jack | Dever
2 |Tom | Dallas
I need to write a query to join these two tables by id, name and city if they are not null in table a. But any of these three column could be null for each row.
I wrote one below but the performance is bad when data grows
Select * from a, b
Where (a.id is not null and a.id=b.id or a.id is null) and
(a.name is not null and a.name=b.name or a.name is null) and
(a.city is not null and a.city=b.city or a.city is null)
Basically, I need to join on the column when it is not null in table a.
Could you shed some light on this?
Thanks a lot!
ATTEMPT I:
Would this be what you need? It seems to do what I can read out from your question.
with a as (select 1 id, 'Jack' name, null city from dual
union all
select 2 id, 'Tom' name, null city from dual
union all
select 3 id, 'Mike' name, 'Miami' city from dual)
,b as (select 1 id, 'Jack' name, 'Dever' city from dual
union all
select 2 id, 'Tom' name, 'Dallas' city from dual
union all
select 3 id, 'Mike' name, 'Boise' city from dual)
select b.*
from b
left outer join a
on a.id = b.id
and a.name = b.name
where b.city = nvl(a.city, b.city);
If not, please advisa as to what would need to change in the result or possibly in the indata.
UPDATE I:
To allow all columns to have the possibility of being null, this could be one way of doing it. I've added testdata for the conditions I think your are describing. It gives the result I think you are looking for.
with a as (select 1 id, 'Jack' name, null city from dual
union all
select 2 id, 'Tom' name, null city from dual
union all
select 3 id, 'Mike' name, 'Miami' city from dual
union all
select 4 id, 'Don' name, null city from dual
union all
select 5 id, null name, 'London' city from dual
union all
select null id, 'Erin' name, 'Berlin' city from dual
)
,b as (select 1 id, 'Jack' name, 'Dever' city from dual
union all
select 2 id, 'Tom' name, 'Dallas' city from dual
union all
select 3 id, 'Mike' name, 'Boise' city from dual
union all
select 4 id, 'Don' name, 'Dover' city from dual
union all
select 5 id, 'Lis' name, 'London' city from dual
union all
select 6 id, 'Erin' name, 'Berlin' city from dual
)
select b.*, a.*
from b
inner join a
on b.id = nvl(a.id, b.id)
and b.name = nvl(a.name, b.name)
and b.city = nvl(a.city, b.city)
order by 1;

How do i retireve individual out put records for duplicate inputs in sql?

I have a set of inputs out of which some may be duplicated. In the result too i need the output to be duplicated. i.e. each input should have an output even if the input is duplicated.
The query I'm trying to execute is:
select imsi
from subscriber
where business_id in ('4162000324','4162000324','4162000324','4162000321','4162000321') ;
My current output is :
imsi
302610914502037
302610913095231
But i want the output to be of 5 rows in which first 3 rows give the same imsi for the first business_id(4162000324) and last 2 rows give the same imsi for the 2nd business_id (4162000321)
Use PHP
array_unique
Try This
$business_array = array('4162000324','4162000324','4162000324','4162000321','4162000321');
$business = array_unique($business_array);
$business = implode("','",$business);
echo "select imsi
from subscriber
where business_id in ('".$business."') ;";
OUTPUT
select imsi from subscriber where business_id in ('4162000324','4162000321') ;
You can do this using a join:
select s.imsi
from subscriber s join
(select '4162000324' as id from dual union all
select '4162000324' from dual union all
select '4162000324' from dual union all
select '4162000321' from dual union all
select '4162000321' from dual
) ids
on s.business_id = ids.id;
If you want these in the same order, then add an additional column for the ordering:
select s.imsi
from subscriber s join
(select '4162000324' as id, 1 as n from dual union all
select '4162000324', 2 from dual union all
select '4162000324', 3 from dual union all
select '4162000321', 4 from dual union all
select '4162000321', 5
) ids
on s.business_id = ids.id
order by ids.n;

Table Variable Style Entities in Oracle

I've been looking around for awhile for something in Oracle that acts like a table variable in SQL Server. I've found people asking questions like this here on SO and people always say "Yes, Oracle has that" but the examples show that the entities are not like SQL Server at all. Can someone show me how to perform the below simple TSQL solution in Oracle?
declare #users table (
ID int,
Name varchar(50),
Age int,
Gender char(1)
)
;with users as (
select 1001 as ID, 'Bob' as Name, 25 as Age, 'M' as Gender
union
select 1021 as ID, 'Sam' as Name, 29 as Age, 'F'
)
insert into #users (ID, Name, Age, Gender)
select * from users
declare #grades table (
UserID int,
ClassID int,
Grade int
)
;with grades as (
select 1001 as UserID , 120 as ClassID, 4 as Grade
Union
select 1001 as UserID , 220 as ClassID, 2 as Grade
Union
select 1021 as UserID , 130 as ClassID, 4 as Grade
Union
select 1021 as UserID , 230 as ClassID, 4 as Grade
Union
select 1021 as UserID , 340 as ClassID, 2 as Grade
)
insert into #grades
select * from grades
select u.ID, u.Name, GPA = AVG(cast(g.grade as decimal))
from #users u
inner join #grades g on u.ID=g.UserID
group by u.ID, u.Name
Some answers may tell you that Oracle has table variables, and it does to a certain extent. However, most answers will tell you that you should not be doing this in Oracle at all; there's simply no need.
In your case I would simply use a CTE:
with users as (
select 1001 as ID, 'Bob' as Name, 25 as Age, 'M' as Gender from dual
union
select 1021 as ID, 'Sam' as Name, 29 as Age, 'F' from dual
)
, grades as (
select 1001 as UserID , 120 as ClassID, 4 as Grade from dual
Union
select 1001 as UserID , 220 as ClassID, 2 as Grade from dual
Union
select 1021 as UserID , 130 as ClassID, 4 as Grade from dual
Union
select 1021 as UserID , 230 as ClassID, 4 as Grade from dual
Union
select 1021 as UserID , 340 as ClassID, 2 as Grade from dual
)
select u.ID, u.Name, AVG(g.grade) as gpa
from users u
join grades g on u.ID = g.UserID
group by u.ID, u.Name
UPDATE: The answer I've been trying to get for a long time is in Ben's comment below which I include here:
"There is no variable, which you can create on the fly and join to other tables in standard SQL #wcm, yes. There is a number of different type of objects that can be created that will allow you to do this, but not exactly as you would in T-SQL".
If I understand correctly, then if I needed temporary storage of data that is confined to being visible to my session, then I'd be using a global temporary table. Probably more overhead than storing in memory, but plenty of advantages too -- gathering statistics on them, indexing them, and the ability to store data without regard to memory consumption.

Column Row Transpose in Oracle Sql

Hi I have a simple query which give this result
And I want to modify it as follows. the Name column becomes the column headers and the studentID column becomes the 1st row
WITH t AS
(SELECT 1001 studentid, 'john' NAME FROM dual
UNION ALL
SELECT 1002, 'kane' FROM dual
)
SELECT * FROM (
SELECT studentid, NAME FROM t)
pivot (max(studentid) for name in ('john' John, 'kane' Kane));

Resources