What is the use of + in oracle join [duplicate] - oracle

This question already has answers here:
Difference between Oracle's plus (+) notation and ansi JOIN notation?
(8 answers)
Is there an Oracle official recommendation on the use of explicit ANSI JOINs vs implicit joins?
(2 answers)
Closed 17 days ago.
What is meant by + sign right after column name while join in oracle .
Example :
select a,d,f,c
From table1, table 2
Where table1.x(+) = table2.y:

The (+) is used to indicate an outer join. It is placed on the side where the row in the table may be missing. That is old Oracle syntax I have known since Oracle 7. Presently, even Oracle encourages using ANSI syntax for joins.

That's the old Oracle's outer join operator.
Old:
SQL> SELECT d.deptno, d.dname, e.ename
2 FROM dept d, emp e
3 WHERE e.deptno (+) = d.deptno
4 ORDER BY d.deptno, e.ename;
DEPTNO DNAME ENAME
---------- -------------- ----------
10 ACCOUNTING CLARK
10 ACCOUNTING KING
10 ACCOUNTING MILLER
20 RESEARCH ADAMS
20 RESEARCH FORD
20 RESEARCH JONES
20 RESEARCH SCOTT
20 RESEARCH SMITH
30 SALES ALLEN
30 SALES BLAKE
30 SALES JAMES
30 SALES MARTIN
30 SALES TURNER
30 SALES WARD
40 OPERATIONS
15 rows selected.
Nowadays, we do it as
SQL> SELECT d.deptno, d.dname, e.ename
2 FROM dept d LEFT JOIN emp e ON e.deptno = d.deptno
3 ORDER BY d.deptno, e.ename;
DEPTNO DNAME ENAME
---------- -------------- ----------
10 ACCOUNTING CLARK
10 ACCOUNTING KING
10 ACCOUNTING MILLER
20 RESEARCH ADAMS
20 RESEARCH FORD
20 RESEARCH JONES
20 RESEARCH SCOTT
20 RESEARCH SMITH
30 SALES ALLEN
30 SALES BLAKE
30 SALES JAMES
30 SALES MARTIN
30 SALES TURNER
30 SALES WARD
40 OPERATIONS
15 rows selected.
SQL>

Related

Why break command didnt work in sql oracle?

I need to make a job application report, I need to produce a table where the duplicated job_id and job_name will be removed from the table
And I actually want to compute total applicants who apply for the job and then I need to get remaining job vacancies (no_of_vacancies - count()) and get the percentage by (count()/no_of_vacancies*100)
Applicant ID
Job ID
Job Name
No of Vacancies
Remaining Job Vacancies
Percentage(%)
A0001
Clinical Specialist
J0001
30
24
20
A0072
A0076
but I could only get this
Applicant ID
Job ID
Job Name
No of Vacancies
Remaining Job Vacancies
Percentage(%)
A0001
Clinical Specialist
J0001
30
29
3
A0072
Clinical Specialist
J0001
30
29
3
A0076
Clinical Specialist
J0001
30
29
3
The duplicated job id and name, no of vacancies and remaining vacancies is still there, and the percentage they shown is incorrect too
Here's the code:
SET pagesize 30
ALTER SESSION SET NLS_DATE_FORMAT = 'DD-MON-YYYY';
ACCEPT v_jobID CHAR FORMAT 'A5' PROMPT ' Enter job id: '
COLUMN job_id FORMAT A12 HEADING "Job ID";
COLUMN job_name FORMAT A20 HEADING "Job Name";
COLUMN applicant_id FORMAT A12 HEADING "Applicant ID";
COLUMN no_of_vacancies FORMAT 999 HEADING "No of Vacancies";
COLUMN Remaining_Job_Vacancies FORMAT 999 HEADING "Remaining Job Vacancies";
COLUMN Percentage FORMAT 999 HEADING "Percentage(%)";
BREAK on J.job_id on job_name skip2 on no_of_vacancies, Remaining_Job_Vacancies, Percentage;
COMPUTE number LABEL 'Total Applicants' ON A.applicant_id;
TTITLE CENTER 'Job Application Report for ' _DATE -
RIGHT 'Page No: ' FORMAT 999 SQL.PNO SKIP 2
SELECT A.applicant_id, J.job_id, job_name, no_of_vacancies, (no_of_vacancies - count(*)) AS Remaining_Job_Vacancies, (count(*)/no_of_vacancies*100) AS Percentage
FROM applicant A, job J, application AP
WHERE A.applicant_id = AP.applicant_id
AND AP.job_id = J.job_id
AND J.job_id LIKE '&v_jobID'
GROUP BY A.applicant_id , J.job_id, job_name, no_of_vacancies
ORDER BY J.job_id;
--CLEAR COLUMNS
--TTITLE OFF
That's because you don't break on table_alias.column_name
BREAK on J.job_id
--
^
|
this
but only on column name (or its alias):
BREAK on job_id
I don't have your tables nor data, so I'll use Scott's sample schema to illustrate it.
This is what you did:
SQL> break on e.deptno
SQL>
SQL> select e.deptno, e.ename from emp e order by e.deptno;
DEPTNO ENAME
---------- ----------
10 CLARK
10 KING
10 MILLER
20 JONES
20 FORD
20 ADAMS
20 SMITH
20 SCOTT
30 WARD
30 TURNER
30 ALLEN
30 JAMES
30 BLAKE
30 MARTIN
14 rows selected.
This is what you should have done:
SQL> break on deptno
SQL>
SQL> select e.deptno, e.ename from emp e order by e.deptno;
DEPTNO ENAME
---------- ----------
10 CLARK
KING
MILLER
20 JONES
FORD
ADAMS
SMITH
SCOTT
30 WARD
TURNER
ALLEN
30 JAMES
BLAKE
MARTIN
14 rows selected.
SQL>

