Hierarchical table in Oracle - sql

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) |

Related

Bigquery avoid null data and merge rows

In Google Bigquery, i'm having data sets with data dispersed between int_value and double value, how can i merge the
-------------------------------------------------------------------------
|user_id | params.string_value | params.int_value | params.double_value |
-------------------------------------------------------------------------
| 12 | null | null | 121 |
| 12 | Tom | null | null |
| 12 | null | null | 141 |
| 12 | Kim | null | null |
| 13 | null | null | 961 |
| 13 | Jack | null | null |
| 14 | null | null | 31 |
| 14 | Jerry | null | null |
-------------------------------------------------------------------------
Result needed
-------------------------------------------------------------------------
|user_id | params.string_value | params.int_value | params.double_value |
-------------------------------------------------------------------------
| 12 | Tom | null | 121 |
| 12 | Kim | null | 141 |
| 13 | Jack | null | 961 |
| 14 | Jerry | null | 31 |
-------------------------------------------------------------------------
There can be multiple data for same user_id but with different params.string_value | params.int_value | params.double_value
I want to merge all the data which has same user_id in Big Query
Below is for BigQuery Standard SQL
#standardSQL
SELECT user_id, STRUCT(string_value, int_value, double_value) params
FROM (
SELECT user_id,
ARRAY_AGG(params.string_value IGNORE NULLS) string_values,
ARRAY_AGG(params.int_value IGNORE NULLS) int_values,
ARRAY_AGG(params.double_value IGNORE NULLS) double_values
FROM `project.dataset.table`
GROUP BY user_id
)
LEFT JOIN UNNEST(string_values) string_value WITH OFFSET
LEFT JOIN UNNEST(int_values) int_value WITH OFFSET USING(OFFSET)
LEFT JOIN UNNEST(double_values) double_value WITH OFFSET USING(OFFSET)
If to apply to sample data from your question
WITH `project.dataset.table` AS (
SELECT 12 user_id, STRUCT<string_value STRING, int_value INT64, double_value FLOAT64>(NULL, NULL, 121) AS params UNION ALL
SELECT 12, STRUCT('Tom', NULL, NULL) UNION ALL
SELECT 12, STRUCT(NULL, NULL, 141) UNION ALL
SELECT 12, STRUCT('Kim', NULL, NULL) UNION ALL
SELECT 13, STRUCT(NULL, NULL, 961) UNION ALL
SELECT 13, STRUCT('Jack', NULL, NULL) UNION ALL
SELECT 14, STRUCT(NULL, NULL, 31) UNION ALL
SELECT 14, STRUCT('Jerry', NULL, NULL)
)
result is
Row user_id params.string_value params.int_value params.double_value
1 12 Tom null 121.0
2 12 Kim null 141.0
3 13 Jack null 961.0
4 14 Jerry null 31.0
You can use the MAX function :
SELECT user_id,
MAX(params.string_value) as string_value,
MAX(params.int_value) as int_value,
MAX(params.double_value) as double_value
FROM your_dataset.your_table
GROUP BY user_id
MAX do not consider NULL values. Neither do MIN so you can also use this one !

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.

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;

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

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 |

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)