Need Help in SQL Query [message #644373] |
Wed, 04 November 2015 03:49 |
|
arifs3738
Messages: 39 Registered: November 2015 Location: India
|
Member |
|
|
Q) List employee name, salary and his income group as 'LOW' or 'HIGH' depending on the salary amount. (If the salary is less than 4000 then he is in 'LOW' income group or else in 'HIGH' income group)?
ENAME SALARY LOW HIGH
SCOTT 3000 LOW
KING 5000 HIGH
I have achieved this with CASE STATEMENT as below:
SELECT E.*,
(CASE WHEN SAL < 4000 THEN 'LOW'
ELSE 'HIGH'
END) AS INCOME_GRP
FROM EMP E
ORDER BY INCOME_GRP;
Note: i was asked without case statement how can we write this in SQL. Please help me in writing this small SQL.
Thanks
|
|
|
|
|
|
|
Re: Need Help in SQL Query [message #644379 is a reply to message #644377] |
Wed, 04 November 2015 04:33 |
cookiemonster
Messages: 13920 Registered: September 2008 Location: Rainy Manchester
|
Senior Member |
|
|
Unless there's some other table that holds income groups there is no easy way to do this without using case and decode. You have to use some functions to check the income and output the resulting text.
|
|
|
Re: Need Help in SQL Query [message #644380 is a reply to message #644378] |
Wed, 04 November 2015 04:35 |
cookiemonster
Messages: 13920 Registered: September 2008 Location: Rainy Manchester
|
Senior Member |
|
|
John Watson wrote on Wed, 04 November 2015 10:27Alternatively, you could inlcude a scalar subquery in your column projection list.
How does that avoid decode?
|
|
|
|
|
|
Re: Need Help in SQL Query [message #644386 is a reply to message #644380] |
Wed, 04 November 2015 04:42 |
John Watson
Messages: 8929 Registered: January 2010 Location: Global Village
|
Senior Member |
|
|
cookiemonster wrote on Wed, 04 November 2015 10:35John Watson wrote on Wed, 04 November 2015 10:27Alternatively, you could inlcude a scalar subquery in your column projection list.
How does that avoid decode?
I was thinking of something like select 'low' from emp where sal < 4000 and .... which will return either 'low' or null, and using coalesce to combine the result of that with another scalar subquery that would return 'high' or null.
Arif, you have been given a lot of hints. What have you tried?
|
|
|
|
|
Re: Need Help in SQL Query [message #644390 is a reply to message #644386] |
Wed, 04 November 2015 05:07 |
cookiemonster
Messages: 13920 Registered: September 2008 Location: Rainy Manchester
|
Senior Member |
|
|
John Watson wrote on Wed, 04 November 2015 10:42
I was thinking of something like select 'low' from emp where sal < 4000 and .... which will return either 'low' or null, and using coalesce to combine the result of that with another scalar subquery that would return 'high' or null.
Unless you're worried about sal being null I don't think you need a second subqery
|
|
|
|
|
|
Re: Need Help in SQL Query [message #644404 is a reply to message #644395] |
Wed, 04 November 2015 07:09 |
John Watson
Messages: 8929 Registered: January 2010 Location: Global Village
|
Senior Member |
|
|
Well done! One point - what will you get if sal=4000 ? What if sal is NULL ?
This was my solution:select
e.ename,e.sal,
coalesce(
(select 'low' from emp f where f.empno=e.empno and sal < 4000),
(select 'high' from emp f where f.empno=e.empno and sal >= 4000)
)
from emp e;
If you want top marks for this assignment, you might mention the efficiency of the query. This is thr execution plan I get for your solution,Plan hash value: 1301082189
---------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time |
---------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 14 | 560 | 6 (0)| 00:00:01 |
| 1 | UNION-ALL | | | | | |
|* 2 | TABLE ACCESS FULL| EMP | 8 | 320 | 3 (0)| 00:00:01 |
|* 3 | TABLE ACCESS FULL| EMP | 6 | 240 | 3 (0)| 00:00:01 |
---------------------------------------------------------------------------
Predicate Information (identified by operation id):
---------------------------------------------------
2 - filter("SAL"<4000)
3 - filter("SAL">4000)
orclz>
and this is mine,Execution Plan
----------------------------------------------------------
Plan hash value: 732000419
---------------------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time |
---------------------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 15 | 225 | 18 (0)| 00:00:01 |
|* 1 | TABLE ACCESS BY INDEX ROWID | EMP | 1 | 9 | 1 (0)| 00:00:01 |
|* 2 | INDEX UNIQUE SCAN | PK_EMP | 1 | | 0 (0)| 00:00:01 |
|* 3 | TABLE ACCESS BY INDEX ROWID| EMP | 1 | 9 | 1 (0)| 00:00:01 |
|* 4 | INDEX UNIQUE SCAN | PK_EMP | 1 | | 0 (0)| 00:00:01 |
| 5 | TABLE ACCESS FULL | EMP | 15 | 225 | 3 (0)| 00:00:01 |
---------------------------------------------------------------------------------------
Predicate Information (identified by operation id):
---------------------------------------------------
1 - filter("SAL"<4000)
2 - access("F"."EMPNO"=:B1)
3 - filter("SAL">=4000)
4 - access("F"."EMPNO"=:B1)
orclz>
Which is better? That would depend on the hardware, the size of the tables, whether you can use parallel query, direct or indirect reads, any number of foactors.
|
|
|
Re: Need Help in SQL Query [message #644405 is a reply to message #644404] |
Wed, 04 November 2015 10:16 |
Solomon Yakobson
Messages: 3273 Registered: January 2010 Location: Connecticut, USA
|
Senior Member |
|
|
Another approach:
SQL> explain plan for
2 with t as (
3 select 4000 sal,
4 'HIGH' grade
5 from dual
6 )
7 select e.*,
8 nvl(grade,'LOW') grade
9 from emp e,
10 t
11 where t.sal(+) <= e.sal
12 /
Explained.
SQL> select *
2 from table(dbms_xplan.display)
3 /
PLAN_TABLE_OUTPUT
---------------------------------------------------------------------------------
Plan hash value: 4010907715
----------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time |
----------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 14 | 560 | 6 (17)| 00:00:01 |
| 1 | MERGE JOIN OUTER | | 14 | 560 | 6 (17)| 00:00:01 |
| 2 | SORT JOIN | | 14 | 532 | 4 (25)| 00:00:01 |
| 3 | TABLE ACCESS FULL| EMP | 14 | 532 | 3 (0)| 00:00:01 |
|* 4 | SORT JOIN | | 1 | 2 | 2 (0)| 00:00:01 |
| 5 | TABLE ACCESS FULL| DUAL | 1 | 2 | 2 (0)| 00:00:01 |
PLAN_TABLE_OUTPUT
---------------------------------------------------------------------------------
----------------------------------------------------------------------------
Predicate Information (identified by operation id):
---------------------------------------------------
4 - access(INTERNAL_FUNCTION("E"."SAL")>=CASE WHEN (ROWID(+) IS NOT
NULL) THEN 4000 ELSE NULL END )
filter(INTERNAL_FUNCTION("E"."SAL")>=CASE WHEN (ROWID(+) IS NOT
NULL) THEN 4000 ELSE NULL END )
20 rows selected.
SQL>
This uses just a single pass through EMP table.
SY.
|
|
|
Re: Need Help in SQL Query [message #644545 is a reply to message #644405] |
Mon, 09 November 2015 07:52 |
mnitu
Messages: 159 Registered: February 2008 Location: Reims
|
Senior Member |
|
|
Basically it's the same idea but seems to be cheaper
Select e.*,
Nvl(t.grade,'High') grade
From emp e
Left Join
(Select -1 grp, 'Low' grade from dual) t
On t.grp = sign(sal - 4000)
|
|
|
Re: Need Help in SQL Query [message #644546 is a reply to message #644545] |
Mon, 09 November 2015 08:18 |
Solomon Yakobson
Messages: 3273 Registered: January 2010 Location: Connecticut, USA
|
Senior Member |
|
|
mnitu wrote on Mon, 09 November 2015 08:52Basically it's the same idea but seems to be cheaper
Are you sure it's cheaper?
SQL> declare
2 v_sum number := 0;
3 cursor v_cur
4 is
5 with t as (
6 select 4000 sal,
7 'HIGH' grade
8 from dual
9 )
10 select e.*,
11 nvl(grade,'LOW') grade
12 from emp e,
13 t
14 where t.sal(+) <= e.sal;
15 begin
16 for v_i in 1..100000 loop
17 for v_rec in v_cur loop
18 v_sum := v_sum + v_rec.sal;
19 end loop;
20 end loop;
21 end;
22 /
PL/SQL procedure successfully completed.
Elapsed: 00:00:08.18
SQL> declare
2 v_sum number := 0;
3 cursor v_cur
4 is
5 Select e.*,
6 Nvl(t.grade,'High') grade
7 From emp e
8 Left Join
9 (Select -1 grp, 'Low' grade from dual) t
10 On t.grp = sign(sal - 4000);
11 begin
12 for v_i in 1..100000 loop
13 for v_rec in v_cur loop
14 v_sum := v_sum + v_rec.sal;
15 end loop;
16 end loop;
17 end;
18 /
PL/SQL procedure successfully completed.
Elapsed: 00:00:12.48
SQL>
SY.
|
|
|
Re: Need Help in SQL Query [message #644548 is a reply to message #644546] |
Mon, 09 November 2015 10:22 |
mnitu
Messages: 159 Registered: February 2008 Location: Reims
|
Senior Member |
|
|
I said "seems" because I just compared estimated cpu_cost for your query <cpu_cost>7654027</cpu_cost> with mine <cpu_cost>3863464</cpu_cost> others things being equal.
I can beat your example with a scalar sub-query but that's cheating.
Connected to Oracle Database 11g Enterprise Edition Release 11.2.0.3.0
SQL> set timi on
SQL>
SQL> declare
2 v_sum number := 0;
3 cursor v_cur
4 is
5 with t as (
6 select 4000 sal,
7 'HIGH' grade
8 from dual
9 )
10 select e.*,
11 nvl(grade,'LOW') grade
12 from emp e,
13 t
14 where t.sal(+) <= e.sal;
15 begin
16 for v_i in 1..100000 loop
17 for v_rec in v_cur loop
18 v_sum := v_sum + v_rec.sal;
19 end loop;
20 end loop;
21 end;
22 /
PL/SQL procedure successfully completed
Executed in 16,443 seconds
SQL> declare
2 v_sum number := 0;
3 cursor v_cur
4 is
5 Select e.*,
6 Nvl((Select 'Low' grade from dual where Sign(sal-4000) = -1),'High') grade
7 From emp e;
8 begin
9 for v_i in 1..100000 loop
10 for v_rec in v_cur loop
11 v_sum := v_sum + v_rec.sal;
12 end loop;
13 end loop;
14 end;
15 /
PL/SQL procedure successfully completed
Executed in 13,54 seconds
SQL>
|
|
|
Re: Need Help in SQL Query [message #644550 is a reply to message #644548] |
Mon, 09 November 2015 10:40 |
|
Michel Cadot
Messages: 68641 Registered: March 2007 Location: Nanterre, France, http://...
|
Senior Member Account Moderator |
|
|
Quote:I can beat your example with a scalar sub-query but that's cheating.
I think so as it falls in the case/decode category.
|
|
|