updating variables in oracle Case statement - sql

I am trying to create a column that records the number of times a profession (doctor, in this case) has occurred in previous rows. Obviously, the code below does not work.
var row_d number
exec :row_d:=0
select case
when occupation='Doctor' then (row_d:=row_d+1)
end row_number
from occupations
;
Since I am new to coding, it is more important that I understand the way that Oracle handles variables in this case than that I have a piece of code that does what I am trying to do.
Ultimately, my question is: how do you update the value of variables in Oracle in general, and is there a way to do it from within a Case statement?

Here is how you can get the result you want. Note that there are no variables involved. SQL is a declarative language: your code describes only the result you want, the code is not made up of specific instructions telling the computer HOW to get the result you want.
I am using the EMP table in the standard SCOTT schema; you can adapt for your needs.
select empno, ename, job,
count(case when job = 'MANAGER' then 1 end) over (order by empno) mgr_running_ct
from scott.emp
order by empno
;
EMPNO ENAME JOB MGR_RUNNING_CT
---------- ---------- --------- --------------
7369 SMITH CLERK 0
7499 ALLEN SALESMAN 0
7521 WARD SALESMAN 0
7566 JONES MANAGER 1
7654 MARTIN SALESMAN 1
7698 BLAKE MANAGER 2
7782 CLARK MANAGER 3
7788 SCOTT ANALYST 3
7839 KING PRESIDENT 3
7844 TURNER SALESMAN 3
7876 ADAMS CLERK 3
7900 JAMES CLERK 3
7902 FORD ANALYST 3
7934 MILLER CLERK 3

Related

One query with combining data from two tables and grouping (sql, oracle)

I am trying to write which create a view that shows the number of departments for each location (Loc) and the number of employees working in the departments in one query.
Tables are provided below.
Table Dept
DEPTNO
DNAME
LOC
10
ACCOUNTING
NEW YORK
20
RESEARCH
DALLAS
30
SALES
CHICAGO
40
OPERATIONS
BOSTON
50
RISK
BOSTON
Table Emp
EMPNO ENAME JOB MGR HIREDATE SAL COMM DEPTNO
---------- ---------- --------- ---------- --------- ---------- ---------- ----------
9901 Kowalski SALESMAN 9345 23-JAN-90 300 100 12
7369 SMITH CLERK 7902 17-MAR-80 15355.58 20
7499 ALLEN SALESMAN 7698 20-MAR-81 1600 300 30
7521 WARD SALESMAN 7698 22-MAR-81 1250 500 30
7566 JONES MANAGER 7839 02-MAR-81 57103.26 20
7654 MARTIN SALESMAN 7698 28-MAR-81 1250 1400 30
7698 BLAKE MANAGER 7839 01-MAR-81 2850 30
7782 CLARK MANAGER 7839 09-MAR-81 2450 10
7788 SCOTT ANALYST 7566 09-MAR-82 57583.15 20
7839 KING PRESIDENT 17-MAR-81 5000 10
7844 TURNER SALESMAN 7698 08-MAR-81 1500 0 30
7876 ADAMS CLERK 7788 12-MAR-83 21113.82 20
7900 JAMES CLERK 7698 03-MAR-81 950 30
7902 FORD ANALYST 7566 03-MAR-81 57583.15 20
7934 MILLER CLERK 7782 23-MAR-82 1300 10
The output should be
LOC NO_DEP NO_EMP
------------- ---------- ----------
NEW YORK 1 3
DALLAS 1 1
CHICAGO 1 5
BOSTON 2 6
So far I was able to create two separate queries which are provided below and which gives this result however I must do it in a one query which will give output in one table.
SELECT DISTINCT d.Loc, Count(d.Deptno) AS No_Dep, d.Deptno
FROM Dept d
GROUP BY d.Loc, d.Deptno;
SELECT DISTINCT COUNT(e.Empno) OVER (PARTITION BY e.Deptno) AS No_Emp, e.Deptno
FROM Emp e;
Correct solution is
SELECT d.LOC, COUNT(DISTINCT d.DEPTNO) AS NO_DEP, COUNT(e.EMPNO) AS NO_EMP
FROM DEPT d
LEFT JOIN EMP e ON d.DEPTNO = e.DEPTNO
GROUP BY D.LOC;
You first need to join them and then and then need to do the aggregation -
SELECT D.LOC, COUNT(DISTINCT DEPTNO) NO_DEP, COUNT(EMPNO) NO_EMP
FROM DEPT D
JOIN EMP E ON D.DEPTNO = E.DEPTNO
GROUP BY D.LOC;
you can use "group by" and "having" to solve such queries
The HAVING clause is used instead of WHERE with aggregate functions. While the GROUP BY Clause groups rows that have the same values into summary rows. The having clause is used with the where clause in order to find rows with certain conditions. The having clause is always used after the group By clause.
example:
select D.LOC AS LOC, COUNT(D.DEPTNO) AS NO_DEP, COUNT(distinct E.EMPNO) AS NO_EMP
from Dept D, Emp E
where D.DEPTNO=E.DEPTNO
group by D.LOC;

