Retrieve data based on condition oracle apex - oracle

I have the following question:
I would like to display all users who have similar names to a specified user.
To do this, I first fetched the data of the specified user and store them in page items.
In the second step, I have created a classic report and would like to list all users here who have a similar name.
How can I say that, for example, only the beginning of the value of this page item must be the same?

If "similarity" means "match first several letters", then you'd
select ...
from table_of_users u
where substr(:P1_USER_NAME, 1, 3) = substr(u.user_name, 1, 3) --> match first 3 letters
However, maybe you'd want to consider another approach and use UTL_MATCH package. For example (cross (self) join of Scott's EMP table):
SQL> SELECT a.ename,
2 b.ename,
3 UTL_MATCH.jaro_winkler_similarity (a.ename, b.ename) similarity
4 FROM emp a CROSS JOIN emp b
5 ORDER BY similarity DESC
6 /
ENAME ENAME SIMILARITY
---------- ---------- ----------
SMITH SMITH 100
MILLER MILLER 100
ALLEN ALLEN 100
FORD FORD 100
JAMES JAMES 100
<snip>
JAMES JONES 76
JONES JAMES 76
JAMES ADAMS 73
CLARK BLAKE 73
BLAKE CLARK 73
<snip>
SCOTT MARTIN 45
JAMES MARTIN 41
MARTIN JAMES 41
TURNER ALLEN 41
ALLEN TURNER 41
FORD JAMES 0
FORD MILLER 0
MILLER WARD 0
MILLER SCOTT 0
<snip>
In your example, that would be e.g.
select ...
from table_of_users u
where utl_match.jaro_winkler_similarity(:P1_USER_NAME, u.user_name) > 80; -- match more than 80%

Related

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

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>

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>

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.

Difference b/w 'HAVING NOT EXISTS' and 'MINUS'

