Every column on different line with one record SQL reports - sql

I am trying to make a report for a particular table emp something like this.
************************************************************
EMPLOYEE NUMBER : 1010
EMPLOYEE NAME : SARAH
JOB : DESIGNER
SALARY : 10000$
*************************************************************
EMPLOYEE NUMBER : 1011
EMPLOYEE NAME : HANNAH
JOB : DECORATOR
SALARY : 20000$
*************************************************************
But I don't know how to get every column name on different line.
sql > break column on empname skip page
The above code does something like this.
EMPNO ENAME JOB SAL
---------- ---------- --------- ----------
7839 KING PRESIDENT 5000
EMPNO ENAME JOB SAL
---------- ---------- --------- ----------
7840 QUEEN PRESIDENT 4000
Please suggest what would be the code for it ? TIA.

If you just want the data in this format in some presentation layer (e.g. a text editor), you can simply select the columns concatenated together, separated by a newline:
DECLARE
CURSOR cur IS
SELECT empno, ename, job, sal FROM yourTable;
v_empno yourTable.empno%INT;
v_ename yourTable.ename%VARCHAR2(100);
v_job yourTable.job%VARCHAR2(100);
v_sal yourTable.sal%NUMBER(10,4);
BEGIN
OPEN cur;
LOOP
FETCH cur INTO v_empno, v_ename, v_job, v_sal
EXIT WHEN cur%NOTFOUND;
dbms_output.put_line('EMPLOYEE NUMBER : ' || v_empno || CHR(13) || 'EMPLOYEE NAME : '
|| v_ename || CHR(13) || 'JOB : ' || v_job || CHR(13) || 'SALARY : ' ||
v_sal || '$' || CHR(13));
END LOOP;
CLOSE cur;
END;

Related

pl/sql ,oracle add heading in cursor

