Display Data in own order by using union - oracle

I want to display
all emps who are seniors to king and who are juniors to smith as in following order.
who are seniors to king are under king header and juniors to smith are under smith header
I tried this one,
select ename from emp where hiredate<(select hiredate from emp where ename='king')
union
select ename from emp where hiredate>(select hiredate from emp where ename='smith');
OUTPUT is only one header(ENAME)
How can i get my desired output(Two Headers KING SMITH)
Can any one help me

Addding another artificial column is a little trick:
select 1 status, ename from emp
where hiredate<(select hiredate from emp where ename='king')
union all
select 2 status, ename from emp
where hiredate>(select hiredate from emp where ename='smith')
order by 1;
You can even use a union all instead of union since all lines will be different. Or, if you really need headers/separators:
select 0 status, 'KING''S SENIORS' from dual
union all
select 1 status, ename from emp where hiredate<(select hiredate from emp where ename='king')
union all
select 2 status, 'SMITH''S JUNIORS' from dual
union all
select 3 status, ename from emp where hiredate>(select hiredate from emp where ename='smith')
order by 1;

may be this one will be useful
select e.ename,
case when e.hiredate < khd.hiredate then 1 else 0 end king_header,
case when e.hiredate > shd.hiredate then 1 else 0 end smith_juniour
from emp e,
(select hiredate from emp where ename='king') khd,
(select hiredate from emp where ename='smith') shd
where e.hiredate < khd.hiredate or e.hiredate > shd.hiredate

Related

Employees with the lower salary than the average in their dept

I got two challenges on my college exam to do.
The first one is to Show data for employees (name, salary, and department) who are paid less than the average salary for their department.
I tried this:
SELECT ename, sal, deptno
FROM Emp
WHERE sal < (SELECT AVG(sal) FROM EMP)
GROUP BY deptno;
But i get the ORA-00979: not a GROUP BY expression error.
The other one i didn't started but is quite the same, Show the data of employees (name, commission and department) who receive commission greater than the average commission in their department.
ps: I'm starting with SQL so the topic isn't very deep yet.
You can use a window function for this
SELECT ename, sal, deptno
FROM (
SELECT *,
AVG(sal) OVER (PARTITION BY deptno) AS AvgPerDept
FROM Emp
) AS Emp
WHERE sal < AvgPerDept
Your attempt to the first one is not correct
The expression SELECT AVG(sal) FROM EMP will give the average salary of everyone, regardless which department they're in.
To get the average for each deparment, you will need to GROUP BY dept_no
SELECT Emp.* , dept_salary.average_sal
FROM Emp
INNER JOIN
-- join with an average salary for each department
(
SELECT deptno, AVG(sal) as average_sal
FROM Emp
GROUP BY deptno
) dept_salary
ON Emp.deptno = dept_salary.deptno
WHERE Emp.dept_no = dept_salary.deptno AND Emp.sal < dept_salary.average_sal;

How to use a rowcount in select statement to modify the query to fetch data for 10 days , if rowcount is 0 for 5 days?