HAVING acts like a where clause and EXISTS checks for the rows that exist for the given row or not. So, when we use HAVING NOT EXISTS it should have the same functionality as MINUS which eliminates the common rows from first table.
But in an example when I substituted HAVING NOT EXISTS for MINUS the result sets are not same. the query is
(
select empno,ename,job,mgr,hiredate,sal,comm,deptno
,
count(*) as cnt
from v
group by empno,ename,job,mgr,hiredate,sal,comm,deptno
minus
( select empno,ename,job,mgr,hiredate,sal,comm,deptno
,
count(*) as cnt
from emp
group by empno,ename,job,mgr,hiredate,sal,comm,deptno)
)
union all
(
select empno,ename,job,mgr,hiredate,sal,comm,deptno
,
count(*) as cnt
from emp
group by empno,ename,job,mgr,hiredate,sal,comm,deptno
minus
( select empno,ename,job,mgr,hiredate,sal,comm,deptno
,
count(*) as cnt
from v
group by empno,ename,job,mgr,hiredate,sal,comm,deptno)
)
V is a view, which is formed like
create or replace view v
as
select * from emp where deptno != 10
union all
select * from emp where ename = 'WARD';
The query is an example to determine whether the tables have same data or not
this is my emp table:
EMPNO ENAME JOB MGR HIREDATE SAL COMM DEPTNO
---------- ---------- --------- ---------- --------- ---------- ---------- ----------
7369 SMITH CLERK 7902 17-DEC-80 800 20
7499 ALLEN SALESMAN 7698 20-FEB-81 1600 300 30
7521 WARD SALESMAN 7698 22-FEB-81 1250 500 30
7566 JONES MANAGER 7839 02-APR-81 2975 20
7654 MARTIN SALESMAN 7698 28-SEP-81 1250 1400 30
7698 BLAKE MANAGER 7839 01-MAY-81 2850 30
7782 CLARK MANAGER 7839 09-JUN-81 2450 10
7788 SCOTT ANALYST 7566 09-DEC-82 3000 20
7839 KING PRESIDENT 17-NOV-81 5000 10
7844 TURNER SALESMAN 7698 08-SEP-81 1500 0 30
7876 ADAMS CLERK 7788 12-JAN-83 1100 20
7900 JAMES CLERK 7698 03-DEC-81 950 30
7902 FORD ANALYST 7566 03-DEC-81 3000 20
7934 MILLER CLERK 7782 23-JAN-82 1300 10
This is the view V
EMPNO ENAME JOB MGR HIREDATE SAL COMM DEPTNO
------- ---------- --------- ---------- --------- ---------- ---------- ----------
7369 SMITH CLERK 7902 17-DEC-80 800 20
7499 ALLEN SALESMAN 7698 20-FEB-81 1600 300 30
7521 WARD SALESMAN 7698 22-FEB-81 1250 500 30
7566 JONES MANAGER 7839 02-APR-81 2975 20
7654 MARTIN SALESMAN 7698 28-SEP-81 1250 1400 30
7698 BLAKE MANAGER 7839 01-MAY-81 2850 30
7788 SCOTT ANALYST 7566 09-DEC-82 3000 20
7844 TURNER SALESMAN 7698 08-SEP-81 1500 0 30
7876 ADAMS CLERK 7788 12-JAN-83 1100 20
7900 JAMES CLERK 7698 03-DEC-81 950 30
7902 FORD ANALYST 7566 03-DEC-81 3000 20
7521 WARD SALESMAN 7698 22-FEB-81 1250 500 30
When minus is used the query results in
EMPNO ENAME JOB MGR HIREDATE SAL COMM DEPTNO CNT
------ ---------- --------- ---------- --------- ---------- ---------- ---------- ----------
7521 WARD SALESMAN 7698 22-FEB-81 1250 500 30 2
7521 WARD SALESMAN 7698 22-FEB-81 1250 500 30 1
7782 CLARK MANAGER 7839 09-JUN-81 2450 10 1
7839 KING PRESIDENT 17-NOV-81 5000 10 1
7934 MILLER CLERK 7782 23-JAN-82 1300 10 1
When HAVING NOT EXISTS is used no rows are resulted from the query
Is there any wrong in my understanding of the terms. Please explain why the result sets are different with a short example.Thanks
This is always going to produce no rows:
select empno,ename,job,mgr,hiredate,sal,comm,deptno,count(*) as cnt
from v
group by empno,ename,job,mgr,hiredate,sal,comm,deptno
having not exists (
select empno,ename,job,mgr,hiredate,sal,comm,deptno, count(*) as cnt
from emp
group by empno,ename,job,mgr,hiredate,sal,comm,deptno
);
There is no correlation between the main select and the data returned by the query in the having clause. Unless emp is empty there will always be something in that clause, so the having not exists has to be false, so all rows in the main query are discarded.
It's nothing to do with your data really; a simplified version does the same:
select * from dual
where not exists (select * from dual);
It's logically impossible for there to be rows in dual, and no rows in dual, at the same time. (OK, if there are no rows in dual you're in big trouble, so maybe this wasn't a great example).
In your case v and emp are related; if emp is empty then v must be too.
The main select in this part finds 11 rows from v. The select inside the having clause finds 14 rows from emp, but it wouldn't matter if it found 1 or 100 rows, as there is no relation between the two sets of rows. The having clause just acts as a filter; having not exists (<query that finds anything>) filters out everything. If you break it down, exists (select ...) is true because there are rows in emp; not exists (select ...) is just the negation of that, so it's false; so you're effectively saying 'give me the rows where true = false'.
The second part of the union finds 14 rows from emp, but the same 'true = false' filter applies, so all rows are still discarded, even though the inner select only finds 11 rows from v. You are not making any connection between the data in v and emp.
But any subquery that always produces rows would have the same effect; because there is no correlation you could be querying a different table which has nothing to do with emp at all, and as long is that subquery returned one or more rows your having clause would filter out everything you expect to see. This behaves the same, for example:
select empno,ename,job,mgr,hiredate,sal,comm,deptno,count(*) as cnt
from v
group by empno,ename,job,mgr,hiredate,sal,comm,deptno
having not exists (
select * from dual
);
For the second part of the union you can get some of the data back by adding the correlation:
select empno,ename,job,mgr,hiredate,sal,comm,deptno,count(*) as cnt
from v
group by empno,ename,job,mgr,hiredate,sal,comm,deptno
having not exists (
select empno,ename,job,mgr,hiredate,sal,comm,deptno, count(*) as cnt
from emp
where emp.empno = v.empno
and emp.ename = v.ename
and emp.job = v.job
and emp.mgr = v.mgr
and emp.hiredate = v.hiredate
and emp.sal = v.sal
and emp.deptno = v.deptno
group by empno,ename,job,mgr,hiredate,sal,comm,deptno
);
EMPNO ENAME JOB MGR HIREDATE SAL COMM DEPTNO CNT
---------- ---------- --------- ---------- --------- ---------- ---------- ---------- ----------
7782 CLARK MANAGER 7839 09-JUN-81 2450 10 1
7934 MILLER CLERK 7782 23-JAN-82 1300 10 1
7839 KING PRESIDENT 17-NOV-81 5000 10 1
Just using empno might work if it's a primary key or unique, unless you'd really be looking for discrepancies between other fields. But correlating the counts between v and emp is tricky, and possibly not worth the effort. Looking at the count at all seems a bit odd; they only differ for WARD because of the contrived way you've built the view. Using minus here is simpler and possibly quicker.

Oracle Cursor Issue

Anyone have any idea how to do the following?
declare cursor
open cursor
fetch cursor
<< Start reading the cursor in a LOOP >>
Lets say the cursor have 10 records.
Read until 5th record then go to the 6th record and do some checking.
Now, is it possible to go back to 5th record from 6th record ?
Depends on the requirements.
You can use the LAG() and LEAD() analytic functions to get information for the next and prior rows, i.e.
SQL> ed
Wrote file afiedt.buf
1 select ename,
2 sal,
3 lead(sal) over (order by ename) next_sal,
4 lag(sal) over (order by ename) prior_sal
5 from emp
6* order by ename
SQL> /
ENAME SAL NEXT_SAL PRIOR_SAL
---------- ---------- ---------- ----------
ADAMS 1100 1600
ALLEN 1600 2850 1100
BLAKE 2850 2450 1600
CLARK 2450 3000 2850
FORD 3000 950 2450
JAMES 950 2975 3000
JONES 2975 5000 950
KING 5000 1250 2975
MARTIN 1250 1300 5000
MILLER 1300 3000 1250
SCOTT 3000 800 1300
ENAME SAL NEXT_SAL PRIOR_SAL
---------- ---------- ---------- ----------
SMITH 800 1500 3000
TURNER 1500 1250 800
WARD 1250 1500
14 rows selected.
If you don't want to use analytic functions, you can use PL/SQL collections, BULK COLLECT the data into those collections (using the LIMIT clause if you have more data than you want to store in your PGA) and then move forward and backward through your collections.
How far do you need to go back? If you only need a look-ahead of one row, you could buffer just the previous row in your loop (application-side).

Resources