CONNECT BY for two tables with two JOINS - oracle

I have 3 tables:
two with hierarchical structures
(like "dimensions" of recursive type of hierarchy);
one with summing data (like "facts" with X column).
They are here:
DIM1 (ID1, PARENT2, NAME1)
DIM2 (ID2, PARENT2, NAME2)
FACTS (ID1, ID2, X)
Example of DIM1 table:
-- 1 0 DIM1
---- 2 1 DIM1-A
------ 3 2 DIM1-A-A
-------- 4 3 DIM1-A-A-A
-------- 5 3 DIM1-A-A-B
------ 6 2 DIM1-A-B
-------- 7 6 DIM1-A-B-A
-------- 8 6 DIM1-A-B-B
------ 9 2 DIM1-A-C
---- 10 1 DIM1-B
------ 11 10 DIM1-B-C
------ 12 10 DIM1-B-D
---- 13 1 DIM1-C
Example of DIM2 table:
-- 1 0 DIM2
---- 2 1 DIM2-A
------ 3 2 DIM2-A-A
-------- 4 3 DIM2-A-A-A
-------- 5 3 DIM2-A-A-B
-------- 6 3 DIM2-A-B-C
------ 7 2 DIM2-A-B
---- 8 1 DIM2-B
---- 9 1 DIM2-C
Example of FACTS table:
1 1 100
1 2 30
1 3 500
-- ................
13 9 200
And I would like to create the only SELECT where I will specify the parent for DIM1 (for example ID1=2 for DIM1-A) and parent for DIM2 (for example ID2=2 for DIM2-A) and SELECT will generate a report like this:
Name_of_1 Name_of_2 Sum_of_X
--------- --------- ----------
DIM1-A-A DIM2-A-A (some sum)
DIM1-A-A DIM2-A-B (some sum)
DIM1-A-B DIM2-A-A (some sum)
DIM1-A-B DIM2-A-B (some sum)
DIM1-A-C DIM2-A-A (some sum)
DIM1-A-C DIM2-A-B (some sum)
I would like to use CONNECT BY phrase, START WITH phrase, SUM phrase, GROUP BY phrase, and OUTER or INNER (?) JOIN. I need no other extensions of Oracle 10.2.
In other words: only with "classic" SQL and
only Oracle extensions for hierarchy queries.
Is it possible?
I tried some experiments with question in
Mixing together Connect by, inner join and sum with Oracle
(where is a very nice solution but only for one
dimension table ("Tasks"), but I need to JOIN two dimension tables to one facts table), but I was not successful.

"Some sum" is not very descriptive, so I don't see why do you need CONNECT BY at all.
SELECT dim1.name, dim2.name, x
FROM (
SELECT id1, id2, SUM(x) AS x
FROM facts
GROUP BY
id1, id2
) f
JOIN dim1
ON dim1.id = f.id1
JOIN dim2
ON dim2.id = f.id2

I think what you're trying to do is get the sum of the value in the facts table for all of the children of the specified rows grouped by the topmost children. This would mean that in your example above, the results for the first row would be the sum any intersections of (DIM1-A-A, DIM1-A-A-A, DIM1-A-A-B) and (DIM2-A-A, DIM2-A-A-A, DIM2-A-A-B, DIM3-A-A-C) found in the FACTS table. With that assumption, I have come to the following solution:
SELECT root_name1, root_name2, SUM(X)
FROM ( SELECT CONNECT_BY_ROOT(name1) AS root_name,
id1
FROM dim1
CONNECT BY parent1 = PRIOR id1
START WITH parent1 = 2) d1
CROSS JOIN
( SELECT CONNECT_BY_ROOT(name2) AS root_name,
id2
FROM dim2
CONNECT BY parent2 = PRIOR id2
START WITH parent2 = 2) d2
LEFT OUTER JOIN
facts
ON d1.id1 = facts.id1
AND d2.id2 = facts.id2
GROUP BY root_name1, root_name2
(This also assumes that the columns of FACTS are named ID1, ID2, and X.)