How can we handle multiple rows returned from a subquery inside a case statement?

Write a SELECT query if department id provided at run time is 100 then it will return all the employee details those belong to department 100 else i wanted to print all the employees from employee table.
i have written below query :-
SELECT * FROM EMPLOYEES
WHERE department_id IN (
CASE &InputDept
WHEN 100 THEN 100
ELSE (SELECT DISTINCT department_id FROM DEPARTMENTS) END ) ;
This works fine when input is 100 but returned "Single-row subquery returns more than one row".
i understand that case is a statement which works like "if-then-else" with single value, also i tried with decode but no luck.
This can be easily done in PL/SQL , but is this possible with SELECT query?
Skip the case expression, use regular AND/OR instead:
SELECT * FROM EMPLOYEES
WHERE (&InputDept = 100 and department_id = 100)
OR (&InputDept <> 100 and department_id IN
(SELECT department_id FROM DEPARTMENTS)) ;
To handle NULL InputDept:
SELECT * FROM EMPLOYEES
WHERE (&InputDept = 100 and department_id = 100)
OR ((&InputDept <> 100 or &InputDept IS NULL) AND department_id IN
(SELECT department_id FROM DEPARTMENTS)) ;
It's better to use a query like this:
SELECT * FROM EMPLOYEES
WHERE department_id = CASE &InputDept WHEN 100 THEN 100 END
UNION ALL
SELECT * FROM EMPLOYEES
WHERE (&InputDept != 100 or &InputDept is NULL)
AND department_id IN (SELECT DISTINCT department_id FROM DEPARTMENTS);
Do not forget about employees without department_id, ie NULLs.
This query will be optimized by optimizer at runtime to do not execute unneeded part of union all
Yet another alternative (example based on Scott's sample schema, ran in SQL*Plus):
SQL> select *
2 from emp
3 where deptno in (select deptno from dept
4 where &&inputdept <> 10
5 union all
6 select &&inputdept from dual
7 where &&inputdept = 10
8 );
Enter value for inputdept: 10
EMPNO ENAME JOB MGR HIREDATE SAL COMM DEPTNO
---------- ---------- --------- ---------- -------- ---------- ---------- ----------
7782 CLARK MANAGER 7839 09.06.81 2450 10
7839 KING PRESIDENT 17.11.81 5000 10
7934 MILLER CLERK 7782 23.01.82 1300 10
SQL> undefine inputdept
SQL> /
Enter value for inputdept: 25
EMPNO ENAME JOB MGR HIREDATE SAL COMM DEPTNO
---------- ---------- --------- ---------- -------- ---------- ---------- ----------
7369 SMITH CLERK 7902 17.12.80 800 20
7499 ALLEN SALESMAN 7698 20.02.81 1600 300 30
7521 WARD SALESMAN 7698 22.02.81 1250 500 30
7566 JONES MANAGER 7839 02.04.81 2975 20
7654 MARTIN SALESMAN 7698 28.09.81 1250 1400 30
7698 BLAKE MANAGER 7839 01.05.81 2850 30
7782 CLARK MANAGER 7839 09.06.81 2450 10
7788 SCOTT ANALYST 7566 09.12.82 3000 20
7839 KING PRESIDENT 17.11.81 5000 10
7844 TURNER SALESMAN 7698 08.09.81 1500 0 30
7876 ADAMS CLERK 7788 12.01.83 1100 20
7900 JAMES CLERK 7698 03.12.81 950 30
7902 FORD ANALYST 7566 03.12.81 3000 20
7934 MILLER CLERK 7782 23.01.82 1300 10
14 rows selected.
SQL>

SQL - I need make a condition that restrict the select to show only lines that correspond both conditions

I have my select and I need two conditions, they are:
and item.group not in (100)
and item.subgroup not in (120)
what I need is that, the select shows me only items that both conditions match.
In the way that i wrote, items that are from group 90 and subgroup 120 aren't being shown, but they need to be.
Can someone help me? :D
I have no clue how to make both conditions match each other.
You can use:
and not (item.group in (100) and item.subgroup in (120))
Or equivalently, or:
and (item.group not in (100) or item.subgroup not in (120))
Note that both of these might need to be tweaked if the values can be NULL -- depending on how you want the NULL values treated.
How about
where (group, subgroup) not in ((100, 120))
For example (based on Scott's emp table):
SQL> select deptno, ename, job from emp order by deptno, job;
DEPTNO ENAME JOB
---------- ---------- ---------
10 MILLER CLERK
CLARK MANAGER
KING PRESIDENT
20 SCOTT ANALYST
FORD ANALYST
ADAMS CLERK --> omit DEPTNO = 20
SMITH CLERK --> and JOB = CLERK
JONES MANAGER
30 JAMES CLERK
BLAKE MANAGER
MARTIN SALESMAN
WARD SALESMAN
ALLEN SALESMAN
TURNER SALESMAN
14 rows selected.
Let's try it:
SQL> select deptno, ename, job from emp
2 where (deptno, job) not in ((20, 'CLERK'))
3 order by deptno, job;
DEPTNO ENAME JOB
---------- ---------- ---------
10 MILLER CLERK
CLARK MANAGER
KING PRESIDENT
20 SCOTT ANALYST --> right; no Adams nor
FORD ANALYST --> Smith (clerks) in DEPTNO = 20
JONES MANAGER --> any more
30 JAMES CLERK
BLAKE MANAGER
ALLEN SALESMAN
WARD SALESMAN
TURNER SALESMAN
MARTIN SALESMAN
12 rows selected.
SQL>
Try this:
where item.group not in (100, 120) and item.subgroup not in (100, 120)

Compare columns in 2 Oracle 11g tables

I have 2 tables in Oracle 11g one containing the actual source data and the other masked data.
The table has 1260000 rows approximately.
The column I am comparing is Varchar.
I am trying to check if all the data has been masked to value different from the original data.
I am performing a select query with 2 conditions on the join 1. with a unique row identifier 2. the value I am trying to compare.
The query never returns upon execution.
Is there a way to get an output where I can see the rows where the value is the same in both the table ?
This is how I understood the question.
For example, I created a "duplicate" of Scott's EMP table and "masked" names:
SQL> create table emp2 as select * From emp;
Table created.
SQL> update emp2 set ename = translate(ename, 'AEIOU', '98765');
14 rows updated.
SQL> select empno, ename from emp2 order by empno;
EMPNO ENAME
---------- ----------
7369 SM7TH
7499 9LL8N
7521 W9RD
7566 J6N8S
7654 M9RT7N
7698 BL9K8
7782 CL9RK
7788 SC6TT
7839 K7NG
7844 T5RN8R
7876 9D9MS
7900 J9M8S
7902 F6RD
7934 M7LL8R
14 rows selected.
To check which names are different:
SQL> select a.empno, a.ename, b.ename
2 from emp a join emp2 b on a.empno = b.empno
3 where a.ename <> b.ename
4 order by a.empno;
EMPNO ENAME ENAME
---------- ---------- ----------
7369 SMITH SM7TH
7499 ALLEN 9LL8N
7521 WARD W9RD
7566 JONES J6N8S
7654 MARTIN M9RT7N
7698 BLAKE BL9K8
7782 CLARK CL9RK
7788 SCOTT SC6TT
7839 KING K7NG
7844 TURNER T5RN8R
7876 ADAMS 9D9MS
7900 JAMES J9M8S
7902 FORD F6RD
7934 MILLER M7LL8R
14 rows selected.
SQL>
Or, in your case, as you'd want to see values that aren't changed, you'd modify line #3 to
where a.ename = b.ename

Can somebody please push me in right direction [closed]

Closed. This question needs debugging details. It is not currently accepting answers.
Edit the question to include desired behavior, a specific problem or error, and the shortest code necessary to reproduce the problem. This will help others answer the question.
Closed 2 years ago.
Improve this question
My code below to delete duplicate entries
with mytable as
(
select a.*,
row_number() over(partition by emp_id order by emp_id) as Row_number
from employee_Details2 a
)
delete from mytable
where Row_number=2;
Error:
*ORA-00928: missing SELECT keyword
00928. 00000 - "missing SELECT keyword"
*Cause:
*Action:
Error at Line: 43 Column: 1*
You can't delete from a CTE.
If it is about deleting from employee_details2, then see if this helps:
delete from employee_details2 a
where a.rowid > (select min(b.rowid)
from employee_details2 b
where b.empid = a.empid
);
If you want to delete rows using "row number" (as you put it), well - yes, you can do that, but what I suggested originally is simpler and better.
SQL> select * From test order by deptno;
EMPNO ENAME JOB MGR HIREDATE SAL COMM DEPTNO
---------- ---------- --------- ---------- ---------- ---------- ---------- ----------
7782 CLARK MANAGER 7839 06/09/1981 2450 10
7839 KING PRESIDENT 11/17/1981 10000 10
7934 MILLER CLERK 7782 01/23/1982 1300 10
7566 JONES MANAGER 7839 04/02/1981 2975 20
7902 FORD ANALYST 7566 12/03/1981 3000 20
7876 ADAMS CLERK 7788 01/12/1983 1300 20
7369 SMITH CLERK 7902 12/17/1980 920 20
7788 SCOTT ANALYST 7566 12/09/1982 3000 20
7521 WARD SALESMAN 7698 02/22/1981 1250 500 30
7844 TURNER SALESMAN 7698 09/08/1981 1500 0 30
7499 ALLEN SALESMAN 7698 02/20/1981 1600 300 30
7900 JAMES CLERK 7698 12/03/1981 950 30
7698 BLAKE MANAGER 7839 05/01/1981 2850 30
7654 MARTIN SALESMAN 7698 09/28/1981 1250 1400 30
14 rows selected.
SQL> delete from test b
2 where b.empno in (select c.empno
3 from (select a.empno, row_number() over (partition by a.deptno order by a.empno) rn
4 from test a
5 ) c
6 where c.rn > 1
7 );
11 rows deleted.
SQL> select * From test order by deptno;
EMPNO ENAME JOB MGR HIREDATE SAL COMM DEPTNO
---------- ---------- --------- ---------- ---------- ---------- ---------- ----------
7782 CLARK MANAGER 7839 06/09/1981 2450 10
7369 SMITH CLERK 7902 12/17/1980 920 20
7499 ALLEN SALESMAN 7698 02/20/1981 1600 300 30
SQL>
The delete can not work with CTE, You need to separate the logic of DELETE and SELECT as follows:
delete from employee_Details2
where rowid in
(select rowid from
( select a.rowid, a.*,
row_number() over(partition by emp_id order by emp_id) as Row_number
from employee_Details2 a )
where Row_number = 2)