Alternate approach for Case statement in oracle - oracle

I have a query insert statement like shown below
insert into my_table
select col1,col2,CASE
WHEN EXISTS
(SELECT 1 FROM Table_A m WHERE m.COLA='XXX' AND m.id=b.id
)
THEN
(SELECT m.COLB FROM Table_A m WHERE m.COLA='XXX' AND m.id=b.id
)
ELSE 'Not Open'
END XXX,
CASE
WHEN EXISTS
(SELECT 1 FROM Table_A m WHERE m.COLA='YYY' AND m.id=b.id
)
THEN
(SELECT m.COLB FROM Table_A m WHERE m.COLA='YYY' AND m.id=b.id
)
ELSE 'Not Open'
END YYY,
.
.
.
.
. -- ~150 case statement
from Test b,table_main c
where b.id=c.id
Here we have case statement which is referring same table (TABLE_A) but checking for different values(XXX,YYY,etc..,) in each statement. This is taking so much time. We are trying to optimize this insert query. Could someone help here?

Try a variation of a pivot query:
SELECT col1, col2 ,
coalesce ( max( CASE WHEN m.COLA='XXX' THEN m.COLB END ), 'Not open' ) As XXX,
coalesce ( max( CASE WHEN m.COLA='YYY' THEN m.COLB END ), 'Not open' ) As YYY,
.....
.....
coalesce ( max( CASE WHEN m.COLA='ZZZ' THEN m.COLB END ), 'Not open' ) As ZZZ
FROM Test b
JOIN table_main c ON b.id=c.id
LEFT JOIN Table_A m ON m.id=b.id
GROUP BY col1, col2

Rewrite using decode and LEFT JOIN as follows.
SELECT col1, col2 ,
decode ( m.COLA , 'XXX' , m.COLB , 'Not Open' ) XXX ,
decode ( m.COLA , 'YYY', m.COLB , 'Not Open' ) YYY ,
...
...
FROM Test b LEFT JOIN Table_A m ON m.id=b.id JOIN table_main c
ON b.id=c.id;

It is taking time because you are making several calls to a table to get your values. Simply reduce the number of calls being made to Table_A.
If the condition Table_A.id = m.id and m.COLA= 'Any Value' will always return one row then you can join the tables Table_A, Test and Table_main in a single join like below
Insert into my_table(col1, col2) values
select
decode(a.COLA,'XXX' , a.COLB , 'Not Open'),
decode(a.COLA,'YYY' , a.COLB , 'Not Open'),
...
...
from Table_A a, Test b, Table_main c
where a.id = b.id
and b.id = c.id
In case the condition may return multiple records then you can use With clause like below
Insert into my_table(col1, col2) values
with table_a as (select col1, col2 , a.COLA, a.COLB
from Table_A a, Test b, Table_main c
where a.id = b.id
and b.id = c.id
group by col1, col2,a.COLA, a.COLB)
select col1,col2,
decode(x.COLA,'XXX' , x.COLB , 'Not Open'),
decode(x.COLA,'YYY' , x.COLB , 'Not Open'),
....
....
from table_a x

Related

Unpivot Multiple columns in Oracle is not working