Related

Is it possible to add distinct to part of a sum clause in Oracle?

I have a pretty lengthy SQL query which I'm going to run on Oracle via hibernate. It consists of two nested selects. In the first select statement, a number of sums are calculated, but in one of them I want to filter the results using unique ids.
SELECT ...
SUM(NVL(CASE WHEN SECOND_STATUS= 50 OR SECOND_STATUS IS NULL THEN RECEIVE_AMOUNT END, 0) +
NVL(CASE WHEN FIRST_STATUS = 1010 THEN AMOUNT END, 0) +
NVL(CASE WHEN FIRST_STATUS = 1030 THEN AMOUNT END, 0) -
NVL(CASE WHEN FIRST_STATUS = 1010 AND (SECOND_STATUS= 50 OR SECOND_STATUS IS NULL) THEN RECEIVE_AMOUNT END, 0)) TOTAL, ...
And at the end:
... FROM (SELECT s.*, p.* FROM FIRST_TABLE s
JOIN SECOND_TABLE p ON s.ID = p.FIRST_ID
In one of the lines that start with NVL (second line actually), I want to add a distinct clause that sums the amounts only if first table ids are unique. But I don't know if this is possible or not. If yes, how would it be?
Assume such setup
select * from first;
ID AMOUNT
---------- ----------
1 10
2 20
select * from second;
SECOND_ID FIRST_ID AMOUNT2
---------- ---------- ----------
1 1 100
2 1 100
3 2 100
After the join you get the total sum of both amounts too high because the amount from the first table is duplicated.
select *
from first
join second on first.id = second.first_id;
ID AMOUNT SECOND_ID FIRST_ID AMOUNT2
---------- ---------- ---------- ---------- ----------
1 10 1 1 100
1 10 2 1 100
2 20 3 2 100
You must add a row_number that identifies the first occurence in the parent table and consider in the AMOUNT only the first row and resets it to NULL in the duplicated rows.
select ID,
case when row_number() over (partition by id order by second_id) = 1 then AMOUNT end as AMOUNT,
SECOND_ID, FIRST_ID, AMOUNT2
from first
join second on first.id = second.first_id;
ID AMOUNT SECOND_ID FIRST_ID AMOUNT2
---------- ---------- ---------- ---------- ----------
1 10 1 1 100
1 2 1 100
2 20 3 2 100
Now you can safely sum in a separate subquery
with tab as (
select ID,
case when row_number() over (partition by id order by second_id) = 1 then AMOUNT end as AMOUNT,
SECOND_ID, FIRST_ID, AMOUNT2
from first
join second on first.id = second.first_id
)
select id, sum(nvl(amount,0) + nvl(amount2,0))
from tab
group by id
;
ID SUM(NVL(AMOUNT,0)+NVL(AMOUNT2,0))
---------- ---------------------------------
1 210
2 120
Note also that this is an answer to your question. Some will argue that in your situation you should first aggregate and than join. This will also resolve your problem possible more elegantly.

Multiple sorting conditions in DolphinDB

Suppose I have a table as follows:
id=`A`B`A`B`B`B`A
item= 10 1 1 3 5 10 6
t=table(id,item)
id item
-- ----
A 10
B 1
A 1
B 3
B 5
B 10
A 6
For example, I want to sort the table with two conditions: first, by the most commonly occurring item in column item, then by the highest number in column item.
How can I sort like this:
id item
--- ----
A 10
B 10
A 1
B 1
A 6
B 5
B 3
Is there any way to go about this? Thanks!
Try this:
t1=table(id,item);
update t1 set count=count(item) context by item;
select * from t1 order by count desc, item desc;

Updating row, according to Rownum in Oracle

I have two tables in Oracle
TableProducts
Product_Code, and 20 others fields
BGU
LSO
MPA
MPA4
MPA5
TPA
UGU
For this example, now I have 7 values, but maybe 9 values later.
CREATE TABLE TableContacts AS SELECT *
FROM Contacts
WHERE Rownum <= (4*(SELECT Count(Distinct Product_Code) FROM TableProducts));
Now I have 28 Rows in my TableContacts.
Now I need To UPDATE the rows in order to create combinations test.
TableContacts
Product_Code, Email, PDF, and 17 others fields.
Email and PDF has two possible values 'N' or 'Y'.
I need to fill the TableContacts with the combinations of Product_Code, Email and PDF fields, according to Rownum position.
Rownum = 1 -> Product_Code='BGU', Email='N', PDF='N'
Rownum = 2 -> Product_Code='BGU', Email='N', PDF='Y'
Rownum = 3 -> Product_Code='BGU', Email='Y', PDF='N'
Rownum = 4 -> Product_Code='BGU', Email='Y', PDF='Y'
Rownum = 5 -> Product_Code='LSO', Email='N', PDF='N'
If I have 7 values for Product_Code, 2 by Email and 2 by PDF, then I will need to fill (7 * 2 *2) = 28 Rows.
How to create and SQL for this situation updating TableContacts?
Partially, requirement doesn't make sense. Rows in a table within the relational databases aren't sorted in any way, so - saying that you want to refer to a rownum is ... strange. That's why I modified the contacts table and added yet another column - rn - which shows that rownum of yours.
Also, this example shows only 3 products (didn't feel like typing all of them). Code that follows doesn't care about number of those products and will work the same regardless.
Products:
SQL> select * From products;
CODE
-----
BGU
LSO
MPA
Insert into Contacts:
SQL> insert into contacts (rn, code, email, pdf)
2 with temp as
3 (select p.code, x.lvl
4 from products p cross join (Select level lvl from dual connect by level <= 4) x
5 )
6 select t.lvl,
7 t.code,
8 case when t.lvl in (1, 2) then 'N'
9 when t.lvl in (3, 4) then 'Y'
10 end email,
11 --
12 case when t.lvl in (1, 3) then 'N'
13 when t.lvl in (2, 4) then 'Y'
14 end pdf
15 from temp t;
12 rows created.
Result:
SQL> select * From contacts
2 order by code, rn;
RN CODE E P
---------- ----- - -
1 BGU N N
2 BGU N Y
3 BGU Y N
4 BGU Y Y
1 LSO N N
2 LSO N Y
3 LSO Y N
4 LSO Y Y
1 MPA N N
2 MPA N Y
3 MPA Y N
4 MPA Y Y
12 rows selected.
SQL>
[EDIT: how to update table that contains rows?]
If I understood you correctly, this is what you initially have in the CONTACTS table:
SQL> select code, rownum from contacts;
CODE ROWNUM
----- ----------
BGU 1
LSO 2
MPA 3
BGU 4
LSO 5
MPA 6
BGU 7
LSO 8
MPA 9
BGU 10
LSO 11
MPA 12
12 rows selected.
SQL>
As I previously said: rownum is irrelevant here, it can change, you can't tell which rownum belongs to which code.
Anyway, such an update (merge, actually) does the job:
SQL> merge into contacts a
2 using (select c.code,
3 c.rowid,
4 row_number() over (partition by c.code order by null) rn
5 from contacts c
6 ) x
7 on (a.rowid = x.rowid)
8 when matched then update set
9 a.email = case when x.rn in (1, 2) then 'N'
10 when x.rn in (3, 4) then 'Y'
11 end,
12 a.pdf = case when x.rn in (1, 3) then 'N'
13 when x.rn in (2, 4) then 'Y'
14 end;
12 rows merged.
SQL> select * From contacts order by code, email, pdf;
CODE EMAIL PDF
----- ----- -----
BGU N N
BGU N Y
BGU Y N
BGU Y Y
LSO N N
LSO N Y
LSO Y N
LSO Y Y
MPA N N
MPA N Y
MPA Y N
MPA Y Y
12 rows selected.
SQL>

How can I query to get the rows according to the certain column's value in oracle? [duplicate]

This question already has an answer here:
how to duplicate my sql results? [duplicate]
(1 answer)
Closed 2 years ago.
Table A is:
--------------
C1 C2
--------------
A 3
B 2
--------------
select * from
(
select 'A' as C1, 3 as C2 from dual
union all
select 'B' as C1, 2 as C2 from dual
)
I want to get the following result view with one query statement:
--------------
C1 N1
--------------
A 1
A 2
A 3
B 1
B 2
--------------
I need to generate rows as many as C2 value
Is this possible?
Thank you.
We can handle this via the use of a calendar/sequence table. Consider:
WITH nums AS (
SELECT 1 AS val FROM dual UNION ALL
SELECT 2 FROM dual UNION ALL
SELECT 3 FROM dual
)
SELECT
a.C1,
n.val AS N1
FROM TableA a
INNER JOIN nums n
ON n.val <= a.C2
ORDER BY
a.C1,
n.val;
Demo
Note that in practice, you might use a dedicated table containing a sequence of numbers to cover all possible values in your table. Or, you might use an Oracle sequence.
Alternatively:
SQL> with test as
2 (select 'A' as C1, 3 as C2 from dual
3 union all
4 select 'B' as C1, 2 as C2 from dual
5 )
6 select c1, column_value n1
7 from test cross join table(cast(multiset(select level from dual
8 connect by level <= c2
9 ) as sys.odcinumberlist))
10 order by c1, column_value;
C N1
- ----------
A 1
A 2
A 3
B 1
B 2
SQL>

How to speed up EDIT_DISTANCE and Insert Query?

---------------
MASTER TABLE
---------------
DATA_KEY NUMBER
TEXT VARCHAR2(2000)
ORDER_NO NUMBER
---------------
DETAIL TABLE
---------------
DATA_KEY NUMBER
SIMILAR_DATA_KEY NUMBER
DISTANCE_COUNT NUMBER
---------------
INSERT QUERY
---------------
INSERT INTO DETAIL
(
SELECT DATA_KEY, SIMILAR_DATA_KEY, DISTANCE_COUNT
FROM
(
SELECT A.DATA_KEY AS DATA_KEY, B.DATA_KEY AS SIMILAR_DATA_KEY,
UTL_MATCH.EDIT_DISTANCE(A.TEXT, B.TEXT) AS DISTANCE_COUNT
FROM
(SELECT DATA_KEY, TEXT, ORDER_NO FROM MASTER) A
INNER JOIN
(SELECT DATA_KEY, TEXT, ORDER_NO FROM MASTER) B
ON (A.ORDER_NO < B.ORDER_NO)
)
WHERE DISTANCE_COUNT <= 5
)
I need compare MASTER table TEXT field with other TEXT field.
indexes are not exist.
master table 90,000 rows.
ORDER_NO field is for avoid duplicated compare. (1 .. 90000)
=============================================================
A.ORDER_NO < B.ORDER_NO
------------------------------------------
1, 1 <- exclude
1, 2 <- join
1, 3 <- join
1, 4 <- join
..
2, 1 <- exclude
2, 2 <- exclude
2, 3 <- join
2, 4 <- join
...
3, 1 <- exclude
3, 2 <- exclude
3, 3 <- exclude
3, 4 <- join
1. NOT need compare 1 and 1
2. Need compare 1 and 2
3. NOT need compare 2 and 1 (because, duplicate 2.)
so, for decrease compare count...
=============================================================
Slow zone is (WHERE DISTANCE_COUNT <= 5) ?
Slow zone is comparing rows (90000*89999/2) ?
Query elapse time is 7 days.
6,000 rows inserted to DETAIL table.
How to speed up?
I'm sorry for poor English...

Resources