I need to modify my script using rowcount to check if the data in table or not?. Here, i write the query to select a data for last 5 days from current system date. But sometimes there is no data in table for 5 days. So i need to fetch for 10 day or more.
Query:
Select ep.ENTERPRISE_NAME||'|'||s.id||'|'||s.SUBMISSION_DATE||'|'||E.VALUE
from JOB_SUMMARY_EXT e, ob_summary s, enterprise ep
where e.id = s.id and e.name_res_key = 'Model'
and s.job_id in (select id from job_summary where
trunc(start_date) > trunc(sysdate) -10 and service_name ='Model2' )
I don't know how to modify my Query using rowcount. If rowcount is 0 then i want select data for 10 days.Otherwise it should to fetch for 5 days automatically. I want this to be done as single query.
It looks that you want to select the last 5 "days" from that table. So, why would you anchor to SYSDATE if there aren't rows for each of those days? I'd suggest another approach: literally, select last 5 days. Here's how.
As I don't have your tables, I'm using Scott's EMP table which contains information about employees. It is an ancient one so HIREDATE column is set to 1980s, but never mind that. Sorting employees by HIREDATE in descending order shows:
SQL> alter session set nls_date_format = 'dd.mm.yyyy';
Session altered.
SQL> select ename, hiredate from emp order by hiredate desc;
ENAME HIREDATE
---------- ----------
ADAMS 12.01.1983 1.
SCOTT 09.12.1982 2.
MILLER 23.01.1982 3.
FORD 03.12.1981 4.
JAMES 03.12.1981 4.
KING 17.11.1981 5. --> I want to fetch rows up to KING
MARTIN 28.09.1981
TURNER 08.09.1981
CLARK 09.06.1981
BLAKE 01.05.1981
JONES 02.04.1981
WARD 22.02.1981
ALLEN 20.02.1981
SMITH 17.12.1980
14 rows selected.
SQL>
As you can see, the 4th date is shared by two employees so I want to include them both. DENSE_RANK analytic function helps:
SQL> with last5 as
2 (select ename,
3 job,
4 sal,
5 hiredate,
6 dense_rank() over (order by hiredate desc) rnk
7 from emp
8 )
9 select ename, job, sal, hiredate
10 from last5
11 where rnk <= 5;
ENAME JOB SAL HIREDATE
---------- --------- ---------- ----------
ADAMS CLERK 1100 12.01.1983
SCOTT ANALYST 3000 09.12.1982
MILLER CLERK 1300 23.01.1982
JAMES CLERK 950 03.12.1981
FORD ANALYST 3000 03.12.1981
KING PRESIDENT 5000 17.11.1981
6 rows selected.
SQL>
What does it do? The LAST5 CTE sorts employees (as above), DENSE_RANK ranks them; finally, the last SELECT (which begins at line #9) fetches desired rows.
In your case, that might look like this:
with last5 as
(select id,
dense_rank() over (order by start_date desc) rnk
from job_summary
where service_name = 'Model2'
)
select ep.enterprise_name,
s.id,
s.submission_date,
e.value
from job_summary_ext e
join ob_summary s on e.id = s.id
join last5 t on t.id = s.id
join enterprise ep on <you're missing join condition for this table>
where e.name_res_key = 'Model';
Note that you're missing join condition for the ENTERPRISE table; if that's really so, no problem - you'd use cross join for that table, but I somehow doubt that you want that.
Finally, as you use SQL*Plus, perhaps you don't need to concatenate all columns and separate them by the pipe | sign - set it as a column separator, e.g.
SQL> set colsep '|'
SQL>
SQL> select deptno, dname, loc from dept;
DEPTNO|DNAME |LOC
----------|--------------|-------------
10|ACCOUNTING |NEW YORK
20|RESEARCH |DALLAS
30|SALES |CHICAGO
40|OPERATIONS |BOSTON
SQL>
If you want to
return 10 last days if select count(*) returns 0, or
return 5 last days if select count(*) returns a positive number
then something like this might help (again based on Scott's EMP table):
with
tcnt as
-- count number of rows; use your own requirement, I'm checking
-- whether someone got hired today. In Scott's EMP table, nobody was
-- so CNT = 0
(select count(*) cnt
from emp
where hiredate >= trunc(sysdate)
)
select e.ename, e.job, e.sal, e.hiredate
from emp e cross join tcnt c
where e.hiredate >= case when c.cnt = 0 then trunc(sysdate) - 10
else trunc(sysdate) - 5
end;
Apply it to your tables; I don't know which of those 3 tables' count you want to check.
Tried to add in comments but it was too long for comments and Not clear on count based on but here is case in where clause substitute your count statement with nvl function
SELECT ep.ENTERPRISE_NAME||'|'||s.id||'|'||s.SUBMISSION_DATE||'|'||E.VALUE
FROM JOB_SUMMARY_EXT e,
ob_summary s,
enterprise ep
WHERE e.id = s.id
AND e.name_res_key = 'Model'
AND s.job_id IN
(SELECT id
FROM job_summary
WHERE service='Model'
AND trunc(start_date) >
CASE WHEN
(WRITE your SELECT COUNT criteria WITH NVL FUNCTION)<=0 THEN
trunc(sysdate) -10
ELSE trunc(sysdate)-5
END )

Hide column from Query

I don't want rank column but still, want the data in the same format by applying dense rank.
select ename,position,deptno,dense_rank() over(partition by deptno order by ename asc) as rank from emp where deptno in ('10','30');
Why do you need the rank just order by deptno first then ename.
SELECT ename,position,deptno,
FROM emp
WHERE deptno in ('10','30')
ORDER BY DeptNo, Ename
Using the analytical function two options derived table or CTE
Derived table/inline view.
SELECT ename,position,deptno
FROM (select ename,position,deptno,dense_rank() over(partition by deptno order by ename asc) as rank
from emp
where deptno in ('10','30')) Z
ORDER BY deptNo, rank
Common Table Expression (CTE):
with Z AS (SELECT ename,position,deptno
, dense_rank() over(partition by deptno order by ename asc) as rank
FROM emp
WHERE deptno in ('10','30'))
SELECT ename,position,deptno
FROM z
ORDER BY deptno, rank
Both these last 2 techniques simply avoid exposing the rank function to the outer query in which the results are returned. They are "Tricks" and sub-optimal execution time. unless there's a specific reason to have the rank data; I'd not use it.

LEFT OUTER JOIN with XMLTABLE doesn't work?

Here is the sample query-
WITH empdata AS (SELECT xmltype ('<office>
<emp>
<empno>1</empno>
<ename>Abraham</ename>
<deptno>10</deptno>
</emp>
<emp>
<empno>2</empno>
<ename>Alexander</ename>
<deptno>10</deptno>
</emp>
<emp>
<empno>3</empno>
<ename>Benjamin</ename>
<deptno>20</deptno>
</emp>
<emp>
<empno>4</empno>
<ename>Bradley</ename>
<deptno>20</deptno>
</emp>
</office>') AS xcol FROM dual),
dept AS
(SELECT 10 deptno, 'Accounting' dname FROM dual
UNION ALL
SELECT 20, 'Broking' FROM dual
UNION ALL
SELECT 30, 'HR' FROM dual)
SELECT d.dname, e.ename, e.empno
FROM dept d
CROSS JOIN empdata e_data
LEFT OUTER JOIN
xmltable (
'office/emp'
PASSING e_data.xcol
COLUMNS deptno NUMBER (28, 0) PATH 'deptno',
ename VARCHAR2 (10) PATH 'ename',
empno NUMBER (28, 0) PATH 'empno') e
ON d.deptno = e.deptno;
Result I'm getting-
DNAME ENAME EMPNO
---------- ---------- ----------
Accounting Abraham 1
Accounting Alexander 2
Broking Benjamin 3
Broking Bradley 4
Why isn't the third row from dept, i.e. that of HR isn't showing in the result set? Ideally according to the rules of a LEFT JOIN all the records from the table in the left should show. Why is that one being filtered out?
You have the outer join and cross join the wrong way round. You need to cross-join empdata to xmltable as a subquery, and use that subquery (inline view) as the target for the outer join:
...
SELECT d.dname, e.ename, e.empno
FROM dept d
LEFT OUTER JOIN
(
SELECT x.*
FROM empdata e_data
CROSS JOIN
xmltable (
'office/emp'
PASSING e_data.xcol
COLUMNS deptno NUMBER (28, 0) PATH 'deptno',
ename VARCHAR2 (10) PATH 'ename',
empno NUMBER (28, 0) PATH 'empno') x
) e
ON d.deptno = e.deptno;
DNAME ENAME EMPNO
---------- ---------- ----------
Accounting Abraham 1
Accounting Alexander 2
Broking Benjamin 3
Broking Bradley 4
HR

How can i select just one row in Oracle by row id?

I have used mysql database in my application, but I want to migrate to Oracle.
The problem is in that query:
select * from users limit ?,1;"
That query returns every row one by one depending on ?.
How can i do that in oracle?
On Oracle 12c, you could use the row limiting feature using FETCH FIRST clause.
SQL> SELECT empno, sal, deptno FROM emp ORDER BY empno DESC
2 FETCH FIRST 1 ROWS ONLY;
EMPNO SAL DEPTNO
---------- ---------- ----------
7934 1300 10
SQL>
Prior 12c solution is ROWNUM, however, if you want the row to be first sorted, then you need to do it in a sub-query -
SQL> SELECT empno, sal, deptno FROM
2 ( SELECT * FROM emp ORDER BY empno DESC
3 ) WHERE ROWNUM = 1;
EMPNO SAL DEPTNO
---------- ---------- ----------
7934 1300 10
SQL>
If the order doesn't matter to you, if you just want any random row, simply use ROWNUM.
Depending on your requirement, you could also use ANALYTIC functions such as ROW_NUMBER, RANK, DENSE_RANK.
select * from (select rownum r, u.* from users u ) where r=1;
or if you want it sorted (replace x by columnnumber or columnname):
select * from (select rownum r, u.* from users u order by x) where r=1;

Resources