confused by the break command of sqlplus - oracle

From documentation of sqlplus for break
When specify multiple on clause, sqlplus would search from the first on clause to the last on clause for a break. When it finds a break it will perform the actions in reverse order, from the last to first.
I connect to the scott schema and have some test:
SQL> break on deptno skip 1 on job skip 2
SQL> break
break on deptno skip 1 nodup
on job skip 2 nodup
SQL> select deptno, job from emp order by deptno, job;
DEPTNO JOB
---------- ---------
10 CLERK
MANAGER
PRESIDENT
20 ANALYST
DEPTNO JOB
---------- ---------
20 ANALYST
CLERK
MANAGER
DEPTNO JOB
---------- ---------
30 CLERK
MANAGER
SALESMAN
14 rows selected.
SQL>
After print the first row(10, CLERK), there is a change in job, it just skips 2 lines, why not 3 lines ? In the second page, there are tow places sqlplus skip 3 lines. At the end of the report is skips 5 lines. Just don't know how it decides how many lines to skip when specify multiple on clause.

After print the first row(10, CLERK), there is a change in job, it just skips 2 lines, why not 3 lines ?
Why would it skip 3 lines when you have mentioned on job skip 2. SQL*Plus did exactly what you asked it to do, to skip 2 lines.
In the second page, there are tow places sqlplus skip 3 lines.
That is because you also mentioned break on deptno skip 1 So, for a new department, there is one more line skipped. So, 2 lines skipped for job and 1 line skipped for department, thus a total of 3 lines skipped.
Also, for department 20, there are 2 ANALYST and 2 CLERK, so it skips 2 lines for each job.
SQL> break on job skip 2
SQL> select deptno, job from emp order by deptno, job;
DEPTNO JOB
---------- ---------
10 CLERK
10 MANAGER
10 PRESIDENT
20 ANALYST
20
20 CLERK
20
20 MANAGER
30 CLERK
30 MANAGER
30 SALESMAN
30
30
30
14 rows selected.
SQL>
At the end of the report is skips 5 lines.
Because, in the end for department 30, there are 4 SALESMAN, and there will always be an extra line before the total number of rows message is displayed.
SQL> set pagesize 50
SQL> select deptno, job from emp order by deptno, job;
DEPTNO JOB
---------- ---------
10 CLERK
10 MANAGER
10 PRESIDENT
20 ANALYST
20 ANALYST
20 CLERK
20 CLERK
20 MANAGER
30 CLERK
30 MANAGER
30 SALESMAN
30 SALESMAN
30 SALESMAN
30 SALESMAN
14 rows selected.
SQL>
The best way to understand this is, add another column without any skip for example add empno and see the lines which are actually skipped.
SQL> break on deptno skip 1 on job skip 2
SQL> select empno, deptno, job from emp order by deptno, job;
EMPNO DEPTNO JOB
---------- ---------- ---------
7934 10 CLERK
7782 MANAGER
7839 PRESIDENT
7788 20 ANALYST
7902
7876 CLERK
7369
7566 MANAGER
7900 30 CLERK
7698 MANAGER
7654 SALESMAN
7521
7499
7844
14 rows selected.
SQL>

You aren't seeing quite what you think. You're only querying the department and job columns, which can have duplicates - as more than one employee can be in each job. The nodup part of the break setting means duplicates of those two columns will not be shown; which means you are seeing a mix of rows with have both columns blanked as duplicates, and rows that are skipped.
I have a different table but for the same effect:
break on department_id skip 1 on job_id skip 2
select department_id, job_id
from employees
where rownum < 10;
DEPARTMENT_ID JOB_ID
------------- ----------
90 AD_PRES
AD_VP
60 IT_PROG
100 FI_MGR
9 rows selected.
If I add another non-null column, and/or the rownum pseudocolumn, you can see what is really happening:
ROWNUM DEPARTMENT_ID JOB_ID EMPLOYEE_ID
---------- ------------- ---------- -----------
1 90 AD_PRES 100
2 AD_VP 101
3 102
4 60 IT_PROG 103
5 104
6 105
7 106
8 107
9 100 FI_MGR 108
9 rows selected.
Now you can see it skips two lines if the job changes within the department, and three when the department changes. Which is what you asked it to do.
You probably don't want to, but you could also just get the distinct values:
select distinct department_id, job_id ...
DEPARTMENT_ID JOB_ID
90 AD_VP
AD_PRES
60 IT_PROG
100 FI_MGR
4 rows selected.
... and again you'll see two skipped lines between jobs within a department, and three between departments.
You might also be confusing the description of the behaviour in the documentation; you said "When it finds a break" but it can find multiple breaks for one row in the results; the docs you linked to actually say:
SQL*Plus searches each row of output for the specified breaks, starting with the outermost break and proceeding—in the order you enter the clauses—to the innermost.
With your query it will either match department, job, or both.
Next, SQL*Plus executes actions beginning with the action specified for the innermost break and proceeding in reverse order toward the outermost break ... SQL*Plus executes each action up to and including the action specified for the first break encountered in the initial search.
The innermost break in your case is on job, the outermost on department. If the department didn't change but the job did then it will only find a matching break on job, and only the innermost skip (of two rows) will be applied. If the department changed - whether or not the job changed too - it will find a match on that, but will still apply all of the on clause from department onwards, in reverse order; so it will apply the skip 1 from job and the skip 2 from department; hence skipping three lines in total.
Yes I am using rownum without an order by, it happens to work better with this query and my data; don't try this at home