I have a table like below
Process_1
Tab1_Count
Tab1_Process_Count
Tab1_Status
Process_2
Tab2_Count
Tab2_Process_Count
Tab2_Status
Tab1_Process
123
123
Equal
Tab2_Process
123
126
Not Equal
I am trying to Unpivot the above like the below
TABLEPROCESS
Tab_Count
Process_Count
Status
Tab1_Process
123
123
Equal
Tab2_Process
123
123
Not Equal
With the below query
SELECT *
FROM
(
SELECT 'Tab1' AS tab1_Process,
count(tb1.ID) AS tab1_count,
count(tbp1.ID) AS tab1_Process_count,
CASE WHEN count(tb1.ID)=count(tbp1.ID) THEN 'Equal' ELSE 'Not Equal' END AS Tab1_Status,
'Tab2' AS tab2_Process,
count(tb2.ID) AS tab2_count,
count(tbp2.ID) AS tab2_Process_count,
CASE WHEN count(tb2.ID)=count(tbp2.ID) THEN 'Equal' ELSE 'Not Equal' END AS Tab2_Status
FROM table1 tb1
LEFT JOIN table1_process tbp1 ON tbp1.ID = tb1.ID
LEFT JOIN table2 tb2 ON tb2.ID = tb1.ID
LEFT JOIN table1_process tbp2 ON tbp2.ID = tb2.ID
)
UNPIVOT
(
(TABLEPROCESS,STATUS)
FOR COUNT IN
(
(Tab1_Count,Tab2_Count) AS 'Tab_Count',
(Tab1_Process_Count,Tab2_Process_Count) AS 'Process_Count',
(Tab1_Status,Tab2_Status) AS 'STATUS'
)
)
but it is giving me error -- ORA-00907: missing right parenthesis
As per comment by Alex Poole tried CROSS APPLY method
SELECT *
FROM
(
SELECT 'Tab1' AS tab1_Process,
count(tb1.ID) AS tab1_count,
count(tbp1.ID) AS tab1_Process_count,
CASE WHEN count(tb1.ID)=count(tbp1.ID) THEN 'Equal' ELSE 'Not Equal' END AS Tab1_Status,
'Tab2' AS tab2_Process,
count(tb2.ID) AS tab2_count,
count(tbp2.ID) AS tab2_Process_count,
CASE WHEN count(tb2.ID)=count(tbp2.ID) THEN 'Equal' ELSE 'Not Equal' END AS Tab2_Status
FROM table1 tb1
LEFT JOIN table1_process tbp1 ON tbp1.ID = tb1.ID
LEFT JOIN table2 tb2 ON tb2.ID = tb1.ID
LEFT JOIN table1_process tbp2 ON tbp2.ID = tb2.ID
)
CROSS APPLY
VALUES (
(tab1_Process,Tab1_Count,Tab1_Process_Count,Tab1_Status),
(tab2_Process,Tab2_Count,Tab2_Process_Count,Tab2_Status),
) (Process,Tab_Count,Process_Count,Status);
It is working in SQL Server but it is giving ORA-00903: invalid table name in oracle
I am new to oracle, Please help
Achieved the expected result using the below UNPIVOT Query
SELECT TABLEPROCESS,TAB_COUNT,PROCESS_COUNT,STATUS
FROM
(
SELECT 'Tab1' AS tab1_Process,
count(tb1.ID) AS tab1_count,
count(tbp1.ID) AS tab1_Process_count,
CASE WHEN count(tb1.ID)=count(tbp1.ID) THEN 'Equal' ELSE 'Not Equal' END AS Tab1_Status,
'Tab2' AS tab2_Process,
count(tb2.ID) AS tab2_count,
count(tbp2.ID) AS tab2_Process_count,
CASE WHEN count(tb2.ID)=count(tbp2.ID) THEN 'Equal' ELSE 'Not Equal' END AS Tab2_Status
FROM table1 tb1
LEFT JOIN table1_process tbp1 ON tbp1.ID = tb1.ID
LEFT JOIN table2 tb2 ON tb2.ID = tb1.ID
LEFT JOIN table1_process tbp2 ON tbp2.ID = tb2.ID
)
UNPIVOT
(
(TABLEPROCESS,TAB_COUNT,PROCESS_COUNT,STATUS)
FOR (t,c,p,s)
IN
(
(tab1_Process,Tab1_Count,Tab1_Process_Count,Tab1_Status),
(tab2_Process,Tab2_Count,Tab2_Process_Count,Tab2_Status)
)
);

Converting Oracle Query to Hive

