I have three 3 tables (having the same key) in the following structure:
Input Table t1:
file_in| f_in_state|
--------------------
F01 | 1 |
F02 | 2 |
F21 | 1 |
F41 | 2 |
Input Table t2:
line_in| file_in| l_in_state |
-----------------------------
L001 | F01 | 1 |
L002 | F01 | 2 |
L003 | F01 | 2 |
L004 | F01 | 2 |
L005 | F21 | 1 |
L006 | F21 | 1 |
L007 | F21 | 1 |
L008 | F21 | 1 |
Input Table t3:
line_out|line_in| file_in| l_out_state|
---------------------------------------
D001 |L001 | F01 | 1 |
D002 |L002 | F01 | 1 |
D003 |L003 | F01 | 1 |
and I need to count the number of occurrences of the columns refering to the different states across my three tables for each "file in id" and then combine them to get an output like this:
file_in_id|file_in_state| A | B | C | D | E |
---------------------------------------------
F01 | 1 | 1 | 3 | 0 | 0 | 3 |
F02 | 2 | 2 | 0 | 0 | 0 | 0 |
F21 | 1 | 1 | 4 | 0 | 0 | 0 |
F41 | 2 | 2 | 0 | 0 | 0 | 0 |
with:
A refers to the number of input lines ("line_in") having the state = '1'
B refers to the number of input lines ("line_in") having the state = '2'
C refers to the number of input lines ("line_in") having the state = '3' (in my case, there is no line with this state, but it is possible to happen)
D refers to the number of output lines ("line_out") having the state = '1'
E refers to the number of output lines ("line_out") having the state = '2'
So, I tried to use the decode function in my query but I didn't get the wished result.
SELECT
t1.file_in AS file_in_id,
t1.f_in_state AS file_in_state,
COUNT(DECODE(t2.f_in_state, '1', 1, null)) AS A,
COUNT(DECODE(t2.f_in_state, '2', 1, null)) AS B,
COUNT(DECODE(t2.f_in_state, '3', 1, null)) AS C,
COUNT(DECODE(t3.f_out_state, '1', 1, null)) AS D,
COUNT(DECODE(t3.f_out_state, '2', 1, null)) AS E
FROM table1 t1,
table2 t2,
table3 t3
WHERE t1.file_in = t2.file_in (+)
AND t2.file_in = t3.file_in (+)
GROUP BY t1.file_in, t1.f_in_state
ORDER BY t1.file_in
But, this is what I get :
file_in_id|file_in_state|A |B |C |D |E |
----------------------------------------
F01 |1 |1 |3 |9 |0 |12|
F02 |2 |2 |0 |0 |0 |0 |
F21 |1 |1 |4 |0 |0 |0 |
F41 |2 |2 |0 |0 |0 |0 |
Could somebody tells me what is wrong with this query and how can I fix it to get what I would like to have as a result.
It's very important, this is how the input table 3 should be :
Input Table t3:
line_out|*file_out*| file_in| l_out_state|
---------------------------------------
D001 |W01 | F01 | 1 |
D002 |W01 | F01 | 1 |
D003 |W01 | F01 | 1 |
This query gives desired result:
select file_in, f_in_state,
count(case l_in_state when '1' then 1 end) a,
count(case l_in_state when '2' then 1 end) b,
count(case l_in_state when '3' then 1 end) c,
count(case l_out_state when '1' then 1 end) d,
count(case l_out_state when '2' then 1 end) e
from t1
left join t2 using (file_in)
left join t3 using (file_in, line_in)
group by file_in, f_in_state
order by file_in
You could also use pivot if you have Oracle 11g or above.
Test:
with t1(file_in, f_in_state) as (
select 'F01', '1' from dual union all
select 'F02', '2' from dual union all
select 'F21', '1' from dual union all
select 'F41', '2' from dual ),
t2(line_in, file_in, l_in_state) as (
select 'L001', 'F01', '1' from dual union all
select 'L002', 'F01', '2' from dual union all
select 'L003', 'F01', '2' from dual union all
select 'L004', 'F01', '2' from dual union all
select 'L005', 'F21', '1' from dual union all
select 'L006', 'F21', '1' from dual union all
select 'L007', 'F21', '1' from dual union all
select 'L008', 'F21', '1' from dual ),
t3(line_out, line_in, file_in, l_out_state) as (
select 'D001', 'L001', 'F01', '1' from dual union all
select 'D002', 'L002', 'F01', '1' from dual union all
select 'D003', 'L003', 'F01', '1' from dual )
select file_in, f_in_state,
count(case l_in_state when '1' then 1 end) a,
count(case l_in_state when '2' then 1 end) b,
count(case l_in_state when '3' then 1 end) c,
count(case l_out_state when '1' then 1 end) d,
count(case l_out_state when '2' then 1 end) e
from t1
left join t2 using (file_in)
left join t3 using (file_in, line_in)
group by file_in, f_in_state
order by file_in
Output:
FILE_IN F_IN_STATE A B C D E
------- ---------- ---------- ---------- ---------- ---------- ----------
F01 1 1 3 0 3 0
F02 2 0 0 0 0 0
F21 1 4 0 0 0 0
F41 2 0 0 0 0 0
Variant with SUM
Select a.file_in, a.f_in_state,
Sum(Case When b.l_in_state=1 Then 1 Else 0 End) A,
Sum(Case When b.l_in_state=2 Then 1 Else 0 End) B,
Sum(Case When b.l_in_state=3 Then 1 Else 0 End) C,
Sum(Case When c.l_out_state=1 Then 1 Else 0 End) D,
Sum(Case When c.l_out_state=2 Then 1 Else 0 End) E
From T1 a
Left join T2 b on a.file_in=b.file_in
Left join T3 c on a.file_in=c.file_in and b.line_in=c.line_in
GROUP BY a.file_in, a.f_in_state
ORDER BY a.file_in
Related
I'm having trouble figuring out how to do something in Oracle. I have this table:
| id | rownum | code | gift |
|-----|--------|-----------|------|
|2000 | 1 | Ganymede | 437 |
|2000 | 2 | Alpha | 50 |
|2000 | 3 | Ambergris | 600 |
And the client wants it to look like this:
| id | code_1 | gift_1 | code_2 | gift_2 | code_3 | gift_3 |
|----|--------|--------|--------|--------|-----------|--------|
|2000|Ganymede| 437 | Alpha | 50 | Ambergris | 600 |
I'm not quite sure how to go about doing this using PIVOT.
You can use PIVOT:
SELECT id,
"1_CODE" AS code_1,
"1_GIFT" AS gift_1,
"2_CODE" AS code_2,
"2_GIFT" AS gift_2,
"3_CODE" AS code_3,
"3_GIFT" AS gift_3
FROM table_name
PIVOT (
MAX(code) AS code,
MAX(gift) AS gift
FOR "ROWNUM" IN (1, 2, 3)
)
or conditional aggregation:
SELECT id,
MAX(CASE "ROWNUM" WHEN 1 THEN code END) AS code_1,
MAX(CASE "ROWNUM" WHEN 1 THEN gift END) AS gift_1,
MAX(CASE "ROWNUM" WHEN 2 THEN code END) AS code_2,
MAX(CASE "ROWNUM" WHEN 2 THEN gift END) AS gift_2,
MAX(CASE "ROWNUM" WHEN 3 THEN code END) AS code_3,
MAX(CASE "ROWNUM" WHEN 3 THEN gift END) AS gift_3
FROM table_name
GROUP BY id
Which, for the sample data:
CREATE TABLE table_name (id, "ROWNUM", code, gift ) AS
SELECT 2000, 1, 'Ganymede', 437 FROM DUAL UNION ALL
SELECT 2000, 2, 'Alpha', 50 FROM DUAL UNION ALL
SELECT 2000, 3, 'Ambergris', 600 FROM DUAL;
Both output:
ID
CODE_1
GIFT_1
CODE_2
GIFT_2
CODE_3
GIFT_3
2000
Ganymede
437
Alpha
50
Ambergris
600
db<>fiddle here
When I run the following code, I would expect b1 and b2 to be equal, however, b2 is doubled. Am I doing something wrong? Is this a bug in the database? We're running Oracle 12c (12.2.0.1.0).
WITH TBL AS
(
SELECT 1 a, 1 b FROM DUAL UNION ALL
SELECT 1 a, 2 b FROM DUAL UNION ALL
SELECT 1 a, 3 b FROM DUAL UNION ALL
SELECT 1 a, 4 b FROM DUAL
)
SELECT
*
FROM
TBL
MATCH_RECOGNIZE
(
PARTITION BY
a
ORDER BY
b
MEASURES
FINAL SUM(b) b1,
NULLIF(FINAL SUM(b), 0) b2
ALL ROWS PER MATCH WITH UNMATCHED ROWS
AFTER MATCH SKIP PAST LAST ROW
PATTERN
(C*)
DEFINE
C AS B > 0
) mr
Result:
| A | B | B1 | B2 |
|---|---|----|----|
| 1 | 1 | 10 | 20 |
| 1 | 2 | 10 | 20 |
| 1 | 3 | 10 | 20 |
| 1 | 4 | 10 | 20 |
The problem seems to be with NULLIF when I converted the same into it's logical equivalent and it is working fine CASE WHEN expr1 = expr 2 THEN NULL ELSE expr1 END
WITH TBL AS
(
SELECT 1 a, 1 b FROM DUAL UNION ALL
SELECT 1 a, 2 b FROM DUAL UNION ALL
SELECT 1 a, 3 b FROM DUAL UNION ALL
SELECT 1 a, 4 b FROM DUAL
)
SELECT
*
FROM
TBL
MATCH_RECOGNIZE
(
PARTITION BY
a
ORDER BY
b
MEASURES
FINAL SUM(b) b1,
CASE WHEN FINAL SUM(b)=0 THEN NULL ELSE FINAL SUM(b) END b2
ALL ROWS PER MATCH WITH UNMATCHED ROWS
AFTER MATCH SKIP PAST LAST ROW
PATTERN
(C*)
DEFINE
C AS B > 0
) mr
Result
| A | B | B1 | B2 |
|---|---|----|----|
| 1 | 1 | 10 | 10 |
| 1 | 2 | 10 | 10 |
| 1 | 3 | 10 | 10 |
| 1 | 4 | 10 | 10 |
So I have some data like this
NO| ID | PID | COUNT
1 | 00033 | P4 | 1
2 | 00033 | P3 | 3
3 | 00033 | P2 | 2
i want to iterate the ID and PID based on count values, like this
NO| ID | PID
1 | 00033 | P4
2 | 00033 | P3
3 | 00033 | P3
4 | 00033 | P3
5 | 00033 | P2
6 | 00033 | P2
actually its already solved using this query
SELECT row_number() OVER ( ORDER BY t."ID", t."PID" DESC ) as NO,
t."ID", t."PID"
FROM Table1 t
CROSS APPLY(
SELECT 1 FROM dual
CONNECT BY level <= t."COUNT"
)
ORDER BY t."ID", t."PID" DESC
as per this link
Oracle iterates thru values on columns
but since our DB using oracle 11. the code doesnt working anymore.
i'd like to know the same approach for oracle 11.
Regards, Rian
Cast this hierarchical query as table of numbers and then join:
with
t("NO", "ID", "PID", "COUNT") as (
select 1, '00033', 'P4', 1 from dual union all
select 2, '00033', 'P3', 3 from dual union all
select 3, '00033', 'P2', 2 from dual ),
a as (select t.*,
cast(multiset(select level lvl
from dual
connect by level <= "COUNT")
as sys.odcinumberlist) nums
from t)
select "NO", "ID", "PID" from a cross join table(nums)
Result:
NO ID PID
---------- ----- ---
1 00033 P4
2 00033 P3
2 00033 P3
2 00033 P3
3 00033 P2
3 00033 P2
6 rows selected
Why do we use a (+) operator in the where clause for instance emp_name(+) IS NOT NULL, emp_name IS NOT NULL AND emp_name(+) IS NOT NULL is the same
Because removing the (+) from the column you're checking is not null turns the join from an outer join into what is effectively an inner join. Leaving the (+) in tells oracle to get all rows from the "main" table, and then match any rows from the outer joined table where that column is not null.
See the below for an example of why the "extra" (+) is needed:
with t1 as (select 1 id, 'a' val from dual union all
select 2 id, 'b' val from dual union all
select 3 id, 'c' val from dual),
t2 as (select 1 id, null val from dual union all
select 3 id, 'd' val from dual)
select *
from t1,
t2
where t1.id = t2.id (+)
and t2.val (+) is not null
order by t1.id;
ID VAL ID_1 VAL_1
---------- --- ---------- -----
1 a
2 b
3 c 3 d
with t1 as (select 1 id, 'a' val from dual union all
select 2 id, 'b' val from dual union all
select 3 id, 'c' val from dual),
t2 as (select 1 id, null val from dual union all
select 3 id, 'd' val from dual)
select *
from t1,
t2
where t1.id = t2.id (+)
and t2.val is not null
order by t1.id;
ID VAL ID_1 VAL_1
---------- --- ---------- -----
3 c 3 d
You can see the difference easier if you convert the query to the ANSI join syntax:
with t1 as (select 1 id, 'a' val from dual union all
select 2 id, 'b' val from dual union all
select 3 id, 'c' val from dual),
t2 as (select 1 id, null val from dual union all
select 3 id, 'd' val from dual)
select *
from t1
left outer join t2 on (t1.id = t2.id and t2.val is not null)
order by t1.id;
ID VAL ID_1 VAL_1
---------- --- ---------- -----
1 a
2 b
3 c 3 d
with t1 as (select 1 id, 'a' val from dual union all
select 2 id, 'b' val from dual union all
select 3 id, 'c' val from dual),
t2 as (select 1 id, null val from dual union all
select 3 id, 'd' val from dual)
select *
from t1
left outer join t2 on (t1.id = t2.id)
where t2.val is not null
order by t1.id;
ID VAL ID_1 VAL_1
---------- --- ---------- -----
3 c 3 d
In other words, it's the difference between the "col is not null" predicate being a part of the outer join condition, or a filter in the where clause.
You'll note as well that having the "t2.val is not null" in the where clause has the effect of turning the outer join into an inner join, despite the fact that you've requested an outer join:
with t1 as (select 1 id, 'a' val from dual union all
select 2 id, 'b' val from dual union all
select 3 id, 'c' val from dual),
t2 as (select 1 id, null val from dual union all
select 3 id, 'd' val from dual)
select *
from t1
left outer join t2 on (t1.id = t2.id)
--where t2.val is not null
order by t1.id;
-----------------------------------------------------------------------------------------------------
| Id | Operation | Name | E-Rows |E-Bytes| Cost (%CPU)| E-Time | OMem | 1Mem | O/1/M |
-----------------------------------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | | | 12 (100)| | | | |
| 1 | SORT ORDER BY | | 3 | 33 | 12 (17)| 00:00:01 | 2048 | 2048 | 1/0/0|
|* 2 | HASH JOIN OUTER| | 3 | 33 | 11 (10)| 00:00:01 | 1156K| 1156K| 1/0/0|
| 3 | VIEW | | 3 | 18 | 6 (0)| 00:00:01 | | | |
| 4 | UNION-ALL | | | | | | | | |
| 5 | FAST DUAL | | 1 | | 2 (0)| 00:00:01 | | | |
| 6 | FAST DUAL | | 1 | | 2 (0)| 00:00:01 | | | |
| 7 | FAST DUAL | | 1 | | 2 (0)| 00:00:01 | | | |
| 8 | VIEW | | 2 | 10 | 4 (0)| 00:00:01 | | | |
| 9 | UNION-ALL | | | | | | | | |
| 10 | FAST DUAL | | 1 | | 2 (0)| 00:00:01 | | | |
| 11 | FAST DUAL | | 1 | | 2 (0)| 00:00:01 | | | |
-----------------------------------------------------------------------------------------------------
with t1 as (select 1 id, 'a' val from dual union all
select 2 id, 'b' val from dual union all
select 3 id, 'c' val from dual),
t2 as (select 1 id, null val from dual union all
select 3 id, 'd' val from dual)
select *
from t1
left outer join t2 on (t1.id = t2.id)
where t2.val is not null
order by t1.id;
-----------------------------------------------------------------------------------------------------
| Id | Operation | Name | E-Rows |E-Bytes| Cost (%CPU)| E-Time | OMem | 1Mem | O/1/M |
-----------------------------------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | | | 10 (100)| | | | |
| 1 | SORT ORDER BY | | 1 | 11 | 10 (20)| 00:00:01 | 2048 | 2048 | 3/0/0|
|* 2 | HASH JOIN | | 1 | 11 | 9 (12)| 00:00:01 | 1156K| 1156K| 3/0/0|
| 3 | VIEW | | 2 | 10 | 2 (0)| 00:00:01 | | | |
| 4 | UNION-ALL | | | | | | | | |
|* 5 | FILTER | | | | | | | | |
| 6 | FAST DUAL | | 1 | | 2 (0)| 00:00:01 | | | |
| 7 | FAST DUAL | | 1 | | 2 (0)| 00:00:01 | | | |
| 8 | VIEW | | 3 | 18 | 6 (0)| 00:00:01 | | | |
| 9 | UNION-ALL | | | | | | | | |
| 10 | FAST DUAL | | 1 | | 2 (0)| 00:00:01 | | | |
| 11 | FAST DUAL | | 1 | | 2 (0)| 00:00:01 | | | |
| 12 | FAST DUAL | | 1 | | 2 (0)| 00:00:01 | | | |
-----------------------------------------------------------------------------------------------------
Note the change from HASH JOIN OUTER to HASH JOIN in the row with id = 2 in the 2nd explain plan.
Closed. This question needs details or clarity. It is not currently accepting answers.
Want to improve this question? Add details and clarify the problem by editing this post.
Closed 8 years ago.
Improve this question
user_id | username | salary |
+---------+----------+------+
| 1 | John | 4000 |
| 2 | Paul | 0900 |
| 3 | Adam | 0589 |
| 4 | Ben | 2154 |
| 5 | Charles | 2489 |
| 6 | Dean | 2500 |
| 7 | Edward | 2900 |
| 8 | Fred | 2800 |
| 9 | George | 4100 |
| 10 | Hugo | 5200 |
I need output like this
range count
--------------------
0-999 2
1000-1999 0
2000-2999 5
3000-3999 0
4000-4999 2
5000-5999 1
Here is an attempt:
with w as
(
select 1000 * (level - 1) low, 1000 * level high from dual
connect by level <= 10
)
select w.low, w.high, sum(decode(t.user_id, null, 0, 1)) nb
from w, test_epn t
where w.low <= t.salary (+)
and w.high > t.salary (+)
group by w.low, w.high
order by w.low
;
This gives:
1 0 1000 2
2 1000 2000 0
3 2000 3000 5
4 3000 4000 0
5 4000 5000 2
6 5000 6000 1
7 6000 7000 0
8 7000 8000 0
9 8000 9000 0
10 9000 10000 0
SQL> col range format a30
SQL> with t as (
2 select 'John' name, 4000 sal from dual union all
3 select 'Paul' name, 900 from dual union all
4 select 'Adam' name, 589 from dual union all
5 select 'Ben' name, 2154 from dual union all
6 select 'Charles' name, 2489 from dual union all
7 select 'Dean' name, 2500 from dual union all
8 select 'Edward' name, 2900 from dual union all
9 select 'Fred' name, 2800 from dual union all
10 select 'George' name, 4100 from dual union all
11 select 'Hugo' name, 5200 from dual
12 )
13 select to_char(pvtid*1000)||'-'||to_char(pvtid*1000+999) range, count(t.sal)
14 from t
15 ,
16 (
17 select rownum-1 pvtid
18 from dual connect by level <= (select floor(max(sal)/1000) from t)+1
19 ) piv
20 where piv.pvtid = floor(t.sal(+)/1000)
21 group by piv.pvtid
22 order by 1
23 /
RANGE COUNT(T.SAL)
------------------------------ ------------
0-999 2
1000-1999 0
2000-2999 5
3000-3999 0
4000-4999 2
5000-5999 1
Oracle 11g R2 Schema Setup:
create table test_table as
select 1 user_id, 'John' username , 4000 salary from dual union all
select 2 , 'Paul' , 0900 from dual union all
select 3 , 'Adam' , 0589 from dual union all
select 4 , 'Ben' , 2154 from dual union all
select 5 , 'Charles' , 2489 from dual union all
select 6 , 'Dean' , 2500 from dual union all
select 7 , 'Edward' , 2900 from dual union all
select 8 , 'Fred' , 2800 from dual union all
select 9 , 'George' , 4100 from dual union all
select 10 , 'Hugo' , 5200 from dual
Query 1:
with range_tab(f,t) as (select (level - 1)*1000 , (level - 1)*1000 + 999
from dual
connect by (level - 1)*1000 <= (select max(salary) from test_table))
select f ||'-'|| t as range, count(user_id)
from test_table
right outer join range_tab on (salary between f and t)
group by f, t
order by 1
[Results][2]:
| RANGE | COUNT(USER_ID) |
|-----------|----------------|
| 0-999 | 2 |
| 1000-1999 | 0 |
| 2000-2999 | 5 |
| 3000-3999 | 0 |
| 4000-4999 | 2 |
| 5000-5999 | 1 |
In case of fixed interval you can also use Oracle WIDTH_BUCKET function.
select count(*),
(WIDTH_BUCKET(salary, 0, 10000,10)-1)*1000 ||'-'||to_char(WIDTH_BUCKET(salary, 0, 10000,10)*1000-1) as salary_range
from table1
group by WIDTH_BUCKET(salary, 0, 10000,10)
order by salary_range;
| COUNT(*) | SALARY_RANGE |
|----------|--------------|
| 2 | 0-999 |
| 5 | 2000-2999 |
| 2 | 4000-4999 |
| 1 | 5000-5999 |
Disadvantage is: It does not count empty buckets, but maybe this satisfy your needs anyway.