combining codes in one anonymous block - sql

I need some help in combining my three codes in one anonymous block and also I need to put a substitution value that a user will use. any idea how I can do that ?? I am using oracle 10g express, here are my codes:
CODE 1: Display department name and number of employees per department for every department that is in "Americas".
select department_name, count(employee_id) "number of employees"
from employees, departments, locations, countries, regions
where employees.DEPARTMENT_ID = departments.DEPARTMENT_ID
AND departments.location_ID = locations.location_ID
AND locations.country_ID = countries.country_ID
AND countries.region_ID = regions.region_ID
AND regions.region_name = 'Americas'
group by department_name
CODE 2: What are the average salaries per position?
SELECT job_ID, AVG(salary) as "avg salary"
FROM employees
GROUP BY job_ID
CODE 3:What are the average salaries per department?
SELECT department_ID, AVG(salary) as "avg salary"
FROM employees
GROUP BY department_ID

Combining completely different datasets into a single block is not possible in Oracle. Anonymous blocks are for executing a series of PL/SQL commands. A query (i.e. anything that returns back a result set, ex: SELECT ...)can only return a single result so it would need to be three separate queries.
If you're using SQL*Plus all of this can be done from the command line and/or with a .sqlscript file. You can also specify parameters on the command line as well (I couldn't tell if thats what you meant for the first query but I added it anyway).
Script (foo.sql):
-- Set page size to something larger than the expected result set.
-- NOTE: You don't want to put 0 (unlimited) as it supresses the column headers.
SET pagesize 50000
-- Get the parameter from the command line:
VAR region VARCHAR2(256)
EXEC :region := '&1'
SELECT 'Processing for region: ' || :region
FROM dual;
-- Query 1
promp "Employee detail in specified region:"
select department_name, count(employee_id) "number of employees"
from HR.employees, HR.departments, HR.locations, HR.countries, HR.regions
where employees.DEPARTMENT_ID = departments.DEPARTMENT_ID
AND departments.location_ID = locations.location_ID
AND locations.country_ID = countries.country_ID
AND countries.region_ID = regions.region_ID
AND regions.region_name = :region
group by department_name
;
-- Query 2:
promp "Average salary by position:"
SELECT job_ID, AVG(salary) as "avg salary"
FROM HR.employees
GROUP BY job_ID;
-- Query 3:
promp "Average salaries by department:"
SELECT department_ID, AVG(salary) as "avg salary"
FROM HR.employees
GROUP BY department_ID;
exit
Output:
$ sqlplus myuser/mypass#lvm01xe #sample.sql Americas
SQL*Plus: Release 11.2.0.2.0 Production on Sun Nov 18 09:11:11 2012
Copyright (c) 1982, 2010, Oracle. All rights reserved.
Connected to:
Oracle Database 10g Express Edition Release 10.2.0.1.0 - Production
PL/SQL procedure successfully completed.
'PROCESSINGFORREGION:'||:REGION
--------------------------------------------------------------------------------
Processing for region: Americas
"Employee detail in specified region:"
DEPARTMENT_NAME number of employees
------------------------------ -------------------
Administration 1
Accounting 2
IT 5
Purchasing 6
Executive 3
Shipping 45
Finance 6
Marketing 2
8 rows selected.
"Average salary by position:"
JOB_ID avg salary
---------- ----------
IT_PROG 5760
AC_MGR 12000
AC_ACCOUNT 8300
ST_MAN 7280
PU_MAN 11000
AD_ASST 4400
AD_VP 17000
SH_CLERK 3215
FI_ACCOUNT 7920
FI_MGR 12000
PU_CLERK 2780
SA_MAN 12200
MK_MAN 13000
PR_REP 10000
AD_PRES 24000
SA_REP 8350
MK_REP 6000
ST_CLERK 2785
HR_REP 6500
19 rows selected.
"Average salaries by department:"
DEPARTMENT_ID avg salary
------------- ----------
100 8600
30 4150
7000
90 19333.3333
20 9500
70 10000
110 10150
50 3475.55556
80 8955.88235
40 6500
60 5760
10 4400
12 rows selected.
Disconnected from Oracle Database 10g Express Edition Release 10.2.0.1.0 - Production

Obviously, anything which is aggregated by the same yardstick can be combined easily. Hence you can merge the third query with your first one like this:
select department_name
, count(employee_id) as "number of employees"
, avg(salary) as "avg salary"
from employees, departments, locations, countries, regions
where employees.DEPARTMENT_ID = departments.DEPARTMENT_ID
AND departments.location_ID = locations.location_ID
AND locations.country_ID = countries.country_ID
AND countries.region_ID = regions.region_ID
AND regions.region_name = :region_name
group by department_name
I have adapted your code to pass in a bind variable, :region_name. There are various way of specifying variables in Apex. Find out more.
There is no meaningful way of including your second query. The values for JOB_ID cross the values of DEPARTMENT_ID, so there's no common key to join on.