How can I convert the below query in Oracle to Hive?
SELECT A.EMP_NO, A.LOGIN_TIMESTAMP FROM TABLE1 A, TABLE2 B
WHERE A.EMP_NO = 1234 AND B.EMP_CURR =
(SELECT MIN(EMP_CURR) FROM TABLE2 WHERE EMP_NO = A.EMP_NO AND
LOGIN_TIMESTAMP = A.LOGIN_TIMESTAMP AND EMP_STATUS_CODE <> 'P')
Use dense_rank() to get rows with minimum EMP_CURR:
SELECT A.EMP_NO, A.LOGIN_TIMESTAMP
FROM TABLE1 A
INNER JOIN (select B.*,
dense_rank() over(partition by B.EMP_NO, B.LOGIN_TIMESTAMP order by B.EMP_CURR) rn
from TABLE2 B where EMP_STATUS_CODE <> 'P'
) B
on B.EMP_NO = A.EMP_NO and B.LOGIN_TIMESTAMP = A.LOGIN_TIMESTAMP and B.rn=1
where B.rn=1 and A.EMP_NO = 1234;

Update with a minimum value from a union in Oracle

UPDATE table1 t SET t.columnA =
(SELECT MIN(columnB) FROM
(SELECT columnB FROM table2
WHERE table2.fk = t.pk
UNION ALL
SELECT columnB FROM table3
WHERE table3.fk = t.pk))
gives me ORA-00904: "T"."PK": invalid identifier . Any ideas on how to achieve this?
This is a problem of scoping. Oracle does not recognize the outer query alias more than one level of nesting deep.
If we assume that values are in both tables, then you can use LEAST() with subqueries:
UPDATE table1 t
SET t.columnA = LEAST( (SELECT MIN(columnB)
FROM table2
WHERE table2.fk = t.pk
),
(SELECT MIN(columnB)
FROM table3
WHERE table2.fk = t.pk
)
);
If not, you can modify your query by moving the correlation clause out one level:
UPDATE table1 t
SET t.columnA = (SELECT MIN(columnB)
FROM ((SELECT table2.fk, columnB FROM table2
) UNION ALL
(SELECT table3.fk, columnB FROM table3
)
) tt
WHERE tt.fk = t.pk
);

loop based on query result get error

I have the result of a first query, and then based on that result I want to join with other tables so I can select the fields in the query result and other tables.
I construct the following
BEGIN
FOR a_rec in (select col1, min(col2) as col2,col3 from a_tbl group by (col1, col3))
LOOP
select a_rec.col2, d_tbl.col4
from b_tbl b, c_tbl c, d_tbl d
where b.col1 = a_rec.col1 and c.col5 = b.col5 and d.col6 = c.col6;
END LOOP;
END;
then it reminds me that PLS-00428: an INTO clause is expected in this SELECT statement, so I create a temp table and the code becomes:
BEGIN
FOR a_rec in (select col1, min(col2) as col2,col3 from a_tbl group by (col1, col3))
LOOP
select a_rec.col2, d_tbl.col4 into tmp_tbl
from b_tbl b, c_tbl c, d_tbl d
where b.col1 = a_rec.col1 and c.col5 = b.col5 and d.col6 = c.col6;
END LOOP;
END;
then I get error like this PLS-00403: expression 'TMP_TBL' cannot be used as an INTO-target of a SELECT/FETCH statement
Maybe I should put the first query result into a temp table and then join it with other tables, although I wonder whether I can do it in such a procedure?
A SELECT INTO throws an error if the SELECT statement returns anything other than a single row. So that's clearly not what you want here. Since tmp_tbl is a global temporary table, you could do an INSERT SELECT in your loop
BEGIN
FOR a_rec in (select col1, min(col2) as col2,col3
from a_tbl
group by (col1, col3))
LOOP
INSERT INTO tmp_tbl( col2, col4 )
select a_rec.col2, d_tbl.col4
from b_tbl b,
c_tbl c,
d_tbl d
where b.col1 = a_rec.col1
and c.col5 = b.col5
and d.col6 = c.col6;
END LOOP;
END;
Since you say that you are just turning around and exporting the resulting data in SQL Developer, however, the temporary table doesn't seem particularly useful. You could remove all the PL/SQL and just write a single SELECT statement
select a.col2, d.col4
from (select col1, min(col2) as col2, col3
from a_tbl
group by col1, col3) a,
b_tbl b,
c_tbl c,
d_tbl d
where b.col1 = a.col1
and c.col5 = b.col5
and d.col6 = c.col6;

Multiple select for aleady joined table