how I can add heading for table shown at top , in my code like this pic:
enter image description here
declare
E_Name employ.name%type;
E_Salary employ.salary%type;
CURSOR c_employees is
SELECT name , salary from employ order by salary desc;
BEGIN
OPEN c_employees;
LOOP
FETCH c_employees into E_Name,E_Salary ;
EXIT WHEN c_employees%notfound;
dbms_output.put_line( rpad(E_Name, 20, '.') || ' ' || rpad('$', (E_Salary/100), '$')||' '||E_Salary);
END LOOP;
CLOSE c_employees;
END;
/
Include two more DBMS_OUTPUT.PUT_LINEs which will display that header (lines #2 and 3).
For example (using my tables as I don't have yours):
SQL> begin
2 dbms_output.put_line('Emloyee name Salary');
3 dbms_output.put_line('------------ ------');
4
5 for cur_r in (select ename, sal from emp where deptno = 10) loop
6 dbms_output.put_line(rpad(cur_r.ename, 12, ' ') ||' '||
7 to_char(cur_r.sal, '99990'));
8 end loop;
9 end;
10 /
Emloyee name Salary
------------ ------
CLARK 2450
KING 5000
MILLER 1300
PL/SQL procedure successfully completed.
SQL>

How to resolve the missing or invalid option in oracle database?

SET SERVEROUTPUT on
declare
type emp_record is record
(v_empid emp1.empno % type;
v_ename emp1.ename % type; )
emp_rec emp_record;
v_sal emp1.sal%type;
begin
v_sal:=:v_sal;
select empno,ename
into emp_rec
from emp1
where sal:=v_sal;
dbms_output.put_line('The employee whose salary is ' || v_sal || 'has Employee Id ' || emp_rec.v_empid || 'and his name is ' || emp_rec.v_ename);
end;
The error is :
ORA-00922: missing or invalid option
There are several issues within the code. Convert to the one as below:
declare
type emp_record is record
(v_empid emp1.empno % type,
v_ename emp1.ename % type );
emp_rec emp_record;
v_sal emp1.sal%type:=&i_sal;
begin
select empno,ename
into emp_rec
from emp1
where sal = v_sal;
dbms_output.put_line('The employee whose salary is ' || v_sal || 'has Employee Id '
|| emp_rec.v_empid || 'and his name is ' || emp_rec.v_ename);
end;
/
Whenever it prompts for the i_sal, you'll input the desired salary parameter value.
Demo
If you have multiple records to be manipulated as #APC mentioned, such as more than one people has the same salary,you'll need such a loop statement :
declare
type emp_record is record
(v_empid emp1.empno % type,
v_ename emp1.ename % type,
v_sal emp1.sal % type);
emp_rec emp_record;
v_sal emp1.sal%type:=&i_sal;
cursor c_emp is
select empno,ename,sal
into emp_rec
from emp1
where sal = v_sal;
begin
open c_emp;
loop
fetch c_emp into emp_rec;
exit when c_emp%notfound;
dbms_output.put_line('The employee whose salary is ' || emp_rec.v_sal || ' has Employee Id '
|| emp_rec.v_empid || ' and his name is ' || emp_rec.v_ename);
end loop;
close c_emp;
end;
/
Demo
The simplest method to fix that particular problem is to use aggregation:
select max(empno), max(ename)
into emp_rec
from emp1
where sal = v_sal;
An aggregation query with no group by always returns exactly one row.
That said, the more correct method is to handle the exception that is generated by not having a matching value. You will also need to handle exceptions for duplicate values as well.

How to print first 5 rows of all the tables in an oracle database (schema)?

I want to open a database (schema) which holds about 100 tables,
Once it's opened I would like to print the top 5 rows of all the tables along with their column names.
My first step was to try and get the schema and table names in the database.
so this is what I used for that.
SELECT owner, table_name
FROM all_tables
The expected result will contain the table name, column names and top 5 rows of the table.
I am using Oracle SQL Developer ( Oracle database)
You can try this if you're running Oracle 12c and above.
SET SERVEROUTPUT ON
SET FEEDBACK OFF
DECLARE
x SYS_REFCURSOR;
BEGIN
FOR tabs IN ( SELECT rownum as rn,owner,table_name
FROM all_tables where owner='HR'
and ROWNUM < 10
) LOOP
OPEN x FOR 'SELECT '''|| tabs.owner||'.'|| tabs.table_name||'''
as "table" FROM DUAL';
dbms_sql.return_result(x);
OPEN x FOR 'select * from '
|| tabs.owner
||'.'
|| tabs.table_name
|| ' FETCH FIRST 5 ROWS ONLY ';
dbms_sql.return_result(x);
END LOOP;
END;
/
Output
table
----------
HR.REGIONS
REGION_ID REGION_NAME
---------- -------------------------
1 Europe
2 Americas
3 Asia
4 Middle East and Africa
table
------------
HR.LOCATIONS
LOCATION_ID STREET_ADDRESS POSTAL_CODE CITY STATE_PROVINCE CO
----------- ---------------------------------------- ------------ ------------------------------ ------------------------- --
1000 1297 Via Cola di Rie 00989 Roma IT
1100 93091 Calle della Testa 10934 Venice IT
1200 2017 Shinjuku-ku 1689 Tokyo Tokyo Prefecture JP
1300 9450 Kamiya-cho 6823 Hiroshima JP
1400 2014 Jabberwocky Rd 26192 Southlake Texas US
table
--------------
HR.DEPARTMENTS
DEPARTMENT_ID DEPARTMENT_NAME MANAGER_ID LOCATION_ID
------------- ------------------------------ ---------- -----------
10 Administration 200 1700
20 Marketing 201 1800
30 Purchasing 114 1700
40 Human Resources 203 2400
50 Shipping 121 1500
Normally we would use dynamic SQL for this sort of thing. But it's messy to handle the output of 100 different tables in PL/SQL. So, you need to generate a script.
Step 1: Run this in SQL Developer.
select 'prompt ' || owner ||'.' || table_name ||chr(10)
||'select * from ' || owner ||'.' || table_name
|| ' where rownum <=5;'
from all_tables
order by owner, table_name;
Step 2: Cut'n'paste the output into a SQL Worksheet. Unless you're luckier than I am you'll need to edit the annoying double quotes at the start and end of each line (a SQL Developer feature caused by the line break, I think).
Step 3: Run the (edited) output as a script and the Script Output pane will give you the results you want.
Is it possible for me to feed the schema(database) name
Add where owner = '&schema_name' after from all_tables. Or just use USER_TABLES if you're interested in the schema to which you're connected.
Following is the code which you are looking for. I have used USER_TAB_COLUMNS view so that only tables owned by your schema in which you are executing the code is considered.
Feel free to format the output according to your need.
SET SERVEROUT ON
DECLARE
LV_QUERY VARCHAR2(32767);
LV_RESULT VARCHAR2(32767);
BEGIN
DBMS_OUTPUT.PUT_LINE('---------------------------------------');
DBMS_OUTPUT.PUT_LINE('---------------------------------------');
FOR I IN (
SELECT
C.TABLE_NAME,
LISTAGG(C.COLUMN_NAME, ' || ''****'' || ') WITHIN GROUP(
ORDER BY
C.COLUMN_ID
) COLS
FROM
USER_TAB_COLUMNS C
GROUP BY
C.TABLE_NAME
ORDER BY
1
) LOOP
LV_QUERY := 'SELECT '
|| q'#RTRIM(XMLAGG(XMLELEMENT(E,VALS,CHR(10)).EXTRACT('//text()') ORDER BY 1).GetClobVal(),CHR(10)) FROM#'
|| '(SELECT '
|| I.COLS
|| ' AS VALS FROM '
|| I.TABLE_NAME
|| ' FETCH FIRST 5 ROWS ONLY)';
BEGIN
-- DBMS_OUTPUT.PUT_LINE(lv_query);
EXECUTE IMMEDIATE LV_QUERY
INTO LV_RESULT;
DBMS_OUTPUT.PUT_LINE(CHR(10));
DBMS_OUTPUT.PUT_LINE('TABLE NAME : ' || I.TABLE_NAME);
DBMS_OUTPUT.PUT_LINE('COLUMN NAMES : '
|| REPLACE(REPLACE(REPLACE(I.COLS, '*', ''), '||', ''), '''', '**'));
DBMS_OUTPUT.PUT_LINE(LV_RESULT);
EXCEPTION
WHEN NO_DATA_FOUND THEN
DBMS_OUTPUT.PUT_LINE('NO DATA IN TABLE : ' || I.TABLE_NAME);
END;
DBMS_OUTPUT.PUT_LINE(CHR(10));
DBMS_OUTPUT.PUT_LINE('---------------------------------------');
DBMS_OUTPUT.PUT_LINE('---------------------------------------');
END LOOP;
END;
Hope this is what you are looking for.

PL/SQL - Exact fetch returns more than requested number of rows

I am trying to create function that will return message with maximum salary for each job inside department and order by Maximum salary.
Message need to be:
Department: Department name,
Job/Position: Name of the job, Maximum salary: salary amount,
create or replace PACKAGE BODY Salary AS
FUNCTION max_sal(DEPTNO_F NUMBER)
RETURN VARCHAR2 IS
dept_name VARCHAR2(25);
job_possition VARCHAR(25);
maximum_salary NUMBER;
message VARCHAR2(255);
BEGIN
SELECT DNAME, JOB, MAX(SAL) AS "SAL"
INTO job_possition, maximum_salary
FROM EMP
WHERE DEPTNO = DEPTNO_F
GROUP BY JOB, DNAME
ORDER BY SAL DESC;
message := 'Department name: '||dept_name|| 'Job positin: ' ||job_possitin||, 'Maximum Salary: ' ||maximum_salary;
return message;
END max_sal;
END Salary;
The problem is your query is returning one than one row and it can't select into your 2 variables. Have you tried running the SELECT SQL statement outside of the package and checked the rows you are getting back?
For any given DEPTNO_F value you should not have more than one permutation of DNAME and JOB
To process several rows in query result you have to use loops:
create or replace PACKAGE BODY Salary AS
FUNCTION max_sal(DEPTNO_F NUMBER)
RETURN VARCHAR2 IS
dept_name VARCHAR2(25);
job_possition VARCHAR(25);
maximum_salary NUMBER;
message VARCHAR2(255);
BEGIN
for i in (SELECT DNAME, JOB, MAX(SAL) SAL
FROM EMP
WHERE DEPTNO = DEPTNO_F
GROUP BY JOB, DNAME
ORDER BY SAL DESC) loop
message := message || 'Department name: ' || i.DNAME || ', Job position: ' ||
i.JOB || ', Maximum Salary: ' || i.SAL || chr(10);
end loop;
return message;
END max_sal;
END Salary;

Cursor attribute %notfound as exit statement returns confusing results

DECLARE
i employees.first_name%TYPE;
a employees.last_name%TYPE;
j employees.salary%TYPE;
v_cnt NUMBER;
CURSOR c1 IS
SELECT first_name
,last_name
,salary
FROM employees
WHERE employee_id BETWEEN 100 AND 105;
BEGIN
OPEN c1;
LOOP
FETCH c1 INTO i, a, j;
dbms_output.put_line(i || ' ' || a || ' SALARY IS ' || j || ' row count is ' || v_cnt);
v_cnt := c1%ROWCOUNT;
EXIT WHEN c1%NOTFOUND;
END LOOP;
CLOSE c1;
END;
/
My above query basically loops through all the employee_id between 100 and 105 and prints all 6 employees' salary using dbms_output.put_line.
However, I am getting 7 results with the last one a duplicate result of the previous record. See below
Steven King SALARY IS 24000 row count is
Neena Kochhar SALARY IS 17000 row count is 1
Lex De Haan SALARY IS 17000 row count is 2
Alexander Hunold SALARY IS 9000 row count is 3
Bruce Ernst SALARY IS 6000 row count is 4
David Austin SALARY IS 4800 row count is 5
David Austin SALARY IS 4800 row count is 6
Why is my result returned like this? And why isn't my cursor rowcount marking the first record as 1?
v_cnt is not initialized i.e. IS NULL on the first call of dbms_output.put_line, so you see an empty line instead of row number in the output.
The last fetch makes no action, because there are no more rows in cursor, so variables i, a, j doesn't change after last fetch. You may fetch as long as you want, but after the last row has been fetched out of cursor the FETCH INTO operator produces no new data.
To get what you need (standard loop behavior) the fetching loop shall be organized as
LOOP
FETCH c1 INTO i, a, j;
EXIT WHEN c1%NOTFOUND;
v_cnt := c1%ROWCOUNT;
dbms_output.put_line(i || ' ' || a || ' SALARY IS ' || j || ' row count is ' || v_cnt);
END LOOP;
To avoid compliactions with fetching a cursor you can do it using "for loop".
This is an appropriate solution in the most cases, and definitely in the case of example you gave in the question.
FOR l_rec IN c1
LOOP
i := l_rec.first_name;
a := l_rec.last_name;
j := l_rec.salary;
v_cnt := c1%ROWCOUNT;
dbms_output.put_line(i || ' ' || a || ' SALARY IS ' || j || ' row count is ' || v_cnt);
END LOOP;