Related

Getting percent of employee's salary within department

I need to create a report that displays information about what part of the salary in percentage terms each employee receives within the department in which they work, using analytical functions.
SELECT EMPLOYEES.FIRST_NAME, EMPLOYEES.LAST_NAME, EMPLOYEES.DEPARTMENT_ID,
DEPARTMENTS.DEPARTMENT_NAME, EMPLOYEES.SALARY,
(SALARY/SUM(SALARY)) * 100 over (partition by DEPARTMENT_ID) AS "PercentWithinDepartment"
FROM HR.EMPLOYEES
FULL JOIN HR.DEPARTMENTS ON EMPLOYEES.DEPARTMENT_ID = DEPARTMENTS.DEPARTMENT_ID
I get an "ORA-00923 FROM keyword not found where expected" error but I think it's not my only mistake within this task.
I cannot provide a code snippet of database but this can be run against the HR sample schema.
My request is to help me figure out mistake to complete this task properly.
The immediate issue is that your over clause is in the wrong place:
(SALARY/SUM(SALARY)) * 100 over (partition by DEPARTMENT_ID)
should be
(SALARY/SUM(SALARY) over (partition by DEPARTMENT_ID)) * 100
But the reference to DEPARTMENT_ID in there is ambiguous as that column is in both tables, so it should be:
(SALARY/SUM(EMPLOYEES.SALARY) over (partition by DEPARTMENTS.DEPARTMENT_ID)) * 100
You might want to consider using table aliases to make the code a bit shorter.
You may also want to round (or truncate/floor) the percentages to, say, one or two decimal places.
I'm not sure you really want a full outer join though - that will include all departments with no employees (e.g. 'Recruiting'). A left outer join looks more appropriate - so 'Kimberley Grant', who is not linked to any department, will still be included.
I have to calculate percent about what part of the salary employee get within all organization and im confused why do i get "not a single-group group function" error here
Because the version you added as a comment has:
SALARY/SUM(EMPLOYEES.SALARY)*100
which is the aggregate form of the function, not the analytic form. As you don't want to aggregate here, you still need an over clause to make it analytical, but that can be empty:
SALARY/SUM(EMPLOYEES.SALARY) over () * 100
And you can of course do both together:
select employees.first_name,
employees.last_name,
employees.department_id,
departments.department_name,
employees.salary,
salary / sum(employees.salary) over (partition by departments.department_id) * 100 as "PercentWithinDepartment",
salary / sum(employees.salary) over () * 100 as "PercentWithinOrganization"
from hr.employees
left join hr.departments on employees.department_id = departments.department_id;
FIRST_NAME LAST_NAME DEPARTMENT_ID DEPARTMENT_NAME SALARY PercentWithinDepartment PercentWithinOrganization
-------------------- ------------------------- ------------- ------------------------------ ---------- ----------------------- -------------------------
Jennifer Whalen 10 Administration 4400 100 .636389933
Michael Hartstein 20 Marketing 13000 68.4210526 1.88024299
Pat Fay 20 Marketing 6000 31.5789474 .867804455
...
Kimberely Grant 7000 100 1.01243853
You might also want to add an order by clause...
I don't have HR schema (but I have Scott) so - here's how. I'm using SQL*Plus' formatting capabilities to make it look prettier.
SQL> set numformat 999g990d00
SQL> break on deptno on dname
SQL> compute sum of pct_sal on deptno
SQL>
SQL> select e.deptno, d.dname, e.ename, e.sal,
2 sum(e.sal) over (partition by e.deptno) dept_sal,
3 --
4 round((e.sal / sum(e.sal) over (partition by e.deptno)) * 100, 2) pct_sal
5 from emp e join dept d on d.deptno = e.deptno
6 order by e.deptno, e.ename;
Result:
DEPTNO DNAME ENAME SAL DEPT_SAL PCT_SAL
----------- -------------- ---------- ----------- ----------- -----------
10,00 ACCOUNTING CLARK 2.450,00 8.750,00 28,00
KING 5.000,00 8.750,00 57,14
MILLER 1.300,00 8.750,00 14,86
*********** ************** -----------
sum 100,00
20,00 RESEARCH ADAMS 1.100,00 10.915,00 10,08
FORD 3.000,00 10.915,00 27,49
JONES 2.975,00 10.915,00 27,26
SCOTT 3.000,00 10.915,00 27,49
SMITH 840,00 10.915,00 7,70
*********** ************** -----------
sum 100,02
30,00 SALES ALLEN 1.600,00 9.400,00 17,02
BLAKE 2.850,00 9.400,00 30,32
JAMES 950,00 9.400,00 10,11
MARTIN 1.250,00 9.400,00 13,30
TURNER 1.500,00 9.400,00 15,96
WARD 1.250,00 9.400,00 13,30
*********** ************** -----------
sum 100,01
14 rows selected.
SQL>
(Sum isn't exactly 100% because of rounding.)
I might be mistaking, but it seems to me that all you need is to use ratio_to_report analytic function. Try this one please
SELECT E.FIRST_NAME, E.LAST_NAME, E.DEPARTMENT_ID,
D.DEPARTMENT_NAME, E.SALARY,
100 * ratio_to_report(e.salary) over (partition by d.department_id)
FROM HR.EMPLOYEES e
JOIN hr.departments d
on e.department_id = d.department_id
order by d.department_id;

How to display employee names instead of employee id while keeping the ","

I have several tables that relate to employees and the total amount of sales in dollars and I have to follow these instructions right here:
The company wants to see the total sale amount per sales person (salesman) for all orders. Assume that some online orders do not have any sales person involved. Display the salesman full name and the total sale amount for that employee.
Include also the Total Sale amount for all Orders without involvement of a salesman.
Exclude sales persons whose first name end on y (like Lily or Daisy).
Include only those sales persons who were responsible for more than 4 million dollars in sale.
Sort the result so that bigger sales are shown first.
Note; Do NOT use LIKE operator.
I already have a query statement written in which it shows the employee id instead of the employee name which is what is needed for this query question to be answered properly. In this case the salesman id "," are sales people without names so for ex. sales over the internet therefore no specific name. How do I make it so that I keep the "," but instead show the names of the employee and not their ID Here is the statement:
SELECT NVL(to_char(o.salesman_id), ',') AS "Emp Name", RPAD(to_char(SUM(i.quantity * i.unit_price),'$999,999,999.99'), 16, ' ') AS "Total Sale"
FROM (orders o
INNER JOIN order_items i ON i.order_id = o.order_id)
GROUP BY o.salesman_id
HAVING SUM(i.quantity * i.unit_price) > 4000000.00
ORDER BY 2 DESC;
Heres the output of the query:
Looks like outer join to me.
I don't have your tables so I'll use Scott's emp to illustrate it.
SQL> select empno, ename from emp where deptno = 10;
EMPNO ENAME
---------- ----------
7782 CLARK
7839 KING
7934 MILLER
I'll use those EMPNO values instead of your 62 and 64 (as displayed on the screenshot). your_query CTE represents output you currently have. Then outer join it with table that contains employee names (emp in my case), but leave , for unknown employees:
SQL> with
2 your_query (emp_name, total_sale) as
3 (select ',' , 100 from dual union all
4 select '7782', 200 from dual union all
5 select '7839', 300 from dual
6 )
7 select nvl(e.ename, q.emp_name) ename,
8 q.total_sale
9 from your_query q left join emp e on q.emp_name = to_char(e.empno);
ENAME TOTAL_SALE
---------- ----------
CLARK 200
KING 300
, 100
SQL>

Write a PL/SQL block to display the department name and the total salary expenditure of the department from the Employee table

Display the department name and the total salary expenditure of the department from the Employee table.
To find the department name and the salary of the person from the particular department must be the output.
THIS IS THE EMPLOYEE TABLE:
EMP_ID EMP_NAME SALARY DEPT
101 Tom 54000 MECH
102 William 43000 CSE
103 John 34560 MECH
104 Smith 56000 CSE
105 Steve 23450 IT
CODING:
I have tried this but i didn't get the output can somebody help me to get the output.
set serveroutput on;
DECLARE
Dept varchar2(25);
Employee_salary VARCHAR2(25);
SELECT
Employee_salary
FROM
Employee;
WHERE salary=23450;
BEGIN
DBMS_OUTPUT.PUT_LINE(‘Department-wise salary expenditure :’ || Dept salary);
END;
/
And the Sample Output:
Department-wise salary expenditure:
IT department, total salary is 48000
CSE department, total salary is 79000
MECH department, total salary is 80000
set serveroutput on;
declare
cursor c_depts is select DEPT, sum(SALARY) as sumsal from employee group by DEPT;
v_depts c_depts%rowtype;
begin
open c_depts;
dbms_output.put_line('Department-wise salary expenditure:');
loop
fetch c_depts into v_depts;
exit when c_depts%notfound;
dbms_output.put_line(v_depts.DEPT || ' department, total salary is ' || v_depts.sumsal );
end loop;
close c_depts;
end;
/
You'll need a loop (in PL/SQL) to display all departments. For example:
SQL> begin
2 dbms_output.put_line('Department-wise salary expenditure:');
3 for cur_r in (select dept, sum(salary) sumsal
4 from employee
5 group by dept
6 order by dept
7 )
8 loop
9 dbms_output.put_line(rpad(cur_r.dept, 4, ' ') ||
10 ' department, total salary is ' || cur_r.sumsal);
11 end loop;
12 end;
13 /
Department-wise salary expenditure:
CSE department, total salary is 99000
IT department, total salary is 23450
MECH department, total salary is 88560
PL/SQL procedure successfully completed.
SQL>
(rpad is here just to nicely align the output)
As of code you've written: it won't compile, too many errors. Fixed (but still not doing what you need) would be
SQL> DECLARE
2 Dept varchar2(25);
3 Employee_salary VARCHAR2(25);
4 begin
5 SELECT salary
6 into employee_salary
7 FROM Employee
8 WHERE salary=23450;
9
10 DBMS_OUTPUT.PUT_LINE('Department-wise salary expenditure :' || employee_salary);
11 END;
12 /
Department-wise salary expenditure :23450
PL/SQL procedure successfully completed.
SQL>
So, what did you do wrong?
begin needs to be put after the declaration section; your is somewhere down below
select in PL/SQL requires an into clause
where clause is useless; you're trying to select salaries which are exactly 23450; if there's none, your query will return no_data_found error. If two (or more) employees earn that much, you'll get too_many_rows error
fancy single quotes in dbms_output.put_line
you're displaying variable that doesn't exist (and can't be split into two separate names, dept salary
I'm not sure why you are using PL/SQL, unless that is a requirement of the exercise. You seem to only want an aggregation query:
SELECT dept, SUM(Employee_salary) as total_salary
FROM Employee;
GROUP BY dept;

will this sql work

Write ONE SQL statement that prints the name of all employees and their department numbers for all salaried employees who satisfy all of the following conditions:
Work on 2 projects,
Do not make $30000,
Their department manager and supervisor have the same name (Note: since name is not the pkey of employee tables, this requirement does not mean these two individuals are the same person).
Note that supervisor is not the manager of the department
SQL code:
select
ename, d#
from
employee, department
where
count(p#) = 2
and salary < 30000
and cname, d# in (select ename, d#
from employee, department
where employee.cname = depatment.cname);
This would be a good start for you, since there is some data missing about managers and supervisors (and generally you haven't provided any schema):
select ename
from employee e
join department d on e.cname = d.cname
where salary < 30000
group by ename
having count(p#) = 2
It will show up every employee with salary below 30K that works on 2 projects.
You're likely to need more joins/subqueries to achieve your desired output.

significance of Partition over in Oracle

Query:
SELECT department_id "Dept", hire_date "Date", last_name "Name",
LISTAGG(last_name, '; ') WITHIN GROUP (ORDER BY hire_date, last_name)
OVER (PARTITION BY department_id) as "Emp_list"
FROM employees
WHERE hire_date < '01-SEP-2003'
ORDER BY "Dept", "Date", "Name";
Dept Date Name Emp_list
----- --------- --------------- --------------------------------
30 07-DEC-02 Raphaely Raphaely; Khoo
30 18-MAY-03 Khoo Raphaely; Khoo
40 07-JUN-02 Mavris Mavris
50 01-MAY-03 Kaufling Kaufling; Ladwig
50 14-JUL-03 Ladwig Kaufling; Ladwig
70 07-JUN-02 Baer Baer
90 13-JAN-01 De Haan De Haan; King
90 17-JUN-03 King De Haan; King
100 16-AUG-02 Faviet Faviet; Greenberg
100 17-AUG-02 Greenberg Faviet; Greenberg
110 07-JUN-02 Gietz Gietz; Higgins
110 07-JUN-02 Higgins Gietz; Higgins
Doubt:
What is significance of Partition over here?
Here is a short answer. You should read the documentation on analytic functions, though.
The function list_agg() -- as with many Oracle functions -- can be either an aggregation function or an analytic function. I assume you know what an aggregation function is. The group by clause puts all rows with the same value of the aggregation variables and summarizes them into a single row.
Analytic functions are similar, but different of course. They also find groups of rows that are similar to each other. However, they don't summarize them on one row. They simply add a new column with that information. The partition by clause specifies how the groups are created.
So, this query is combining all the last_names in rows with the same department_id. The last_names are ordered by hiring date and then the name. This list is then attached to every line in the same table. You can compare your query to a similar aggregation query:
SELECT department_id "Dept",
LISTAGG(last_name, '; ') WITHIN GROUP (ORDER BY hire_date, last_name)
FROM employees
WHERE hire_date < '01-SEP-2003'
GROUP BY department_id;