Below is a part of my select query. In the same query I am selecting COLUMN_1 from a table TABLE2 with condition check. Also I am joining this table at end with one of the condition in the inner select as below. Can we have any other way to handle this situation with out using multiple `SELECT inside.
SELECT
T1.COLUMN_1
, (SELECT COLUMN_1 FROM TABLE2 WHERE COLUMN_22 ='A' AND COLUMN_11=T2.COLUMN_11)
, T1.COLUMN_2
, (SELECT COLUMN_1 FROM TABLE2 WHERE COLUMN_22 ='B' AND COLUMN_11=T2.COLUMN_11)
, T1.COLUMN_3
, (SELECT COLUMN_1 FROM TABLE2 WHERE COLUMN_22 ='C' AND COLUMN_11=T2.COLUMN_11)
, T1.COLUMN_4
, (SELECT COLUMN_1 FROM TABLE2 WHERE COLUMN_22 ='D' AND COLUMN_11=T2.COLUMN_11)
, T1.COLUMN_5
, (SELECT COLUMN_1 FROM TABLE2 WHERE COLUMN_22 ='E' AND COLUMN_11=T2.COLUMN_11)
, T1.COLUMN_6
, (SELECT COLUMN_1 FROM TABLE2 WHERE COLUMN_22 ='F' AND COLUMN_11=T2.COLUMN_11)
FROM TABLE1 T1, TABLE2 T2
-- plus two more tables
--plus some other conditions
WHERE T1.COLUMN_11=T2.COLUMN_11
Use CASE instead:
SELECT T1.COLUMN_1
,CASE
WHEN T2.COLUMN_22 = 'A'
THEN T2.COLUMN_1
END
,T1.COLUMN_2
,CASE
WHEN T2.COLUMN_22 = 'B'
THEN T2.COLUMN_1
END
,T1.COLUMN_3
,CASE
WHEN T2.COLUMN_22 = 'C'
THEN T2.COLUMN_1
END
,T1.COLUMN_4
,CASE
WHEN T2.COLUMN_22 = 'D'
THEN T2.COLUMN_1
END
,T1.COLUMN_5
,CASE
WHEN T2.COLUMN_22 = 'E'
THEN T2.COLUMN_1
END
,T1.COLUMN_6
,CASE
WHEN T2.COLUMN_22 = 'F'
THEN T2.COLUMN_1
END
FROM TABLE1 T1
INNER JOIN TABLE2 T2 ON T1.COLUMN_11 = T2.COLUMN_11;
EDIT
I changed the query to use the ansi join syntax. But that change is irrelevant to what you are asking. You can keep your join syntax if you want. The only relevant change is in the SELECT portion of the query.
You won't have very clean solutions I think. Another possibility is with an inner join by case:
SELECT
T1.COLUMN_1,
T2_1.COLUMN_1,
T1.COLUMN_2,
T2_2.COLUMN_1
T1.COLUMN_3,
T2_3.COLUMN_1
T1.COLUMN_4,
T2_4.COLUMN_1
T1.COLUMN_5,
T2_5.COLUMN_1
T1.COLUMN_6,
T2_6.COLUMN_1
FROM TABLE1 T1
INNER JOIN TABLE2 T2_1 ON T1.COLUMN_11=T2_1.COLUMN_11 AND T2_1.COLUMN_22 = 'A',
INNER JOIN TABLE2 T2_2 ON T1.COLUMN_11=T2_2.COLUMN_11 AND T2_2.COLUMN_22 = 'B',
INNER JOIN TABLE2 T2_3 ON T1.COLUMN_11=T2_3.COLUMN_11 AND T2_3.COLUMN_22 = 'C',
INNER JOIN TABLE2 T2_4 ON T1.COLUMN_11=T2_4.COLUMN_11 AND T2_4.COLUMN_22 = 'D',
INNER JOIN TABLE2 T2_5 ON T1.COLUMN_11=T2_5.COLUMN_11 AND T2_5.COLUMN_22 = 'E',
INNER JOIN TABLE2 T2_6 ON T1.COLUMN_11=T2_6.COLUMN_11 AND T2_6.COLUMN_22 = 'F',
WHERE
etc...

Resources