Related

oracle pl/sql-problem /how I can add some dots after the name to make it reach 10 characters

For Oracle SQL problem how I can add some dots after the name to make it reach 10 characters such as ( Sara......)
and add dollar simple $ for every 100 in salary, if the salary= 500 so we need to add 5 simple$
Looks like a RPAD function, e.g.
SQL> select
2 rpad(ename, 10, '.') name,
3 sal,
4 rpad('$', (sal/100), '$') money
5 from emp
6 where deptno = 30;
NAME SAL MONEY
---------- ---------- ------------------------------
ALLEN..... 1600 $$$$$$$$$$$$$$$$
WARD...... 1250 $$$$$$$$$$$$
MARTIN.... 1250 $$$$$$$$$$$$
BLAKE..... 2850 $$$$$$$$$$$$$$$$$$$$$$$$$$$$
TURNER.... 1500 $$$$$$$$$$$$$$$
JAMES..... 950 $$$$$$$$$
6 rows selected.
SQL>

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>

How to align database output in text file using sql query

I have an issue while opening the text file of database output. The null value columns are replaced with the columns which have data. So the data is not properly formatting in the text file. I generated the text file from oracle sql developer. And after opening the text file I can see the alignment of columns is not proper(misplacing of values between null columns and columns which have data ). Can anyone please help me to solve this issue
Just guessing here, but - what you said reminds me of this. Example is based on Scott's EMP table where some employees have NULL in the COMM column:
SQL> select ename, job, comm, deptno
2 from emp
3 where deptno = 30;
ENAME JOB COMM DEPTNO
---------- --------- ---------- ----------
ALLEN SALESMAN 300 30
WARD SALESMAN 500 30
MARTIN SALESMAN 1400 30
BLAKE MANAGER 30
TURNER SALESMAN 30
JAMES CLERK 30
6 rows selected.
If you're spooling such data by concatenating columns, you get mess which is difficult to read:
SQL> select ename||'-'||job||'-'||comm||'-'||deptno
2 from emp
3 where deptno = 30;
ENAME||'-'||JOB||'-'||COMM||'-'||DEPTNO
----------------------------------------------------------
ALLEN-SALESMAN-300-30
WARD-SALESMAN-500-30
MARTIN-SALESMAN-1400-30
BLAKE-MANAGER--30
TURNER-SALESMAN--30
JAMES-CLERK--30
6 rows selected.
But, if you right-pad strings to certain length (RPAD function) and use NVL for missing data, then the result is somewhat prettier:
SQL> select rpad(ename, 10, ' ') ||'-'||
2 rpad(job , 10, ' ') ||'-'||
3 nvl(to_char(comm, '9990D00'), ' ') ||'-'||
4 deptno as result
5 from emp
6 where deptno = 30;
RESULT
---------------------------------------------------------------------
ALLEN -SALESMAN - 300,00-30
WARD -SALESMAN - 500,00-30
MARTIN -SALESMAN - 1400,00-30
BLAKE -MANAGER - -30
TURNER -SALESMAN - -30
JAMES -CLERK - -30
6 rows selected.
SQL>

PL/SQL - do not update changed rows

I'm planning to do a long running update on my huge table (more than billion rows). This update will multiply one column's values by fixed number.
The problem is that during my update (which may last several hours) there will definitely be short transactions that will update some rows and those rows will have correct value that should not be updated though they will still satisfy my update's condition.
So the question is - how do I skip (do not update) rows that were updated outside my long running update's transaction?
One way is to use FOR UPDATE SKIP LOCKED such that other sessions won't be able to pick the rows which are already picked for update.
For example,
Session 1:
SQL> SELECT empno, deptno
2 FROM emp WHERE
3 deptno = 10
4 FOR UPDATE NOWAIT;
EMPNO DEPTNO
---------- ----------
7782 10
7839 10
7934 10
SQL>
Session 2:
SQL> SELECT empno, deptno
2 FROM emp WHERE
3 deptno in (10, 20)
4 FOR UPDATE NOWAIT;
FROM emp WHERE
*
ERROR at line 2:
ORA-00054: resource busy and acquire with NOWAIT specified or timeout expired
Now let's skip the rows which are locked by session 1.
SQL> SELECT empno, deptno
2 FROM emp WHERE
3 deptno IN (10, 20)
4 FOR UPDATE SKIP LOCKED;
EMPNO DEPTNO
---------- ----------
7369 20
7566 20
7788 20
7876 20
7902 20
SQL>
So, department = 10 were locked by session 1 and then department = 20 are locked by session 2.
I have done something like your problem but my table isn't too huge like your.
I re-designed my table, added 2 columns.
created_date: A Trigger put sysdate when insert data.
modified_date: A Trigger put sysdate when update data.
Then I can use created_date or modified_date in my where clause.
Example:
UPDATE TABLE table_name
SET column_name = 'values'
WHERE created_date < SYSDATE;
I hope this will help you.
Oh, I absolutely forgot about this question.
So, I ended up making a snapshot of current rows by saving their copies to another table (I had to update only those rows that satisfied given condition). After that I updated rows that haven't changed their values (using merge).

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