Solutions for outer join operator (+) not allowed in operand like OR or IN

I have tried the following query but I still get this error and I am not able to find a solution to resolve it. I want to be able to get one of the three dates (LOAD,LAST_MODIFIED,ORDER_ITEM_LAST_UPDATE) according to the situation. However, I need also to use the actual calendar date to use a specific column inside. It is why I do the join (+) with the LOAD_DATE. Now, I try to find a way not to get the error ORA-01719, Someone can help me?
ORA-01719: outer join operator (+) not allowed in operand of OR or IN
01719. 00000 - "outer join operator (+) not allowed in operand of OR or IN"
*Cause: An outer join appears in an or clause.
*Action: If A and B are predicates, to get the effect of (A(+) or B),
try (select where (A(+) and not B)) union all (select where (B)).
Error at Line: 17 Column: 35
CODE:
SELECT A.PRODUCT_ID AS KEY,
TO_CHAR(A.ORDER_DATE, 'MM/DD/YYYY') AS ORDER_CREATION_DATE,
TO_CHAR(B.LOAD_DATE, 'MM/DD/YYYY') AS LOAD_DATE,
TO_CHAR(B.LAST_MODIFIED_DATE, 'MM/DD/YYYY') AS LAST_MODIFIED_DATE,
TO_CHAR(B.ORDER_ITEM_LAST_UPDATED, 'MM/DD/YYYY') AS ORDER_ITEM_LAST_UPDATED,
A.ORDER_STATUS AS ORDER_STATUS,
A.ORDER_ITEM_STATUS AS ORDER_ITEM_STATUS,
A.ORDER_ACTION AS ORDER_ACTION,
NVL(B.ORDER_ITEM_ACTION, 'NOT APPLICABLE') AS ORDER_ITEM_ACTION,
NVL(B.CEASE_REASON, 'NOT APPLICABLE') AS ORDER_CEASE_REASON,
A.PRO_SYSTEM AS PRO_SYSTEM,
CASE WHEN (A.PRO_SYSTEM = 'X') THEN 'Y' ELSE 'Z' END AS FLOW
FROM PART1 A, PART2 B,DATA_REP_CALENDAR CAL
WHERE A.ORDER_SEQ = B.ORDER_SEQ
AND TRUNC(B.LOAD_DATE) = CAL.ACTUAL_CALENDAR_DATE (+)
AND TRUNC(B.LOAD_DATE) BETWEEN ADD_MONTHS( TO_DATE('07/01/2019', 'MM/DD/YYYY'),- 6) and LAST_DAY( TO_DATE('07/01/2019', 'MM/DD/YYYY'))
OR TRUNC(B.LAST_MODIFIED_DATE) BETWEEN ADD_MONTHS( TO_DATE('07/01/2019', 'MM/DD/YYYY'),- 6) and LAST_DAY( TO_DATE('07/01/2019', 'MM/DD/YYYY'))
OR TRUNC(B.ORDER_ITEM_LAST_UPDATED) BETWEEN ADD_MONTHS( TO_DATE('07/01/2019', 'MM/DD/YYYY'),- 6) and LAST_DAY( TO_DATE('07/01/2019', 'MM/DD/YYYY'));
A simple example based on Scott's EMP and DEPT tables; there's department 40, and nobody works in it. If you want to display it (while joining those tables), use outer join. The "old" Oracle outer join operator is (+) which has its ... drawbacks. If you use outer join, many things (that won't work with (+)) will now work.
This is what you have now:
SQL> select d.deptno, d.dname, e.ename
2 from dept d,
3 emp e
4 where d.deptno = e.deptno (+)
5 order by d.deptno, e.ename;
DEPTNO DNAME ENAME
---------- -------------- ----------
10 ACCOUNTING CLARK
10 ACCOUNTING KING
10 ACCOUNTING MILLER
20 RESEARCH ADAMS
20 RESEARCH FORD
20 RESEARCH JONES
20 RESEARCH SCOTT
20 RESEARCH SMITH
30 SALES ALLEN
30 SALES BLAKE
30 SALES JAMES
30 SALES MARTIN
30 SALES TURNER
30 SALES WARD
40 OPERATIONS
15 rows selected.
SQL>
This is how you should rewrite it:
SQL> select d.deptno, d.dname, e.ename
2 from dept d left join emp e on e.deptno = d.deptno
3 order by d.deptno, e.ename;
DEPTNO DNAME ENAME
---------- -------------- ----------
10 ACCOUNTING CLARK
10 ACCOUNTING KING
10 ACCOUNTING MILLER
20 RESEARCH ADAMS
20 RESEARCH FORD
20 RESEARCH JONES
20 RESEARCH SCOTT
20 RESEARCH SMITH
30 SALES ALLEN
30 SALES BLAKE
30 SALES JAMES
30 SALES MARTIN
30 SALES TURNER
30 SALES WARD
40 OPERATIONS
15 rows selected.
SQL>

Can it be possible to use where clause in order by condition oracle?

Actually I have a birthday table and daily wise I am showing list of birthdays to all of our offices. But I want to show the birthdays of the employees first to their office employees and then others. My query is below:-
select *
from TBL_VIEW_EMPL_MST
where ltrim(TO_CHAR(TO_DATE(BIRTHDATE,'dd/mm/yyyy'),'dd-mm'),'0') =
ltrim(TO_CHAR(sysdate,'dd-mm'),'0')
order by office_code DESC;
I think I understand what you are asking.
Depending on who executes the query, you'd want to display employees who work in his/her department first, and then other departments. If that's so, an example based on Scott's sample schema.
EMP table contents:
SQL> select deptno, ename, job from emp order by deptno, ename;
DEPTNO ENAME JOB
---------- ---------- ---------
10 CLARK MANAGER
10 KING PRESIDENT
10 MILLER CLERK
20 ADAMS CLERK
20 FORD ANALYST
20 JONES MANAGER
20 SCOTT ANALYST
20 SMITH CLERK
30 ALLEN SALESMAN
30 BLAKE MANAGER
30 JAMES CLERK
30 MARTIN SALESMAN
30 TURNER SALESMAN
30 WARD SALESMAN
14 rows selected.
SQL>
A query you might be looking for:
I'm using a parameter to enter employee's name; you might be using USER or something else (a function, perhaps? Can't tell)
the MY_DEPT CTE returns par_my_name employee's department number
note the ORDER BY clause which utilizes my_dept's result
For SCOTT, who works in DEPTNO = 20, this department will be sorted first:
SQL> with my_dept as
2 (select deptno
3 from emp
4 where ename = '&par_my_name'
5 )
6 select e.deptno, e.ename, e.job
7 from emp e cross join my_dept m
8 order by case when e.deptno = m.deptno then 1
9 else 2
10 end,
11 e.deptno;
Enter value for par_my_name: SCOTT
DEPTNO ENAME JOB
---------- ---------- ---------
20 SMITH CLERK
20 FORD ANALYST
20 ADAMS CLERK
20 JONES MANAGER
20 SCOTT ANALYST
10 CLARK MANAGER
10 KING PRESIDENT
10 MILLER CLERK
30 ALLEN SALESMAN
30 TURNER SALESMAN
30 WARD SALESMAN
30 MARTIN SALESMAN
30 JAMES CLERK
30 BLAKE MANAGER
14 rows selected.
For MARTIN, department 30 goes first:
SQL> /
Enter value for par_my_name: MARTIN
DEPTNO ENAME JOB
---------- ---------- ---------
30 JAMES CLERK
30 ALLEN SALESMAN
30 WARD SALESMAN
30 TURNER SALESMAN
30 MARTIN SALESMAN
30 BLAKE MANAGER
10 MILLER CLERK
10 CLARK MANAGER
10 KING PRESIDENT
20 JONES MANAGER
20 ADAMS CLERK
20 SMITH CLERK
20 FORD ANALYST
20 SCOTT ANALYST
14 rows selected.
SQL>

Rank the managers based on how many employees they have

[Update]
Thanks for all the comments!
Appreciated!
I solved this by the code below, after referring to all of your posts.
I did not put into account of hierarchy but I will for the later works.
Thank you!
SELECT m.first_name
, m.last_name
, RANK() over (partition by (select COUNT(e.employee_id)
from DB1_employee e)
ORDER BY e.employee_id DESC) AS RANK from DB2_manager m LEFT JOIN RITDB_employee e ON m.employee_id=e.manager
I am having difficulty to troubleshoot my code to rank the managers
based on the number of employees they have. The error is "missing right parenthesis". Any helps would be highly appreciated. Thanks!
SELECT m.first_name
, m.last_name
, RANK() over (partition by (select COUNT(e.employee_id)
from DB1_employee e) AS NUM_EMP
ORDER BY NUM_EMP DESC) AS RANK
from DB2_manager m
, DB1_employee e
group by m.first_name, m.last_name
ORDER BY RANK
An example based on Scott's schema (as I don't have your tables nor data), so that it is easier to see what should be returned.
SQL> select mgr, empno, ename, job from emp order by mgr;
MGR EMPNO ENAME JOB
---------- ---------- ---------- ---------
7566 7902 FORD ANALYST --> MGR 7566 (Jones) has 2 employees
7566 7788 SCOTT ANALYST
7698 7900 JAMES CLERK --> 7698 (Blake) has 5 employees
7698 7499 ALLEN SALESMAN
7698 7521 WARD SALESMAN
7698 7844 TURNER SALESMAN
7698 7654 MARTIN SALESMAN
7782 7934 MILLER CLERK --> 7782 (Clark) has 1 employee
7788 7876 ADAMS CLERK --> 7788 (Scott) has 1 employee
7839 7698 BLAKE MANAGER --> 7839 (King) has 3 employees
7839 7566 JONES MANAGER
7839 7782 CLARK MANAGER
7902 7369 SMITH CLERK --> 7902 (Ford) has 1 employee
7839 KING PRESIDENT
14 rows selected.
SQL>
So:
SQL> select m.ename mgrname,
2 count(*) cnt,
3 dense_rank() over (order by count(*) desc) rnk
4 from emp e join emp m on e.mgr = m.empno
5 where e.mgr is not null
6 group by e.mgr, m.ename
7 order by rnk;
MGRNAME CNT RNK
---------- ---------- ----------
BLAKE 5 1
KING 3 2
JONES 2 3
SCOTT 1 4
FORD 1 4
CLARK 1 4
6 rows selected.
SQL>
However:
That's not correct. Why? Because of hierarchy. Not all managers share the same level. Have a look: King is the president; he is the big boss and manages them all; how can he be ranked as #2? The same goes for the rest of them.
SQL> select lpad(' ', 2 * level - 2) || e.empno ||' '|| e.ename val
2 from emp e
3 connect by prior e.empno = e.mgr
4 start with e.mgr is null;
VAL
--------------------------------------------------------------------
7839 KING
7566 JONES --> Jones has 4 employees!!! (Scott, Adams, Ford and Smith)
7788 SCOTT --> Scott has 1 employee (that's Adams)
7876 ADAMS
7902 FORD --> Ford has 1 employee (that's Smith)
7369 SMITH
7698 BLAKE
7499 ALLEN
7521 WARD
7654 MARTIN
7844 TURNER
7900 JAMES
7782 CLARK
7934 MILLER
14 rows selected.
SQL>
Therefore, we need another approach. Begin with a simple query which, basically, returns "root" for each of them:
SQL> select connect_by_root(ename) manager
2 from emp
3 connect by prior empno = mgr;
MANAGER
----------
SCOTT
SCOTT
FORD
FORD
ALLEN
JAMES
<snip>
KING
KING
39 rows selected.
SQL>
It is further used as a source for
SQL> select x.mgrname,
2 count(*) - 1 cnt
3 from (select connect_by_root(e.ename) mgrname
4 from emp e
5 connect by prior e.empno = e.mgr
6 ) x
7 group by x.mgrname;
MGRNAME CNT
---------- ----------
ALLEN 0
JONES 4
FORD 1
MILLER 0
CLARK 1
WARD 0
SMITH 0
SCOTT 1
TURNER 0
MARTIN 0
ADAMS 0
JAMES 0
BLAKE 5
KING 13
14 rows selected.
SQL>
Finally, remove those that don't have any employees and rank them; the result is quite different than the first (the most obvious, but probably wrong) approach:
SQL> select r.mgrname,
2 r.cnt,
3 dense_rank() over (order by cnt desc) rnk
4 from (select x.mgrname,
5 count(*) - 1 cnt
6 from (select connect_by_root(e.ename) mgrname
7 from emp e
8 connect by prior e.empno = e.mgr
9 ) x
10 group by x.mgrname
11 ) r
12 where r.cnt > 0
13 order by rnk;
MGRNAME CNT RNK
---------- ---------- ----------
KING 13 1
BLAKE 5 2
JONES 4 3
SCOTT 1 4
FORD 1 4
CLARK 1 4
6 rows selected.
SQL>
First of all
Your query is doing a cross join, and then grouping by manager name. There can be an issue if two managers have the same name. Better to use PK and FK.
Don't use old join syntax
NUM_EMP. has no reference in your query and still used in the ORDER BY.
I am considering that DB1_employee table must have some column referring to its manager(let's say, MANAGER_FK ). So writing the query accordingly as following:
SELECT
M.FIRST_NAME,
M.LAST_NAME,
RANK() OVER(
ORDER BY
E.EMPLOYEE_CNT DESC
) AS RNK
FROM
DB2_MANAGER M
JOIN (
SELECT
MANAGER_FK,
COUNT(1) AS EMPLOYEE_CNT
FROM
DB1_EMPLOYEE
GROUP BY
MANAGER_FK
) E ON ( M.MANAGER_ID = E.MANAGER_FK )
ORDER BY
RNK;
Cheers!!
try:
SELECT m.first_name
, m.last_name
, RANK() over (partition by (select COUNT(e.employee_id)
from DB1_employee e)
ORDER BY NUM_EMP DESC) AS RANK
from DB2_manager m
, DB1_employee e
group by m.first_name, m.last_name
ORDER BY RANK
;

Oracle Conditional where clause

is there any way to write query with following functionality, add where clause as a conditional way,
select e.emp_id, emp.admin_user from employees e
if emp.admin != 'Y'
then
query run with where clause
else
query run without where clause ?
Using a CASE expression in the WHERE clause should do the trick. When you say you don't need the where clause if condition is not met, then all you want is a condition like WHERE 1 = 1, i.e. when condition is not met then return all rows. So, you need to make the not met condition as always TRUE.
For example,
I have an employee table,
SQL> SELECT empno, ename, deptno
2 FROM emp;
EMPNO ENAME DEPTNO
---------- ---------- ----------
7369 SMITH 20
7499 ALLEN 30
7521 WARD 30
7566 JONES 20
7654 MARTIN 30
7698 BLAKE 30
7782 CLARK 10
7788 SCOTT 20
7839 KING 10
7844 TURNER 30
7876 ADAMS 20
7900 JAMES 30
7902 FORD 20
7934 MILLER 10
14 rows selected.
SQL>
I want to select the employee details, if department is 20 then use the where clause else return all the employee details, but filter the department which meets the where condition.
SQL> SELECT empno, ename, deptno
2 FROM emp
3 WHERE ename =
4 CASE
5 WHEN deptno = 20
6 THEN 'SCOTT'
7 ELSE ename
8 END
9 /
EMPNO ENAME DEPTNO
---------- ---------- ----------
7499 ALLEN 30
7521 WARD 30
7654 MARTIN 30
7698 BLAKE 30
7782 CLARK 10
7788 SCOTT 20
7839 KING 10
7844 TURNER 30
7900 JAMES 30
7934 MILLER 10
10 rows selected.
SQL>
So, for department 20, the filter is applied by where clause, and I get only the row for ename SCOTT, for others it returns all the rows.
To keep it simple I would go for union clause in this case, so you can have your where clause as complex as you need. I tried to guess your table structure from above comment, let's see this example:
SQL> create table employees (emp_id number, admin_user number, project_id number);
Table created.
SQL> create table project_accessible_to_user (emp_id number, project_id number);
Table created.
Now make simple union all of two queries one with where condition anoother without it
SQL> select * from employees e where e.admin_user!='Y' and project_id in
(select project_id from project_accessible_to_user where emp_id=e.emp_id)
union all
select * from employees e where (e.admin_user is null or
e.admin_user='Y');
UNION ALL is better from performance point of view as UNION because it means that it is not checking for intersect values so if there are any it will return duplicates. However in this case it is filtered already by condition on admin_user, so these duplicates will not occure.

Resources